feat: dynamic PiP collision avoidance, reaction pill redesign, channel dedup fix
PiP positioning: - Replace hardcoded layout constants with dynamic DOM measurement system - Obstacle elements declare themselves via data-pip-obstacle="left|bottom" - getPipBounds() queries actual element rects at runtime via getBoundingClientRect - MutationObserver + ResizeObserver re-clamp PiP when obstacles appear/resize - PiP reappears after close when navigating away from voice channel Reactions: - Align reaction pills with Aether Drift prototype - Own-reaction accent: purple → mint (bg, border, count color) - Default pills: subtle white overlay bg + border-soft border - Count text: 12px/semibold/txt-secondary per prototype spec Bug fix: - Deduplicate channel insertion in createChannel store action - Prevents double-add race between REST response and WS broadcast
This commit is contained in:
@@ -172,9 +172,10 @@ export const useServerStore = create<ServerState>((set, get) => ({
|
||||
|
||||
createChannel: async (serverId: string, name: string, type: 'text' | 'voice' | 'video', topic?: string) => {
|
||||
const channel = await api.channels.create(serverId, { name, type, topic });
|
||||
set((state) => ({
|
||||
channels: [...state.channels, channel].sort((a, b) => a.position - b.position),
|
||||
}));
|
||||
set((state) => {
|
||||
if (state.channels.some(c => c.id === channel.id)) return state;
|
||||
return { channels: [...state.channels, channel].sort((a, b) => a.position - b.position) };
|
||||
});
|
||||
return channel;
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user