feat(call): add docked call panel and remote cursor overlay
- Implemented DockedCallPanel component to render the docked call interface when an active call is present. - Created RemoteCursorOverlay component to display remote cursor positions during screen sharing. - Added hooks for managing docked call state and remote cursor functionality. - Introduced pending drag state management for seamless transitions between docked and floating states. - Developed embed filter management components for personal and room-wide settings, allowing users to customize URL embed visibility. - Implemented auto-joining functionality for rooms within spaces, enhancing user experience during space navigation. - Added state management for tracking joining progress of rooms in spaces.
This commit is contained in:
@@ -23,7 +23,10 @@ import {
|
||||
Header,
|
||||
config,
|
||||
Spinner,
|
||||
color,
|
||||
toRem,
|
||||
} from 'folds';
|
||||
import { HexColorPicker } from 'react-colorful';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import { SequenceCard } from '../../../components/sequence-card';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
@@ -43,6 +46,8 @@ import { ModalWide } from '../../../styles/Modal.css';
|
||||
import { createUploadAtom, UploadSuccess } from '../../../state/upload';
|
||||
import { CompactUploadCardRenderer } from '../../../components/upload-card';
|
||||
import { useCapabilities } from '../../../hooks/useCapabilities';
|
||||
import { HexColorPickerPopOut } from '../../../components/HexColorPickerPopOut';
|
||||
import { useUserColor } from '../../../hooks/useUserColor';
|
||||
|
||||
type ProfileProps = {
|
||||
profile: UserProfile;
|
||||
@@ -303,6 +308,126 @@ function ProfileDisplayName({ profile, userId }: ProfileProps) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Color picker component for user's profile color
|
||||
* Stored in avatar image metadata, visible to other Paarrot users
|
||||
*/
|
||||
function ProfileColor() {
|
||||
const [userColor, setUserColor, loading] = useUserColor();
|
||||
const [localColor, setLocalColor] = useState(userColor ?? '#3b82f6');
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [error, setError] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
if (userColor) {
|
||||
setLocalColor(userColor);
|
||||
}
|
||||
}, [userColor]);
|
||||
|
||||
const handleColorChange = (newColor: string) => {
|
||||
setLocalColor(newColor);
|
||||
setError(undefined);
|
||||
};
|
||||
|
||||
const handleSaveColor = async () => {
|
||||
setSaving(true);
|
||||
setError(undefined);
|
||||
try {
|
||||
await setUserColor(localColor);
|
||||
} catch (e) {
|
||||
setError('Failed to save. Make sure you have an avatar set.');
|
||||
}
|
||||
setSaving(false);
|
||||
};
|
||||
|
||||
const handleRemoveColor = async () => {
|
||||
setSaving(true);
|
||||
setError(undefined);
|
||||
try {
|
||||
await setUserColor(undefined);
|
||||
setLocalColor('#3b82f6');
|
||||
} catch (e) {
|
||||
setError('Failed to remove color.');
|
||||
}
|
||||
setSaving(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingTile
|
||||
title={
|
||||
<Text as="span" size="L400">
|
||||
Profile Color
|
||||
</Text>
|
||||
}
|
||||
description="Embedded in your avatar. Other Paarrot users will see this color."
|
||||
after={
|
||||
<Box alignItems="Center" gap="200">
|
||||
{loading ? (
|
||||
<Text size="T300" style={{ opacity: 0.7 }}>Loading...</Text>
|
||||
) : (
|
||||
<HexColorPickerPopOut
|
||||
picker={
|
||||
<Box direction="Column" gap="200">
|
||||
<HexColorPicker color={localColor} onChange={handleColorChange} />
|
||||
<Box gap="100" alignItems="Center">
|
||||
<Input
|
||||
size="300"
|
||||
variant="Secondary"
|
||||
style={{ width: toRem(100) }}
|
||||
value={localColor}
|
||||
onChange={(e) => {
|
||||
setLocalColor(e.target.value);
|
||||
setError(undefined);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
size="300"
|
||||
variant="Primary"
|
||||
fill="Solid"
|
||||
radii="300"
|
||||
onClick={handleSaveColor}
|
||||
disabled={saving}
|
||||
>
|
||||
<Text size="B300">{saving ? 'Saving...' : 'Save'}</Text>
|
||||
</Button>
|
||||
</Box>
|
||||
{error && (
|
||||
<Text size="T200" style={{ color: color.Critical.Main }}>{error}</Text>
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
onRemove={userColor ? handleRemoveColor : undefined}
|
||||
>
|
||||
{(onOpen) => (
|
||||
<Button
|
||||
size="300"
|
||||
variant="Secondary"
|
||||
fill="Soft"
|
||||
radii="300"
|
||||
onClick={onOpen}
|
||||
disabled={saving}
|
||||
before={
|
||||
<Box
|
||||
style={{
|
||||
width: toRem(16),
|
||||
height: toRem(16),
|
||||
borderRadius: toRem(4),
|
||||
backgroundColor: userColor ?? localColor,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Text size="T300">{userColor ? 'Change' : 'Choose'}</Text>
|
||||
</Button>
|
||||
)}
|
||||
</HexColorPickerPopOut>
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Profile() {
|
||||
const mx = useMatrixClient();
|
||||
const userId = mx.getUserId()!;
|
||||
@@ -318,6 +443,7 @@ export function Profile() {
|
||||
gap="400"
|
||||
>
|
||||
<ProfileAvatar userId={userId} profile={profile} />
|
||||
<ProfileColor />
|
||||
<ProfileDisplayName userId={userId} profile={profile} />
|
||||
</SequenceCard>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user