Skip to content

Commit b96fcab

Browse files
authored
feat: improve mobile standup experience (#5986)
1 parent 57a875d commit b96fcab

14 files changed

Lines changed: 800 additions & 196 deletions

packages/shared/src/components/fields/RichTextInput.tsx

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ interface RichTextInputProps {
146146
allowBlockFormatting?: boolean;
147147
minHeightClassName?: string;
148148
markdownToHtml?: (markdown: string) => string;
149+
hideToolbar?: boolean;
149150
}
150151

151152
export interface RichTextInputRef {
@@ -181,6 +182,7 @@ function RichTextInput(
181182
allowBlockFormatting = true,
182183
minHeightClassName = 'min-h-[8rem]',
183184
markdownToHtml = markdownToHtmlBasic,
185+
hideToolbar = false,
184186
}: RichTextInputProps,
185187
ref: ForwardedRef<RichTextInputRef>,
186188
): ReactElement {
@@ -780,48 +782,53 @@ function RichTextInput(
780782
</>
781783
) : (
782784
<>
783-
<RichTextToolbar
784-
ref={toolbarRef}
785-
editor={editor}
786-
allowBlockFormatting={allowBlockFormatting}
787-
onLinkAdd={(url, label) => {
788-
if (!editor) {
789-
return;
790-
}
791-
if (!editor.state.selection.empty) {
792-
editor.chain().focus().setLink({ href: url }).run();
793-
return;
785+
{hideToolbar ? null : (
786+
<RichTextToolbar
787+
ref={toolbarRef}
788+
editor={editor}
789+
allowBlockFormatting={allowBlockFormatting}
790+
onLinkAdd={(url, label) => {
791+
if (!editor) {
792+
return;
793+
}
794+
if (!editor.state.selection.empty) {
795+
editor.chain().focus().setLink({ href: url }).run();
796+
return;
797+
}
798+
const linkText = label || url;
799+
editor
800+
.chain()
801+
.focus()
802+
.insertContent({
803+
type: 'text',
804+
text: linkText,
805+
marks: [{ type: 'link', attrs: { href: url } }],
806+
})
807+
.run();
808+
}}
809+
inlineActions={hasToolbarActions ? toolbarActions : null}
810+
rightActions={
811+
<div className="flex items-center gap-0">
812+
{savingLabel}
813+
<SimpleTooltip content="Switch to Markdown Editor">
814+
<Button
815+
type="button"
816+
variant={ButtonVariant.Tertiary}
817+
size={ButtonSize.Small}
818+
icon={<MarkdownIcon />}
819+
onClick={switchToMarkdownMode}
820+
/>
821+
</SimpleTooltip>
822+
{onClose && (
823+
<CloseButton
824+
size={ButtonSize.Small}
825+
onClick={onClose}
826+
/>
827+
)}
828+
</div>
794829
}
795-
const linkText = label || url;
796-
editor
797-
.chain()
798-
.focus()
799-
.insertContent({
800-
type: 'text',
801-
text: linkText,
802-
marks: [{ type: 'link', attrs: { href: url } }],
803-
})
804-
.run();
805-
}}
806-
inlineActions={hasToolbarActions ? toolbarActions : null}
807-
rightActions={
808-
<div className="flex items-center gap-0">
809-
{savingLabel}
810-
<SimpleTooltip content="Switch to Markdown Editor">
811-
<Button
812-
type="button"
813-
variant={ButtonVariant.Tertiary}
814-
size={ButtonSize.Small}
815-
icon={<MarkdownIcon />}
816-
onClick={switchToMarkdownMode}
817-
/>
818-
</SimpleTooltip>
819-
{onClose && (
820-
<CloseButton size={ButtonSize.Small} onClick={onClose} />
821-
)}
822-
</div>
823-
}
824-
/>
830+
/>
831+
)}
825832
{isUploadEnabled && (
826833
<input
827834
type="file"

packages/shared/src/components/liveRooms/LiveRoom.spec.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const mockUsePushNotificationContext = jest.fn();
2020
const mockSubscribeToLiveRoom = jest.fn();
2121
const mockUnsubscribeFromLiveRoom = jest.fn();
2222
const mockEnablePush = jest.fn();
23+
const mockUseViewSize = jest.fn<boolean, [unknown]>(() => true);
2324
const mockUseQueries = useQueries as jest.Mock;
2425

2526
jest.mock('@tanstack/react-query', () => {
@@ -73,6 +74,14 @@ jest.mock('../../hooks/useToastNotification', () => ({
7374
useToastNotification: () => ({ displayToast: mockDisplayToast }),
7475
}));
7576

77+
jest.mock('../../hooks', () => {
78+
const actual = jest.requireActual('../../hooks');
79+
return {
80+
...actual,
81+
useViewSize: (size: unknown) => mockUseViewSize(size),
82+
};
83+
});
84+
7685
jest.mock('../../graphql/users', () => ({
7786
getUserShortInfo: jest.fn(),
7887
}));
@@ -257,6 +266,7 @@ const createParticipant = (
257266
describe('LiveRoom', () => {
258267
beforeEach(() => {
259268
jest.clearAllMocks();
269+
mockUseViewSize.mockReturnValue(true);
260270
mockUseLiveRoomParticipantStreams.mockReturnValue(new Map());
261271
mockSubscribeToLiveRoom.mockResolvedValue({});
262272
mockUnsubscribeFromLiveRoom.mockResolvedValue({});
@@ -946,6 +956,49 @@ describe('LiveRoom', () => {
946956
expect(screen.getAllByTestId('live-room-tile')).toHaveLength(12);
947957
});
948958

959+
it('limits stage pagination to four visible speakers on mobile', () => {
960+
mockUseViewSize.mockReturnValue(false);
961+
const activeSpeakerParticipantIds = Array.from(
962+
{ length: 12 },
963+
(_, index) => `speaker-${index + 1}`,
964+
);
965+
const participants = activeSpeakerParticipantIds.reduce<
966+
NonNullable<LiveRoomContextValue['roomState']>['participants']
967+
>(
968+
(acc, participantId) => ({
969+
...acc,
970+
[participantId]: createParticipant(participantId),
971+
}),
972+
{
973+
host: createParticipant('host', 'host'),
974+
},
975+
);
976+
977+
mockUseLiveRoomConnection.mockReturnValue(
978+
createContextValue({
979+
roomState: {
980+
...createContextValue().roomState!,
981+
participants,
982+
stage: {
983+
speakerQueueParticipantIds: [],
984+
activeSpeakerParticipantIds,
985+
raisedHandParticipantIds: [],
986+
},
987+
},
988+
}),
989+
);
990+
991+
renderLiveRoom();
992+
993+
expect(screen.getByText('Page 1 / 4')).toBeInTheDocument();
994+
expect(screen.getAllByTestId('live-room-tile')).toHaveLength(4);
995+
996+
fireEvent.click(screen.getByRole('button', { name: 'Next' }));
997+
998+
expect(screen.getByText('Page 2 / 4')).toBeInTheDocument();
999+
expect(screen.getAllByTestId('live-room-tile')).toHaveLength(4);
1000+
});
1001+
9491002
it('logs a room query error only once for the same failure', () => {
9501003
let contextValue = createContextValue({ selectedMicId: 'mic-1' });
9511004

0 commit comments

Comments
 (0)