Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add_per_room_icon_display.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: minor
---

Add per Space setting for when to show room icons in sidebar
5 changes: 5 additions & 0 deletions .changeset/fix-various-banner-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: fix
---

Various small banner changes
231 changes: 128 additions & 103 deletions src/app/components/room-card/RoomCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
as,
color,
config,
toRem,
} from 'folds';
import classNames from 'classnames';
import FocusTrap from 'focus-trap-react';
Expand All @@ -36,6 +37,9 @@ import { KnockRoomPrompt } from '$components/knock-room-prompt';
import { RoomAvatar } from '$components/room-avatar';
import { formatCompactNumber } from '$utils/formatCompactNumber';
import * as css from './style.css';
import type { RoomBannerContent } from '$types/matrix-sdk-events';
import { CustomStateEvent } from '$types/matrix/room';
import colorMXID from '$utils/colorMXID';

type GridColumnCount = '1' | '2' | '3';
const getGridColumnCount = (gridWidth: number): GridColumnCount => {
Expand Down Expand Up @@ -66,7 +70,6 @@ export function RoomCardGrid({ children }: { children: ReactNode }) {
export const RoomCardBase = as<'div'>(({ className, ...props }, ref) => (
<Box
direction="Column"
gap="300"
className={classNames(css.RoomCardBase, className)}
{...props}
ref={ref}
Expand Down Expand Up @@ -185,6 +188,11 @@ export const RoomCard = as<'div', RoomCardProps>(
? getRoomAvatarUrl(mx, joinedRoom, 96, useAuthentication)
: avatarUrl && mxcUrlToHttp(mx, avatarUrl, useAuthentication, 96, 96, 'crop');

const bannerState = joinedRoom
? getStateEvent(joinedRoom, CustomStateEvent.RoomBanner)
: undefined;
const bannerMXC = bannerState?.getContent<RoomBannerContent>()?.url;
const bannerURI = mxcUrlToHttp(mx, bannerMXC ?? '', true);
const roomName = joinedRoom?.name || name || fallbackName;
const roomTopic =
(topicEvent?.getContent().topic as string) || undefined || topic || fallbackTopic;
Expand Down Expand Up @@ -215,11 +223,25 @@ export const RoomCard = as<'div', RoomCardProps>(
const [viewTopic, setViewTopic] = useState(false);
const closeTopic = () => setViewTopic(false);
const openTopic = () => setViewTopic(true);

return (
<RoomCardBase {...props} ref={ref}>
<Box gap="200" justifyContent="SpaceBetween">
<Avatar size="500">
<Box style={{ height: toRem(120) }} direction="Column">
{!bannerURI && !avatar ? (
<span
className={css.RoomCardBanner({ trueBanner: false })}
style={{
backgroundColor: colorMXID(roomIdOrAlias),
}}
/>
) : (
<img
className={css.RoomCardBanner({ trueBanner: !!bannerURI })}
src={bannerURI || avatar || undefined}
alt={`${name} cover`}
draggable="false"
/>
)}
<Avatar className={css.RoomCardAvatar} size="500">
<RoomAvatar
roomId={roomIdOrAlias}
src={avatar ?? undefined}
Expand All @@ -231,117 +253,120 @@ export const RoomCard = as<'div', RoomCardProps>(
)}
/>
</Avatar>
{(roomType === RoomType.Space || joinedRoom?.isSpaceRoom()) && (
<Badge variant="Secondary" fill="Soft" outlined>
<Text size="L400">Space</Text>
</Badge>
)}
</Box>
<Box grow="Yes" direction="Column" gap="100">
<RoomCardName>{roomName}</RoomCardName>
<RoomCardTopic onClick={openTopic} onKeyDown={onEnterOrSpace(openTopic)} tabIndex={0}>
{roomTopic}
</RoomCardTopic>

<Overlay open={viewTopic} backdrop={<OverlayBackdrop />}>
<OverlayCenter>
<FocusTrap
focusTrapOptions={{
initialFocus: false,
clickOutsideDeactivates: true,
onDeactivate: closeTopic,
escapeDeactivates: stopPropagation,
}}
>
{renderTopicViewer(roomName, roomTopic, closeTopic)}
</FocusTrap>
</OverlayCenter>
</Overlay>
</Box>
{typeof joinedMemberCount === 'number' && (
<Box gap="100">
<Icon size="50" src={Icons.User} />
<Text size="T200">{`${formatCompactNumber(joinedMemberCount)} Members`}</Text>
<Box className={css.RoomCardItems} direction="Column" gap="300">
<Box gap="200" justifyContent="SpaceBetween">
<Box grow="Yes" direction="Column" gap="100">
<RoomCardName>{roomName}</RoomCardName>
<RoomCardTopic onClick={openTopic} onKeyDown={onEnterOrSpace(openTopic)} tabIndex={0}>
{roomTopic}
</RoomCardTopic>
</Box>
<Overlay open={viewTopic} backdrop={<OverlayBackdrop />}>
<OverlayCenter>
<FocusTrap
focusTrapOptions={{
initialFocus: false,
clickOutsideDeactivates: true,
onDeactivate: closeTopic,
escapeDeactivates: stopPropagation,
}}
>
{renderTopicViewer(roomName, roomTopic, closeTopic)}
</FocusTrap>
</OverlayCenter>
</Overlay>
{(roomType === RoomType.Space || joinedRoom?.isSpaceRoom()) && (
<Badge variant="Secondary" fill="Soft" outlined>
<Text size="L400">Space</Text>
</Badge>
)}
</Box>
)}
{typeof joinedRoomId === 'string' && (
<Button
onClick={onView ? () => onView(joinedRoomId) : undefined}
variant="Secondary"
fill="Soft"
size="300"
>
<Text size="B300" truncate>
View
</Text>
</Button>
)}
{typeof joinedRoomId !== 'string' &&
joinState.status !== AsyncStatus.Error &&
(joinRule === JoinRule.Knock ? (
<>
<Button onClick={() => setKnocking(true)} variant="Secondary" size="300">
<Text size="B300" truncate>
Knock
</Text>
</Button>

{knocking && (
<KnockRoomPrompt
roomId={roomIdOrAlias}
via={viaServers}
onDone={() => setKnocking(false)}
onCancel={() => setKnocking(false)}
/>
)}
</>
) : (
{typeof joinedMemberCount === 'number' && (
<Box gap="100">
<Icon size="50" src={Icons.User} />
<Text size="T200">{`${formatCompactNumber(joinedMemberCount)} Members`}</Text>
</Box>
)}
{typeof joinedRoomId === 'string' && (
<Button
onClick={join}
onClick={onView ? () => onView(joinedRoomId) : undefined}
variant="Secondary"
fill="Soft"
size="300"
disabled={joining}
before={joining && <Spinner size="50" variant="Secondary" fill="Soft" />}
>
<Text size="B300" truncate>
{joining ? 'Joining' : 'Join'}
View
</Text>
</Button>
))}
{typeof joinedRoomId !== 'string' && joinState.status === AsyncStatus.Error && (
<Box gap="200">
<Button
onClick={join}
className={css.ActionButton}
variant="Critical"
fill="Solid"
size="300"
>
<Text size="B300" truncate>
Retry
</Text>
</Button>
<ErrorDialog
title="Join Error"
message={joinState.error.message || 'Failed to join. Unknown Error.'}
>
{(openError) => (
<Button
onClick={openError}
className={css.ActionButton}
variant="Critical"
fill="Soft"
outlined
size="300"
>
)}
{typeof joinedRoomId !== 'string' &&
joinState.status !== AsyncStatus.Error &&
(joinRule === JoinRule.Knock ? (
<>
<Button onClick={() => setKnocking(true)} variant="Secondary" size="300">
<Text size="B300" truncate>
View Error
Knock
</Text>
</Button>
)}
</ErrorDialog>
</Box>
)}

{knocking && (
<KnockRoomPrompt
roomId={roomIdOrAlias}
via={viaServers}
onDone={() => setKnocking(false)}
onCancel={() => setKnocking(false)}
/>
)}
</>
) : (
<Button
onClick={join}
variant="Secondary"
size="300"
disabled={joining}
before={joining && <Spinner size="50" variant="Secondary" fill="Soft" />}
>
<Text size="B300" truncate>
{joining ? 'Joining' : 'Join'}
</Text>
</Button>
))}
{typeof joinedRoomId !== 'string' && joinState.status === AsyncStatus.Error && (
<Box gap="200">
<Button
onClick={join}
className={css.ActionButton}
variant="Critical"
fill="Solid"
size="300"
>
<Text size="B300" truncate>
Retry
</Text>
</Button>
<ErrorDialog
title="Join Error"
message={joinState.error.message || 'Failed to join. Unknown Error.'}
>
{(openError) => (
<Button
onClick={openError}
className={css.ActionButton}
variant="Critical"
fill="Soft"
outlined
size="300"
>
<Text size="B300" truncate>
View Error
</Text>
</Button>
)}
</ErrorDialog>
</Box>
)}
</Box>
</RoomCardBase>
);
}
Expand Down
33 changes: 31 additions & 2 deletions src/app/components/room-card/style.css.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { style } from '@vanilla-extract/css';
import { DefaultReset, config } from 'folds';
import { DefaultReset, color, config, toRem } from 'folds';
import { ContainerColor } from '$styles/ContainerColor.css';
import { recipe } from '@vanilla-extract/recipes';

export const CardGrid = style({
display: 'grid',
Expand All @@ -12,11 +13,15 @@ export const RoomCardBase = style([
DefaultReset,
ContainerColor({ variant: 'SurfaceVariant' }),
{
padding: config.space.S500,
borderRadius: config.radii.R500,
overflow: 'hidden',
},
]);

export const RoomCardItems = style({
padding: config.space.S500,
backgroundColor: color.SurfaceVariant.Container,
});
export const RoomCardTopic = style({
minHeight: `calc(3 * ${config.lineHeight.T200})`,
display: '-webkit-box',
Expand All @@ -34,3 +39,27 @@ export const ActionButton = style({
flex: '1 1 0',
minWidth: 1,
});

export const RoomCardBanner = recipe({
base: {
height: toRem(96),
minHeight: toRem(96),
width: '100%',
objectFit: 'cover',
objectPosition: 'center center',
},
variants: {
trueBanner: {
true: {},
false: {
filter: 'blur(10px)',
},
},
},
});
export const RoomCardAvatar = style({
position: 'sticky',
transform: 'translateY(-50%)',
marginLeft: config.space.S500,
outline: `${config.borderWidth.B600} solid ${color.Surface.Container}`,
});
Loading
Loading