feat: two-step registration with avatar/color picker, fix focus ring clipping

Refactor RegisterPage into a two-step flow: credentials first, then
personalization (display name, avatar upload, avatar color). Replace the
dual-panel sliding layout with conditional rendering and CSS keyframe
animations to eliminate overflow-hidden clipping of focus rings.

Supporting changes:
- Server accepts avatarColor on registration
- Auth store resets all user-scoped stores on login/register/logout
- Voice store gains resetSession() for full session cleanup
- Sync presence status to federated instances
- Propagate presence_update to socialStore regardless of origin
This commit is contained in:
Jannis Braun
2026-03-11 03:43:20 +01:00
parent 10be7a44f8
commit c8e2945c07
8 changed files with 364 additions and 84 deletions
+33
View File
@@ -104,6 +104,7 @@ interface VoiceState {
setPermissionMutedUser: (spaceId: string, userId: string, muted: boolean) => void;
getVoiceUsers: (channelId: string) => string[];
clearAllVoiceUsers: () => void;
resetSession: () => void;
clearVoiceUsersForOrigin: (origin: string) => void;
leaveVoice: () => void;
handleForceDisconnect: () => void;
@@ -365,6 +366,38 @@ export const useVoiceStore = create<VoiceState>()(
clearAllVoiceUsers: () => set({ voiceUsers: new Map(), voiceUserStates: new Map() }),
resetSession: () => set({
// Connection state
voiceUsers: new Map(),
voiceUserStates: new Map(),
currentVoiceChannelId: null,
participants: [],
speakingParticipantIds: new Set(),
connectionError: null,
isLiveKitConnected: false,
connectionQuality: 'unknown',
focusedParticipantId: null,
// Call state
incomingCall: null,
outgoingCall: null,
activeDmCall: null,
// Per-session media state
isCameraOn: false,
isScreenSharing: false,
// Per-session maps
participantVolumes: new Map(),
participantMutes: new Map(),
deafenedUserIds: new Set(),
streamVolumes: new Map(),
streamMutes: new Map(),
watchingStreams: new Set(),
unwatchedCameras: new Set(),
// Server-enforced restrictions
serverMutedUserIds: new Set(),
serverDeafenedUserIds: new Set(),
permissionMutedUserIds: new Set(),
}),
clearVoiceUsersForOrigin: (origin: string) => {
const { channelOriginMap } = useSpaceStore.getState();
set((state) => {