diff --git a/src/app/components/create-room/CreateRoomKindSelector.tsx b/src/app/components/create-room/CreateRoomKindSelector.tsx
index 096954fb..ebc55e7f 100644
--- a/src/app/components/create-room/CreateRoomKindSelector.tsx
+++ b/src/app/components/create-room/CreateRoomKindSelector.tsx
@@ -2,12 +2,8 @@ import React from 'react';
import { Box, Text, Icon, Icons, config, IconSrc } from 'folds';
import { SequenceCard } from '../sequence-card';
import { SettingTile } from '../setting-tile';
+import { CreateRoomKind } from './types';
-export enum CreateRoomKind {
- Private = 'private',
- Restricted = 'restricted',
- Public = 'public',
-}
type CreateRoomKindSelectorProps = {
value?: CreateRoomKind;
onSelect: (value: CreateRoomKind) => void;
diff --git a/src/app/components/create-room/CreateRoomVoiceSelector.tsx b/src/app/components/create-room/CreateRoomVoiceSelector.tsx
new file mode 100644
index 00000000..f3862db2
--- /dev/null
+++ b/src/app/components/create-room/CreateRoomVoiceSelector.tsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import { Box, Text, Icon, Icons, config, IconSrc } from 'folds';
+import { SequenceCard } from '../sequence-card';
+import { SettingTile } from '../setting-tile';
+import { CreateRoomVoice } from './types';
+
+type CreateRoomVoiceSelectorProps = {
+ value?: CreateRoomVoice;
+ onSelect: (value: CreateRoomVoice) => void;
+ disabled?: boolean;
+ getIcon: (kind: CreateRoomVoice) => IconSrc;
+};
+export function CreateRoomVoiceSelector({
+ value,
+ onSelect,
+ disabled,
+ getIcon,
+}: CreateRoomVoiceSelectorProps) {
+ return (
+
+ onSelect(CreateRoomVoice.TextRoom)}
+ disabled={disabled}
+ >
+ }
+ after={value === CreateRoomVoice.TextRoom && }
+ >
+ Text
+
+ Send text messages, videos and GIFs.
+
+
+
+ onSelect(CreateRoomVoice.VoiceRoom)}
+ disabled={disabled}
+ >
+ }
+ after={value === CreateRoomVoice.VoiceRoom && }
+ >
+ Voice
+
+ A room optimized for voice calls.
+
+
+
+
+ );
+}
diff --git a/src/app/components/create-room/index.ts b/src/app/components/create-room/index.ts
index ffd9cb3d..a9c603b2 100644
--- a/src/app/components/create-room/index.ts
+++ b/src/app/components/create-room/index.ts
@@ -3,3 +3,4 @@ export * from './CreateRoomAliasInput';
export * from './RoomVersionSelector';
export * from './utils';
export * from './AdditionalCreatorInput';
+export * from './types';
diff --git a/src/app/components/create-room/types.ts b/src/app/components/create-room/types.ts
new file mode 100644
index 00000000..e35af47a
--- /dev/null
+++ b/src/app/components/create-room/types.ts
@@ -0,0 +1,10 @@
+export enum CreateRoomVoice {
+ TextRoom = 'text',
+ VoiceRoom = 'voice',
+}
+
+export enum CreateRoomKind {
+ Private = 'private',
+ Restricted = 'restricted',
+ Public = 'public',
+}
diff --git a/src/app/components/create-room/utils.ts b/src/app/components/create-room/utils.ts
index f10adf21..3d702f2a 100644
--- a/src/app/components/create-room/utils.ts
+++ b/src/app/components/create-room/utils.ts
@@ -7,11 +7,11 @@ import {
Room,
} from 'matrix-js-sdk';
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
-import { CreateRoomKind } from './CreateRoomKindSelector';
import { RoomType, StateEvent } from '../../../types/matrix/room';
import { getViaServers } from '../../plugins/via-servers';
import { getMxIdServer } from '../../utils/matrix';
import { IPowerLevels } from '../../hooks/usePowerLevels';
+import { CreateRoomKind } from './types';
export const createRoomCreationContent = (
type: RoomType | undefined,
diff --git a/src/app/features/call/SmallWidget.ts b/src/app/features/call/SmallWidget.ts
index 0f9e4064..0f15b321 100644
--- a/src/app/features/call/SmallWidget.ts
+++ b/src/app/features/call/SmallWidget.ts
@@ -40,7 +40,7 @@ export const getWidgetUrl = (
roomId: string,
elementCallUrl: string,
widgetId: string,
- setParams: any
+ setParams: any,
): URL => {
const baseUrl = window.location.origin;
const url = elementCallUrl
@@ -54,6 +54,7 @@ export const getWidgetUrl = (
skipLobby: setParams.skipLobby ?? 'true', // TODO: skipLobby is deprecated, use intent instead (intent doesn't produce the same effect?)
returnToLobby: setParams.returnToLobby ?? 'true',
perParticipantE2EE: setParams.perParticipantE2EE ?? 'true',
+ callIntent: setParams.callIntent ?? 'video',
header: 'none',
confineToRoom: 'true',
theme: setParams.theme ?? 'dark',
@@ -123,7 +124,7 @@ export class SmallWidget extends EventEmitter {
this.mockWidget,
WidgetKind.Room,
true,
- this.roomId
+ this.roomId,
);
this.iframe = iframe;
this.messaging = new ClientWidgetApi(this.mockWidget, iframe, driver);
@@ -189,7 +190,7 @@ export class SmallWidget extends EventEmitter {
// MAKE PERSISTENT HERE
// Send the ack after the widget actually has become sticky.
}
- }
+ },
);
return this.messaging;
@@ -345,7 +346,7 @@ export const getWidgetData = (
client: MatrixClient,
roomId: string,
currentData: object,
- overwriteData: object
+ overwriteData: object,
): IWidgetData => {
// Example: Determine E2EE based on room state if needed
const perParticipantE2EE = true; // Default or based on logic
@@ -381,7 +382,7 @@ export const createVirtualWidget = (
url: URL,
waitForIframeLoad: boolean,
data: IWidgetData,
- roomId: string
+ roomId: string,
): IApp => ({
client,
id,
diff --git a/src/app/features/create-room/CreateRoom.tsx b/src/app/features/create-room/CreateRoom.tsx
index ef8e67be..9cc8bee8 100644
--- a/src/app/features/create-room/CreateRoom.tsx
+++ b/src/app/features/create-room/CreateRoom.tsx
@@ -1,5 +1,5 @@
import React, { FormEventHandler, useCallback, useEffect, useState } from 'react';
-import { MatrixError, Room } from 'matrix-js-sdk';
+import { MatrixError, Room, JoinRule } from 'matrix-js-sdk';
import {
Box,
Button,
@@ -37,22 +37,40 @@ import {
CreateRoomKindSelector,
RoomVersionSelector,
useAdditionalCreators,
+ CreateRoomVoice,
} from '../../components/create-room';
import { RoomType, StateEvent } from '../../../types/matrix/room';
import { IPowerLevels } from '../../hooks/usePowerLevels';
+import { CreateRoomVoiceSelector } from '../../components/create-room/CreateRoomVoiceSelector';
+import { getRoomIconSrc } from '../../utils/room';
-const getCreateRoomKindToIcon = (kind: CreateRoomKind) => {
- if (kind === CreateRoomKind.Private) return Icons.HashLock;
- if (kind === CreateRoomKind.Restricted) return Icons.Hash;
- return Icons.HashGlobe;
+const getCreateRoomKindToIcon = (kind: CreateRoomKind, voice?: CreateRoomVoice) => {
+ const isVoiceRoom = voice === CreateRoomVoice.VoiceRoom;
+
+ let joinRule: JoinRule = JoinRule.Public;
+ if (kind === CreateRoomKind.Restricted) joinRule = JoinRule.Restricted;
+ if (kind === CreateRoomKind.Private) joinRule = JoinRule.Knock;
+
+ return getRoomIconSrc(Icons, isVoiceRoom ? RoomType.Call : undefined, joinRule);
+};
+
+const getCreateRoomVoiceToIcon = (kind: CreateRoomVoice) => {
+ if (kind === CreateRoomVoice.VoiceRoom) return Icons.VolumeHigh;
+ return Icons.Hash;
};
type CreateRoomFormProps = {
defaultKind?: CreateRoomKind;
+ defaultVoice?: CreateRoomVoice;
space?: Room;
onCreate?: (roomId: string) => void;
};
-export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormProps) {
+export function CreateRoomForm({
+ defaultKind,
+ defaultVoice,
+ space,
+ onCreate,
+}: CreateRoomFormProps) {
const mx = useMatrixClient();
const alive = useAlive();
@@ -66,6 +84,7 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
const allowRestricted = space && restrictedSupported(selectedRoomVersion);
+ const [voice, setVoice] = useState(defaultVoice ?? CreateRoomVoice.TextRoom);
const [kind, setKind] = useState(
defaultKind ?? allowRestricted ? CreateRoomKind.Restricted : CreateRoomKind.Private
);
@@ -74,7 +93,6 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
useAdditionalCreators();
const [federation, setFederation] = useState(true);
const [encryption, setEncryption] = useState(false);
- const [callRoom, setCallRoom] = useState(false);
const [knock, setKnock] = useState(false);
const [advance, setAdvance] = useState(false);
@@ -123,7 +141,7 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
const powerOverrides: IPowerLevels = {
events: {},
};
- if (callRoom) {
+ if (voice === CreateRoomVoice.VoiceRoom) {
roomType = RoomType.Call;
powerOverrides.events![StateEvent.GroupCallMemberPrefix] = 0;
}
@@ -150,6 +168,17 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
return (
+ {!space && (
+
+ Type
+
+
+ )}
Access
getCreateRoomKindToIcon(roomKind, voice)}
/>
Name
}
+ before={}
name="nameInput"
autoFocus
size="500"
@@ -184,20 +213,6 @@ export function CreateRoomForm({ defaultKind, space, onCreate }: CreateRoomFormP
disabled={disabled}
/>
-
-
- }
- />
-
{kind === CreateRoomKind.Public && }
diff --git a/src/app/features/create-room/CreateRoomModal.tsx b/src/app/features/create-room/CreateRoomModal.tsx
index c9919ba9..5fa9b6ca 100644
--- a/src/app/features/create-room/CreateRoomModal.tsx
+++ b/src/app/features/create-room/CreateRoomModal.tsx
@@ -23,12 +23,13 @@ import {
} from '../../state/hooks/createRoomModal';
import { CreateRoomModalState } from '../../state/createRoomModal';
import { stopPropagation } from '../../utils/keyboard';
+import { CreateRoomVoice } from '../../components/create-room/types';
type CreateRoomModalProps = {
state: CreateRoomModalState;
};
function CreateRoomModal({ state }: CreateRoomModalProps) {
- const { spaceId } = state;
+ const { spaceId, voice } = state;
const closeDialog = useCloseCreateRoomModal();
const allJoinedRooms = useAllJoinedRoomsSet();
@@ -57,7 +58,7 @@ function CreateRoomModal({ state }: CreateRoomModalProps) {
}}
>
- New Room
+ New {voice === CreateRoomVoice.VoiceRoom && 'Voice '}Room
@@ -74,7 +75,7 @@ function CreateRoomModal({ state }: CreateRoomModalProps) {
direction="Column"
gap="500"
>
-
+
diff --git a/src/app/features/lobby/SpaceItem.tsx b/src/app/features/lobby/SpaceItem.tsx
index 64a97900..3dfbbdea 100644
--- a/src/app/features/lobby/SpaceItem.tsx
+++ b/src/app/features/lobby/SpaceItem.tsx
@@ -36,6 +36,7 @@ import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { useOpenCreateRoomModal } from '../../state/hooks/createRoomModal';
import { useOpenCreateSpaceModal } from '../../state/hooks/createSpaceModal';
import { AddExistingModal } from '../add-existing';
+import { CreateRoomVoice } from '../../components/create-room/types';
function SpaceProfileLoading() {
return (
@@ -249,8 +250,8 @@ function AddRoomButton({ item }: { item: HierarchyItem }) {
setCords(evt.currentTarget.getBoundingClientRect());
};
- const handleCreateRoom = () => {
- openCreateRoomModal(item.roomId);
+ const handleCreateRoom = (voice?: CreateRoomVoice) => {
+ openCreateRoomModal(item.roomId, voice);
setCords(undefined);
};
@@ -281,10 +282,19 @@ function AddRoomButton({ item }: { item: HierarchyItem }) {
radii="300"
variant="Primary"
fill="None"
- onClick={handleCreateRoom}
+ onClick={() => handleCreateRoom(CreateRoomVoice.TextRoom)}
>
New Room
+
diff --git a/src/app/features/room-nav/RoomNavItem.tsx b/src/app/features/room-nav/RoomNavItem.tsx
index 03c2d169..a8e35fbf 100644
--- a/src/app/features/room-nav/RoomNavItem.tsx
+++ b/src/app/features/room-nav/RoomNavItem.tsx
@@ -302,7 +302,7 @@ export function RoomNavItem({
navigateRoom(room.roomId);
}
} else {
- navigateRoom(room.roomId);
+ navigate(linkPath);
}
};
diff --git a/src/app/features/room/RoomViewHeader.tsx b/src/app/features/room/RoomViewHeader.tsx
index 5c430268..ac7a5aa3 100644
--- a/src/app/features/room/RoomViewHeader.tsx
+++ b/src/app/features/room/RoomViewHeader.tsx
@@ -268,7 +268,7 @@ export function RoomViewHeader() {
const { isChatOpen, toggleChat } = useCallState();
const pinnedEvents = useRoomPinnedEvents(room);
const encryptionEvent = useStateEvent(room, StateEvent.RoomEncryption);
- const ecryptedRoom = !!encryptionEvent;
+ const encryptedRoom = !!encryptionEvent;
const avatarMxc = useRoomAvatar(room, direct);
const name = useRoomName(room);
const topic = useRoomTopic(room);
@@ -371,83 +371,83 @@ export function RoomViewHeader() {
- {!ecryptedRoom && (!room.isCallRoom() || isChatOpen) && (
-
- Search
-
- }
- >
- {(triggerRef) => (
-
-
-
- )}
-
- )}
{(!room.isCallRoom() || isChatOpen) && (
-
- Pinned Messages
-
- }
- >
- {(triggerRef) => (
-
+ {!encryptedRoom && (
+
+ Search
+
+ }
>
- {pinnedEvents.length > 0 && (
-
-
- {pinnedEvents.length}
-
-
+ {(triggerRef) => (
+
+
+
)}
-
-
+
)}
-
- )}
- {(!room.isCallRoom() || isChatOpen) && (
- setPinMenuAnchor(undefined),
- clickOutsideDeactivates: true,
- isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
- isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
- escapeDeactivates: stopPropagation,
- }}
- >
- setPinMenuAnchor(undefined)} />
-
- }
- />
+
+ Pinned Messages
+
+ }
+ >
+ {(triggerRef) => (
+
+ {pinnedEvents.length > 0 && (
+
+
+ {pinnedEvents.length}
+
+
+ )}
+
+
+ )}
+
+ setPinMenuAnchor(undefined),
+ clickOutsideDeactivates: true,
+ isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
+ isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
+ escapeDeactivates: stopPropagation,
+ }}
+ >
+ setPinMenuAnchor(undefined)} />
+
+ }
+ />
+ >
)}
{!room.isCallRoom() && screenSize === ScreenSize.Desktop && (
diff --git a/src/app/hooks/useRoomMeta.ts b/src/app/hooks/useRoomMeta.ts
index 8b0ae8cc..086c3a56 100644
--- a/src/app/hooks/useRoomMeta.ts
+++ b/src/app/hooks/useRoomMeta.ts
@@ -20,6 +20,8 @@ export const useRoomName = (room: Room): string => {
const [name, setName] = useState(room.name);
useEffect(() => {
+ setName(room.name);
+
const handleRoomNameChange: RoomEventHandlerMap[RoomEvent.Name] = () => {
setName(room.name);
};
diff --git a/src/app/pages/Router.tsx b/src/app/pages/Router.tsx
index 2e791da2..25744d27 100644
--- a/src/app/pages/Router.tsx
+++ b/src/app/pages/Router.tsx
@@ -68,9 +68,10 @@ import { Create } from './client/create';
import { CreateSpaceModalRenderer } from '../features/create-space';
import { SearchModalRenderer } from '../features/search';
import { getFallbackSession } from '../state/sessions';
+import { pushSessionToSW } from '../../sw-session';
import { CallProvider } from './client/call/CallProvider';
import { PersistentCallContainer } from './client/call/PersistentCallContainer';
-import { pushSessionToSW } from '../../sw-session';
+
export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => {
const { hashRouter } = clientConfig;
diff --git a/src/app/pages/client/call/PersistentCallContainer.tsx b/src/app/pages/client/call/PersistentCallContainer.tsx
index ea435a4c..3b5f8954 100644
--- a/src/app/pages/client/call/PersistentCallContainer.tsx
+++ b/src/app/pages/client/call/PersistentCallContainer.tsx
@@ -47,7 +47,7 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
smallWidgetRef: React.MutableRefObject,
iframeRef: React.MutableRefObject,
skipLobby: boolean,
- themeKind: ThemeKind | null
+ themeKind: ThemeKind | null,
) => {
if (mx?.getUserId()) {
if (activeCallRoomId && !isActiveCallReady) {
@@ -64,7 +64,8 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
returnToLobby: 'true',
perParticipantE2EE: 'true',
theme: themeKind,
- }
+ callIntent: 'audio',
+ },
);
if (
@@ -96,8 +97,8 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
'm.call',
newUrl,
true,
- getWidgetData(mx, roomIdToSet, {}, { skipLobby: true }),
- roomIdToSet
+ getWidgetData(mx, roomIdToSet, {}, { skipLobby: true, callIntent: 'audio' }),
+ roomIdToSet,
);
const smallWidget = new SmallWidget(app);
@@ -109,7 +110,7 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
roomIdToSet,
widgetApiRef.current,
smallWidget,
- iframeElement
+ iframeElement,
);
}
}
@@ -121,7 +122,7 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
clientConfig.elementCallUrl,
activeClientWidget,
registerActiveClientWidgetApi,
- ]
+ ],
);
useEffect(() => {
diff --git a/src/app/state/createRoomModal.ts b/src/app/state/createRoomModal.ts
index 81af5d5b..3d1a6803 100644
--- a/src/app/state/createRoomModal.ts
+++ b/src/app/state/createRoomModal.ts
@@ -1,7 +1,9 @@
import { atom } from 'jotai';
+import { CreateRoomVoice } from '../components/create-room/types';
export type CreateRoomModalState = {
spaceId?: string;
+ voice?: CreateRoomVoice;
};
export const createRoomModalAtom = atom(undefined);
diff --git a/src/app/state/hooks/createRoomModal.ts b/src/app/state/hooks/createRoomModal.ts
index 15db7289..dfb15017 100644
--- a/src/app/state/hooks/createRoomModal.ts
+++ b/src/app/state/hooks/createRoomModal.ts
@@ -1,6 +1,7 @@
import { useCallback } from 'react';
import { useAtomValue, useSetAtom } from 'jotai';
import { createRoomModalAtom, CreateRoomModalState } from '../createRoomModal';
+import { CreateRoomVoice } from '../../components/create-room/types';
export const useCreateRoomModalState = (): CreateRoomModalState | undefined => {
const data = useAtomValue(createRoomModalAtom);
@@ -19,13 +20,13 @@ export const useCloseCreateRoomModal = (): CloseCallback => {
return close;
};
-type OpenCallback = (space?: string) => void;
+type OpenCallback = (space?: string, voice?: CreateRoomVoice) => void;
export const useOpenCreateRoomModal = (): OpenCallback => {
const setSettings = useSetAtom(createRoomModalAtom);
const open: OpenCallback = useCallback(
- (spaceId) => {
- setSettings({ spaceId });
+ (spaceId, voice) => {
+ setSettings({ spaceId, voice });
},
[setSettings]
);