feat: Implement sub-room functionality

- Added support for sub-rooms in the SpaceHierarchy component, allowing rooms to have nested child rooms.
- Introduced UnjoinedSubRoomItem component to display unjoined sub-rooms with a join button.
- Created SubRooms settings page for managing sub-rooms, including adding and removing sub-rooms.
- Updated RoomNavItem to include options for creating sub-rooms.
- Enhanced room fetching logic to filter out sub-rooms from the main room list.
- Added hooks for managing sub-rooms state and permissions.
- Updated relevant state management to handle sub-room creation and association with parent rooms.
This commit is contained in:
2026-03-15 16:25:53 +11:00
parent a0c0068997
commit c7bd376292
21 changed files with 917 additions and 111 deletions

View File

@@ -307,6 +307,7 @@ export const isAuthenticatedMediaUrl = (url: string): boolean =>
* Downloads media with optional authentication.
* For authenticated media URLs, the access token is required.
* Falls back to unauthenticated request if authenticated request fails with 401.
* Returns an empty blob with error type if all attempts fail, to prevent cascading failures.
*/
export const downloadMedia = async (src: string, accessToken?: string | null): Promise<Blob> => {
const needsAuth = isAuthenticatedMediaUrl(src);
@@ -316,19 +317,26 @@ export const downloadMedia = async (src: string, accessToken?: string | null): P
headers.Authorization = `Bearer ${accessToken}`;
}
let res = await fetch(src, { method: 'GET', headers });
// If we got a 401 and we tried with auth, fallback to unauthenticated request
if (!res.ok && res.status === 401 && needsAuth && accessToken) {
console.warn('[downloadMedia] Auth failed (401), attempting unauthenticated fallback');
res = await fetch(src, { method: 'GET' });
try {
let res = await fetch(src, { method: 'GET', headers });
// If we got a 401 and we tried with auth, fallback to unauthenticated request
if (!res.ok && res.status === 401 && needsAuth && accessToken) {
console.warn('[downloadMedia] Auth failed (401), attempting unauthenticated fallback for:', src);
res = await fetch(src, { method: 'GET' });
}
if (!res.ok) {
console.warn(`[downloadMedia] Failed to download media (${res.status}) from:`, src);
throw new Error(`Failed to download media: ${res.status}`);
}
const blob = await res.blob();
return blob;
} catch (error) {
// Log but re-throw so callers can handle appropriately
console.warn('[downloadMedia] Error downloading media:', error);
throw error;
}
if (!res.ok) {
throw new Error(`Failed to download media: ${res.status}`);
}
const blob = await res.blob();
return blob;
};
export const downloadEncryptedMedia = async (