multiuser
This commit is contained in:
184
src/app/components/account-switcher/AccountSwitcher.tsx
Normal file
184
src/app/components/account-switcher/AccountSwitcher.tsx
Normal file
@@ -0,0 +1,184 @@
|
||||
import React from 'react';
|
||||
import { Box, Text, Icon, Icons, MenuItem, config } from 'folds';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { sessionsAtom, Session } from '../../state/sessions';
|
||||
import { setLocalStorageItem } from '../../state/utils/atomWithLocalStorage';
|
||||
import { getMxIdServer } from '../../utils/matrix';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
|
||||
type AccountSwitcherProps = {
|
||||
currentUserId?: string;
|
||||
};
|
||||
|
||||
const deleteAllMatrixDatabases = async (): Promise<void> => {
|
||||
console.log('[AccountSwitcher] Deleting all Matrix IndexedDB databases...');
|
||||
|
||||
// List of known Matrix database names/patterns to delete
|
||||
const dbsToDelete = [
|
||||
'matrix-js-sdk::matrix-sdk-crypto', // Shared crypto store (causes conflicts)
|
||||
];
|
||||
|
||||
// Try to get all databases if the API is available
|
||||
try {
|
||||
if ('databases' in indexedDB) {
|
||||
const allDbs = await (indexedDB as any).databases();
|
||||
const matrixDbs = allDbs
|
||||
.map((db: any) => db.name)
|
||||
.filter((name: string) =>
|
||||
name && (
|
||||
name.startsWith('matrix-js-sdk') ||
|
||||
name.includes('@@') ||
|
||||
name.startsWith('sync@@') ||
|
||||
name.startsWith('crypto@@')
|
||||
)
|
||||
);
|
||||
dbsToDelete.push(...matrixDbs);
|
||||
console.log('[AccountSwitcher] Found Matrix databases:', matrixDbs);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[AccountSwitcher] Could not enumerate databases:', e);
|
||||
}
|
||||
|
||||
// Delete each database with timeout
|
||||
const deletePromises = dbsToDelete.map(async (dbName) => {
|
||||
try {
|
||||
const deletePromise = new Promise<void>((resolve, reject) => {
|
||||
const request = indexedDB.deleteDatabase(dbName);
|
||||
request.onsuccess = () => {
|
||||
console.log(`[AccountSwitcher] ✅ Deleted database: ${dbName}`);
|
||||
resolve();
|
||||
};
|
||||
request.onerror = () => {
|
||||
console.warn(`[AccountSwitcher] ⚠️ Failed to delete: ${dbName}`, request.error);
|
||||
resolve(); // Don't reject, just continue
|
||||
};
|
||||
request.onblocked = () => {
|
||||
console.warn(`[AccountSwitcher] ⚠️ Delete blocked: ${dbName}`);
|
||||
resolve(); // Don't reject, reload will handle it
|
||||
};
|
||||
});
|
||||
|
||||
// Add 1-second timeout per database
|
||||
const timeoutPromise = new Promise<void>((resolve) => {
|
||||
setTimeout(() => {
|
||||
console.warn(`[AccountSwitcher] ⏱️ Delete timeout: ${dbName}`);
|
||||
resolve();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
await Promise.race([deletePromise, timeoutPromise]);
|
||||
} catch (error) {
|
||||
console.warn(`[AccountSwitcher] Error deleting ${dbName}:`, error);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(deletePromises);
|
||||
console.log('[AccountSwitcher] ✅ Database cleanup complete');
|
||||
};
|
||||
|
||||
export function AccountSwitcher({ currentUserId }: AccountSwitcherProps) {
|
||||
const sessions = useAtomValue(sessionsAtom);
|
||||
const mx = useMatrixClient();
|
||||
|
||||
const handleSwitchAccount = async (session: Session) => {
|
||||
if (session.userId === currentUserId) return;
|
||||
|
||||
console.log('[AccountSwitcher] Switching account from', currentUserId, 'to', session.userId);
|
||||
|
||||
// Stop the current client before switching
|
||||
if (mx) {
|
||||
console.log('[AccountSwitcher] Stopping current Matrix client...');
|
||||
try {
|
||||
mx.stopClient();
|
||||
console.log('[AccountSwitcher] Matrix client stopped');
|
||||
} catch (error) {
|
||||
console.error('[AccountSwitcher] Error stopping client:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the new current session
|
||||
setLocalStorageItem('currentSessionUserId', session.userId);
|
||||
|
||||
// Delete all Matrix databases to prevent conflicts
|
||||
await deleteAllMatrixDatabases();
|
||||
|
||||
// Small delay to ensure cleanup is complete
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
// Reload the page to initialize the new session
|
||||
console.log('[AccountSwitcher] Reloading page...');
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// If no current user, don't show anything
|
||||
if (!currentUserId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Filter out other sessions (not current)
|
||||
const otherSessions = sessions.filter(s => s.userId !== currentUserId);
|
||||
const hasMultipleAccounts = sessions.length > 1;
|
||||
|
||||
return (
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400" style={{ padding: config.space.S200 }}>
|
||||
{hasMultipleAccounts ? 'Switch Account' : 'Accounts'}
|
||||
</Text>
|
||||
|
||||
{/* Show current account */}
|
||||
{currentUserId && (
|
||||
<MenuItem
|
||||
key={currentUserId}
|
||||
size="300"
|
||||
radii="300"
|
||||
variant="SurfaceVariant"
|
||||
disabled
|
||||
after={<Icon size="100" src={Icons.Check} filled />}
|
||||
>
|
||||
<Box gap="200" alignItems="Center">
|
||||
<Icon size="100" src={Icons.User} />
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="T300" truncate>
|
||||
{currentUserId}
|
||||
</Text>
|
||||
{getMxIdServer(currentUserId) && (
|
||||
<Text size="T200" priority="300" truncate>
|
||||
{getMxIdServer(currentUserId)}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{/* Show other accounts if any */}
|
||||
{otherSessions.map((session) => {
|
||||
const server = getMxIdServer(session.userId);
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={session.userId}
|
||||
size="300"
|
||||
radii="300"
|
||||
onClick={() => handleSwitchAccount(session)}
|
||||
variant="Surface"
|
||||
>
|
||||
<Box gap="200" alignItems="Center">
|
||||
<Icon size="100" src={Icons.User} />
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="T300" truncate>
|
||||
{session.userId}
|
||||
</Text>
|
||||
{server && (
|
||||
<Text size="T200" priority="300" truncate>
|
||||
{server}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
1
src/app/components/account-switcher/index.ts
Normal file
1
src/app/components/account-switcher/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { AccountSwitcher } from './AccountSwitcher';
|
||||
Reference in New Issue
Block a user