fix: remove CSS transform scaling from pinned shapes
Pinned shapes should only stay fixed in screen position, not fixed in visual size. The CSS transform: scale() was causing shapes to appear differently sized when pinned. Now pinned shapes: - Stay at a fixed screen position (don't move when panning) - Scale normally with zoom (get bigger/smaller like other shapes) - Don't change appearance when pin is toggled 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
cc1928852f
commit
72c2e52ae7
|
|
@ -1,43 +0,0 @@
|
||||||
---
|
|
||||||
id: task-053
|
|
||||||
title: Fix user identity caching on logout/login
|
|
||||||
status: Done
|
|
||||||
assignee: []
|
|
||||||
created_date: '2025-12-15 23:40'
|
|
||||||
updated_date: '2025-12-15 23:40'
|
|
||||||
labels:
|
|
||||||
- bug-fix
|
|
||||||
- auth
|
|
||||||
- presence
|
|
||||||
dependencies: []
|
|
||||||
priority: high
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
|
||||||
Fixed issues with user identity state not being properly cleared/restored during logout and login cycles, causing duplicate cursors and stale presence data in the social network graph.
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
<!-- AC:BEGIN -->
|
|
||||||
- [x] #1 Crypto keys and tldraw user IDs persist across logout (account data)
|
|
||||||
- [x] #2 Session-specific data cleared on logout (permissions, graph cache)
|
|
||||||
- [x] #3 No duplicate cursors when logging out and back in
|
|
||||||
- [x] #4 Tools load properly after login from logged-out state
|
|
||||||
- [x] #5 CryptIDDropdown and NetworkGraph reset state on logout
|
|
||||||
<!-- AC:END -->
|
|
||||||
|
|
||||||
## Implementation Notes
|
|
||||||
|
|
||||||
<!-- SECTION:NOTES:BEGIN -->
|
|
||||||
Implemented in commits df80a3f and related changes to Board.tsx.
|
|
||||||
|
|
||||||
Key changes:
|
|
||||||
- sessionPersistence.ts: Added session-logged-in and session-cleared events, preserve tldraw user IDs and crypto keys on logout
|
|
||||||
- Board.tsx: Check localStorage directly for auth in onMount, listen for session events
|
|
||||||
- CryptIDDropdown.tsx: Clear connections state on logout
|
|
||||||
- useNetworkGraph.ts: Clear graph cache on logout
|
|
||||||
|
|
||||||
The root cause was clearing tldraw-user-id-* keys on logout, which created new presence IDs on each login while old presence records persisted in Automerge.
|
|
||||||
<!-- SECTION:NOTES:END -->
|
|
||||||
|
|
@ -64,8 +64,6 @@ export interface StandardizedToolWrapperProps {
|
||||||
onTagsChange?: (tags: string[]) => void
|
onTagsChange?: (tags: string[]) => void
|
||||||
/** Whether tags can be edited */
|
/** Whether tags can be edited */
|
||||||
tagsEditable?: boolean
|
tagsEditable?: boolean
|
||||||
/** Zoom level when the shape was pinned (for constant visual size) */
|
|
||||||
pinnedAtZoom?: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -92,67 +90,13 @@ export const StandardizedToolWrapper: React.FC<StandardizedToolWrapperProps> = (
|
||||||
tags = [],
|
tags = [],
|
||||||
onTagsChange,
|
onTagsChange,
|
||||||
tagsEditable = true,
|
tagsEditable = true,
|
||||||
pinnedAtZoom,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [isHoveringHeader, setIsHoveringHeader] = useState(false)
|
const [isHoveringHeader, setIsHoveringHeader] = useState(false)
|
||||||
const [isEditingTags, setIsEditingTags] = useState(false)
|
const [isEditingTags, setIsEditingTags] = useState(false)
|
||||||
const [editingTagInput, setEditingTagInput] = useState('')
|
const [editingTagInput, setEditingTagInput] = useState('')
|
||||||
const [zoomScale, setZoomScale] = useState(1)
|
|
||||||
const tagInputRef = useRef<HTMLInputElement>(null)
|
const tagInputRef = useRef<HTMLInputElement>(null)
|
||||||
const isDarkMode = useIsDarkMode()
|
const isDarkMode = useIsDarkMode()
|
||||||
|
|
||||||
// Calculate zoom compensation scale when pinned
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isPinnedToView || !editor) {
|
|
||||||
setZoomScale(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get pinnedAtZoom from shape meta if not passed as prop
|
|
||||||
let originalZoom = pinnedAtZoom
|
|
||||||
if (!originalZoom && shapeId) {
|
|
||||||
const shape = editor.getShape(shapeId)
|
|
||||||
originalZoom = (shape?.meta as any)?.pinnedAtZoom
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!originalZoom) {
|
|
||||||
setZoomScale(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateScale = () => {
|
|
||||||
const currentZoom = editor.getCamera().z
|
|
||||||
// Scale inversely to zoom to maintain constant visual size
|
|
||||||
const scale = originalZoom / currentZoom
|
|
||||||
setZoomScale(scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial scale calculation
|
|
||||||
updateScale()
|
|
||||||
|
|
||||||
// Listen for camera changes
|
|
||||||
const handleChange = () => {
|
|
||||||
updateScale()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use requestAnimationFrame for smooth updates
|
|
||||||
let rafId: number | null = null
|
|
||||||
const throttledUpdate = () => {
|
|
||||||
if (rafId) return
|
|
||||||
rafId = requestAnimationFrame(() => {
|
|
||||||
updateScale()
|
|
||||||
rafId = null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.on('change' as any, throttledUpdate)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
editor.off('change' as any, throttledUpdate)
|
|
||||||
if (rafId) cancelAnimationFrame(rafId)
|
|
||||||
}
|
|
||||||
}, [isPinnedToView, editor, shapeId, pinnedAtZoom])
|
|
||||||
|
|
||||||
// Handle Esc key to exit maximize mode
|
// Handle Esc key to exit maximize mode
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isMaximized || !onMaximize) return
|
if (!isMaximized || !onMaximize) return
|
||||||
|
|
@ -229,11 +173,6 @@ export const StandardizedToolWrapper: React.FC<StandardizedToolWrapperProps> = (
|
||||||
? `${primaryColor}15` // 15% opacity
|
? `${primaryColor}15` // 15% opacity
|
||||||
: `${primaryColor}10` // 10% opacity
|
: `${primaryColor}10` // 10% opacity
|
||||||
|
|
||||||
// Calculate transform for pinned shapes
|
|
||||||
const pinnedTransform = isPinnedToView && zoomScale !== 1
|
|
||||||
? `scale(${zoomScale})`
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
const wrapperStyle: React.CSSProperties = {
|
const wrapperStyle: React.CSSProperties = {
|
||||||
width: typeof width === 'number' ? `${width}px` : width,
|
width: typeof width === 'number' ? `${width}px` : width,
|
||||||
height: isMinimized ? 40 : (typeof height === 'number' ? `${height}px` : height), // Minimized height is just the header
|
height: isMinimized ? 40 : (typeof height === 'number' ? `${height}px` : height), // Minimized height is just the header
|
||||||
|
|
@ -251,9 +190,6 @@ export const StandardizedToolWrapper: React.FC<StandardizedToolWrapperProps> = (
|
||||||
pointerEvents: 'auto',
|
pointerEvents: 'auto',
|
||||||
transition: isPinnedToView ? 'box-shadow 0.2s ease' : 'height 0.2s ease, box-shadow 0.2s ease',
|
transition: isPinnedToView ? 'box-shadow 0.2s ease' : 'height 0.2s ease, box-shadow 0.2s ease',
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
// Apply zoom compensation transform for pinned shapes
|
|
||||||
transform: pinnedTransform,
|
|
||||||
transformOrigin: 'top left',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerStyle: React.CSSProperties = {
|
const headerStyle: React.CSSProperties = {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue