Removes expensive immer current calls
This commit is contained in:
parent
e4b154950f
commit
364dbe0fd9
16 changed files with 118 additions and 85 deletions
|
@ -6,6 +6,7 @@ import tld from 'utils/tld'
|
|||
import { DotCircle, Handle } from './misc'
|
||||
import useShapeDef from 'hooks/useShape'
|
||||
import useShapesToRender from 'hooks/useShapesToRender'
|
||||
import styled from 'styles'
|
||||
|
||||
export default function Defs(): JSX.Element {
|
||||
const shapeIdsToRender = useShapesToRender()
|
||||
|
@ -15,6 +16,7 @@ export default function Defs(): JSX.Element {
|
|||
<DotCircle id="dot" r={4} />
|
||||
<Handle id="handle" r={4} />
|
||||
<ExpandDef />
|
||||
<ShadowDef />
|
||||
{shapeIdsToRender.map((id) => (
|
||||
<Def key={id} id={id} />
|
||||
))}
|
||||
|
@ -41,9 +43,22 @@ function Def({ id }: { id: string }) {
|
|||
|
||||
function ExpandDef() {
|
||||
const zoom = useSelector((s) => tld.getCurrentCamera(s.data).zoom)
|
||||
|
||||
return (
|
||||
<filter id="expand">
|
||||
<feMorphology operator="dilate" radius={2 / zoom} />
|
||||
</filter>
|
||||
)
|
||||
}
|
||||
|
||||
function ShadowDef() {
|
||||
return (
|
||||
<filter id="hover">
|
||||
<StyledShadow dx="0" dy="0" stdDeviation="1.2" floodOpacity="1" />
|
||||
</filter>
|
||||
)
|
||||
}
|
||||
|
||||
const StyledShadow = styled('feDropShadow', {
|
||||
floodColor: '$selected',
|
||||
})
|
||||
|
|
|
@ -37,7 +37,6 @@ function HoveredShape({ id }: { id: string }) {
|
|||
|
||||
const StyledHoverShape = styled('use', {
|
||||
stroke: '$selected',
|
||||
filter: 'url(#expand)',
|
||||
opacity: 0.1,
|
||||
})
|
||||
|
||||
|
|
|
@ -1,26 +1,34 @@
|
|||
import Command from './command'
|
||||
import history from '../history'
|
||||
import { Data, GroupShape, ShapeType } from 'types'
|
||||
import { getCommonBounds } from 'utils'
|
||||
import { current } from 'immer'
|
||||
import { deepClone, getCommonBounds } from 'utils'
|
||||
import tld from 'utils/tld'
|
||||
import { createShape, getShapeUtils } from 'state/shape-utils'
|
||||
import commands from '.'
|
||||
|
||||
export default function groupCommand(data: Data): void {
|
||||
const cData = current(data)
|
||||
const { currentPageId } = cData
|
||||
const { currentPageId } = data
|
||||
|
||||
const oldSelectedIds = tld.getSelectedIds(cData)
|
||||
const oldSelectedIds = tld.getSelectedIds(data)
|
||||
|
||||
const initialShapes = tld
|
||||
.getSelectedShapes(cData)
|
||||
.getSelectedShapes(data)
|
||||
.sort((a, b) => a.childIndex - b.childIndex)
|
||||
.map((shape) => deepClone(shape))
|
||||
|
||||
const isAllSameParent = initialShapes.every(
|
||||
(shape, i) => i === 0 || shape.parentId === initialShapes[i - 1].parentId
|
||||
)
|
||||
|
||||
// Do we need to ungroup the selected shapes shapes, rather than group them?
|
||||
if (isAllSameParent && initialShapes[0]?.parentId !== currentPageId) {
|
||||
const parent = tld.getShape(data, initialShapes[0]?.parentId) as GroupShape
|
||||
if (parent.children.length === initialShapes.length) {
|
||||
commands.ungroup(data)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let newGroupParentId: string
|
||||
|
||||
const initialShapeIds = initialShapes.map((s) => s.id)
|
||||
|
@ -34,19 +42,11 @@ export default function groupCommand(data: Data): void {
|
|||
if (isAllSameParent) {
|
||||
const parentId = initialShapes[0].parentId
|
||||
if (parentId === currentPageId) {
|
||||
// Create the new group on the current page
|
||||
newGroupParentId = currentPageId
|
||||
} else {
|
||||
// Are all of the parent's children selected?
|
||||
const parent = tld.getShape(data, parentId) as GroupShape
|
||||
|
||||
if (parent.children.length === initialShapes.length) {
|
||||
// !!! Hey! We're not going any further. We need to ungroup those shapes.
|
||||
commands.ungroup(data)
|
||||
return
|
||||
} else {
|
||||
// Make the group inside of the current group
|
||||
newGroupParentId = parentId
|
||||
}
|
||||
// Create the new group as a child of the shapes' current parent group
|
||||
newGroupParentId = parentId
|
||||
}
|
||||
} else {
|
||||
// Find the least-deep parent among the shapes and add the group as a child
|
||||
|
|
|
@ -2,22 +2,20 @@ import Command from './command'
|
|||
import history from '../history'
|
||||
import { Data, ShapeStyles } from 'types'
|
||||
import tld from 'utils/tld'
|
||||
import { setToArray } from 'utils'
|
||||
import { deepClone, setToArray } from 'utils'
|
||||
import { getShapeUtils } from 'state/shape-utils'
|
||||
import { current } from 'immer'
|
||||
|
||||
export default function styleCommand(
|
||||
data: Data,
|
||||
styles: Partial<ShapeStyles>
|
||||
): void {
|
||||
const cData = current(data)
|
||||
const page = tld.getPage(cData)
|
||||
const page = tld.getPage(data)
|
||||
|
||||
const selectedIds = setToArray(tld.getSelectedIds(data))
|
||||
|
||||
const shapesToStyle = selectedIds
|
||||
.flatMap((id) => tld.getDocumentBranch(data, id))
|
||||
.map((id) => page.shapes[id])
|
||||
.map((id) => deepClone(page.shapes[id]))
|
||||
|
||||
history.execute(
|
||||
data,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Command from './command'
|
||||
import history from '../history'
|
||||
import { Data } from 'types'
|
||||
import { current } from 'immer'
|
||||
import { TransformSingleSnapshot } from 'state/sessions/transform-single-session'
|
||||
import tld from 'utils/tld'
|
||||
import { deepClone } from 'utils'
|
||||
|
||||
export default function transformSingleCommand(
|
||||
data: Data,
|
||||
|
@ -11,7 +11,7 @@ export default function transformSingleCommand(
|
|||
after: TransformSingleSnapshot,
|
||||
isCreating: boolean
|
||||
): void {
|
||||
const shape = current(tld.getPage(data).shapes[after.id])
|
||||
const shape = deepClone(tld.getPage(data).shapes[after.id])
|
||||
|
||||
history.execute(
|
||||
data,
|
||||
|
|
|
@ -2,8 +2,7 @@ import { ArrowShape, Data } from 'types'
|
|||
import vec from 'utils/vec'
|
||||
import BaseSession from './base-session'
|
||||
import commands from 'state/commands'
|
||||
import { current } from 'immer'
|
||||
import { getBoundsFromPoints, setToArray } from 'utils'
|
||||
import { deepClone, getBoundsFromPoints, setToArray } from 'utils'
|
||||
import { getShapeUtils } from 'state/shape-utils'
|
||||
import tld from 'utils/tld'
|
||||
|
||||
|
@ -110,7 +109,7 @@ export default class ArrowSession extends BaseSession {
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function getArrowSnapshot(data: Data, id: string) {
|
||||
const initialShape = tld.getPage(current(data)).shapes[id] as ArrowShape
|
||||
const initialShape = deepClone(tld.getPage(data).shapes[id]) as ArrowShape
|
||||
|
||||
return {
|
||||
id,
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { current } from 'immer'
|
||||
import { Bounds, Data, ShapeType } from 'types'
|
||||
import BaseSession from './base-session'
|
||||
import { getShapeUtils } from 'state/shape-utils'
|
||||
import { getBoundsFromPoints, setToArray } from 'utils'
|
||||
import { deepClone, getBoundsFromPoints, setToArray } from 'utils'
|
||||
import vec from 'utils/vec'
|
||||
import tld from 'utils/tld'
|
||||
|
||||
|
@ -67,7 +66,7 @@ export default class BrushSession extends BaseSession {
|
|||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function getBrushSnapshot(data: Data) {
|
||||
const cData = current(data)
|
||||
const cData = data
|
||||
const { selectedIds } = tld.getPageState(cData)
|
||||
|
||||
const shapesToTest = tld
|
||||
|
@ -76,6 +75,7 @@ export function getBrushSnapshot(data: Data) {
|
|||
.filter(
|
||||
(shape) => !(selectedIds.has(shape.id) || selectedIds.has(shape.parentId))
|
||||
)
|
||||
.map(deepClone)
|
||||
|
||||
return {
|
||||
selectedIds: setToArray(selectedIds),
|
||||
|
@ -84,9 +84,10 @@ export function getBrushSnapshot(data: Data) {
|
|||
return [
|
||||
shape.id,
|
||||
{
|
||||
selectId: tld.getTopParentId(cData, shape.id),
|
||||
test: (bounds: Bounds) =>
|
||||
getShapeUtils(shape).hitTestBounds(shape, bounds),
|
||||
selectId: tld.getTopParentId(data, shape.id),
|
||||
test: (bounds: Bounds) => {
|
||||
return getShapeUtils(shape).hitTestBounds(shape, bounds)
|
||||
},
|
||||
},
|
||||
]
|
||||
})
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { current } from 'immer'
|
||||
import { Data, DrawShape } from 'types'
|
||||
import BaseSession from './base-session'
|
||||
import { getShapeUtils } from 'state/shape-utils'
|
||||
import { getBoundsFromPoints } from 'utils'
|
||||
import { deepClone, getBoundsFromPoints } from 'utils'
|
||||
import tld from 'utils/tld'
|
||||
import vec from 'utils/vec'
|
||||
import commands from 'state/commands'
|
||||
|
@ -140,8 +139,8 @@ export default class DrawSession extends BaseSession {
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function getDrawSnapshot(data: Data, shapeId: string) {
|
||||
const page = tld.getPage(current(data))
|
||||
const { points, point } = page.shapes[shapeId] as DrawShape
|
||||
const page = tld.getPage(data)
|
||||
const { points, point } = deepClone(page.shapes[shapeId]) as DrawShape
|
||||
|
||||
return {
|
||||
id: shapeId,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Data, Shape } from 'types'
|
||||
import BaseSession from './base-session'
|
||||
import commands from 'state/commands'
|
||||
import { current } from 'immer'
|
||||
import { getShapeUtils } from 'state/shape-utils'
|
||||
import tld from 'utils/tld'
|
||||
import { deepClone } from 'utils'
|
||||
|
||||
export default class EditSession extends BaseSession {
|
||||
snapshot: EditSnapshot
|
||||
|
@ -35,7 +35,7 @@ export default class EditSession extends BaseSession {
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function getEditSnapshot(data: Data) {
|
||||
const initialShape = tld.getSelectedShapes(current(data))[0]
|
||||
const initialShape = deepClone(tld.getSelectedShapes(data)[0])
|
||||
|
||||
return {
|
||||
currentPageId: data.currentPageId,
|
||||
|
|
|
@ -2,9 +2,9 @@ import { Data } from 'types'
|
|||
import vec from 'utils/vec'
|
||||
import BaseSession from './base-session'
|
||||
import commands from 'state/commands'
|
||||
import { current } from 'immer'
|
||||
import tld from 'utils/tld'
|
||||
import { getShapeUtils } from 'state/shape-utils'
|
||||
import { deepClone } from 'utils'
|
||||
|
||||
export default class HandleSession extends BaseSession {
|
||||
delta = [0, 0]
|
||||
|
@ -69,7 +69,7 @@ export function getHandleSnapshot(
|
|||
shapeId: string,
|
||||
handleId: string
|
||||
) {
|
||||
const initialShape = tld.getPage(current(data)).shapes[shapeId]
|
||||
const initialShape = deepClone(tld.getShape(data, shapeId))
|
||||
|
||||
return {
|
||||
currentPageId: data.currentPageId,
|
||||
|
|
|
@ -2,12 +2,10 @@ import { Data, ShapeType } from 'types'
|
|||
import vec from 'utils/vec'
|
||||
import BaseSession from './base-session'
|
||||
import commands from 'state/commands'
|
||||
import { current } from 'immer'
|
||||
import {
|
||||
clampToRotationToSegments,
|
||||
getBoundsCenter,
|
||||
getCommonBounds,
|
||||
setToArray,
|
||||
} from 'utils'
|
||||
import tld from 'utils/tld'
|
||||
import { getShapeUtils } from 'state/shape-utils'
|
||||
|
@ -95,14 +93,7 @@ export default class RotateSession extends BaseSession {
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function getRotateSnapshot(data: Data) {
|
||||
const cData = current(data)
|
||||
const page = tld.getPage(cData)
|
||||
|
||||
const initialShapes = setToArray(tld.getSelectedIds(data))
|
||||
.flatMap((id) =>
|
||||
tld.getDocumentBranch(cData, id).map((id) => page.shapes[id])
|
||||
)
|
||||
.filter((shape) => !shape.isLocked)
|
||||
const initialShapes = tld.getSelectedBranchSnapshot(data)
|
||||
|
||||
const hasUnlockedShapes = initialShapes.length > 0
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
getCommonBounds,
|
||||
getRelativeTransformedBoundingBox,
|
||||
getTransformedBoundingBox,
|
||||
setToArray,
|
||||
} from 'utils'
|
||||
|
||||
export default class TransformSession extends BaseSession {
|
||||
|
@ -117,14 +116,8 @@ export default class TransformSession extends BaseSession {
|
|||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function getTransformSnapshot(data: Data, transformType: Edge | Corner) {
|
||||
const { currentPageId } = data
|
||||
const page = tld.getPage(data)
|
||||
|
||||
const initialShapes = setToArray(tld.getSelectedIds(data))
|
||||
.flatMap((id) =>
|
||||
tld.getDocumentBranch(data, id).map((id) => page.shapes[id])
|
||||
)
|
||||
.filter((shape) => !shape.isLocked)
|
||||
.map((shape) => deepClone(shape))
|
||||
const initialShapes = tld.getSelectedBranchSnapshot(data)
|
||||
|
||||
const hasUnlockedShapes = initialShapes.length > 0
|
||||
|
||||
|
|
|
@ -2,9 +2,8 @@ import { Data, Edge, Corner } from 'types'
|
|||
import vec from 'utils/vec'
|
||||
import BaseSession from './base-session'
|
||||
import commands from 'state/commands'
|
||||
import { current } from 'immer'
|
||||
import { getShapeUtils } from 'state/shape-utils'
|
||||
import { getTransformedBoundingBox } from 'utils'
|
||||
import { deepClone, getTransformedBoundingBox } from 'utils'
|
||||
import tld from 'utils/tld'
|
||||
|
||||
export default class TransformSingleSession extends BaseSession {
|
||||
|
@ -85,7 +84,7 @@ export function getTransformSingleSnapshot(
|
|||
data: Data,
|
||||
transformType: Edge | Corner
|
||||
) {
|
||||
const shape = tld.getSelectedShapes(current(data))[0]
|
||||
const shape = deepClone(tld.getSelectedShapes(data)[0])
|
||||
const bounds = getShapeUtils(shape).getBounds(shape)
|
||||
|
||||
return {
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Data, GroupShape, Shape, ShapeType } from 'types'
|
|||
import vec from 'utils/vec'
|
||||
import BaseSession from './base-session'
|
||||
import commands from 'state/commands'
|
||||
import { current } from 'immer'
|
||||
import { uniqueId } from 'utils'
|
||||
import { getShapeUtils } from 'state/shape-utils'
|
||||
import tld from 'utils/tld'
|
||||
|
@ -174,32 +173,28 @@ export default class TranslateSession extends BaseSession {
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function getTranslateSnapshot(data: Data) {
|
||||
const cData = current(data)
|
||||
const page = tld.getPage(data)
|
||||
|
||||
// Get selected shapes
|
||||
// Filter out the locked shapes
|
||||
// Collect the branch children for each remaining shape
|
||||
// Filter out doubles using a set
|
||||
// End up with an array of ids for all of the shapes that will change
|
||||
// Map into shapes from data snapshot
|
||||
|
||||
const page = tld.getPage(cData)
|
||||
const selectedShapes = tld
|
||||
.getSelectedShapes(cData)
|
||||
.filter((shape) => !shape.isLocked)
|
||||
const selectedShapes = tld.getSelectedShapeSnapshot(data)
|
||||
|
||||
const hasUnlockedShapes = selectedShapes.length > 0
|
||||
|
||||
const parents = Array.from(
|
||||
const initialParents = Array.from(
|
||||
new Set(selectedShapes.map((s) => s.parentId)).values()
|
||||
)
|
||||
.filter((id) => id !== data.currentPageId)
|
||||
.map((id) => page.shapes[id])
|
||||
.map((id) => {
|
||||
const shape = page.shapes[id]
|
||||
return {
|
||||
id: shape.id,
|
||||
children: shape.children,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
hasUnlockedShapes,
|
||||
currentPageId: data.currentPageId,
|
||||
initialParents: parents.map(({ id, children }) => ({ id, children })),
|
||||
initialParents,
|
||||
initialShapes: selectedShapes.map(({ id, point, parentId }) => ({
|
||||
id,
|
||||
point,
|
||||
|
@ -211,9 +206,8 @@ export function getTranslateSnapshot(data: Data) {
|
|||
const clone: Shape = {
|
||||
...shape,
|
||||
id: uniqueId(),
|
||||
|
||||
parentId: shape.parentId,
|
||||
childIndex: tld.getChildIndexAbove(cData, shape.id),
|
||||
childIndex: tld.getChildIndexAbove(data, shape.id),
|
||||
isGenerated: false,
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import history from './history'
|
|||
import storage from './storage'
|
||||
import clipboard from './clipboard'
|
||||
import * as Sessions from './sessions'
|
||||
import pusher from './coop/client-supa'
|
||||
import coopClient from './coop/client-pusher'
|
||||
import commands from './commands'
|
||||
import {
|
||||
getCommonBounds,
|
||||
|
@ -1162,7 +1162,7 @@ const state = createState({
|
|||
},
|
||||
sendRtCursorMove(data, payload: PointerInfo) {
|
||||
const point = tld.screenToWorld(payload.point, data)
|
||||
pusher.moveCursor(data.currentPageId, point)
|
||||
coopClient.moveCursor(data.currentPageId, point)
|
||||
},
|
||||
moveRtCursor(
|
||||
data,
|
||||
|
@ -1214,7 +1214,7 @@ const state = createState({
|
|||
},
|
||||
connectToRoom(data, payload: { id: string }) {
|
||||
data.room = { id: payload.id, status: 'connecting', peers: {} }
|
||||
pusher.connect(payload.id)
|
||||
coopClient.connect(payload.id)
|
||||
},
|
||||
|
||||
resetPageState(data) {
|
||||
|
|
47
utils/tld.ts
47
utils/tld.ts
|
@ -326,6 +326,40 @@ export default class ProjectUtils {
|
|||
|
||||
/* ----------------- Shapes Related ----------------- */
|
||||
|
||||
/**
|
||||
* Get a deep-cloned
|
||||
* @param data
|
||||
* @param fn
|
||||
*/
|
||||
static getSelectedBranchSnapshot<K>(
|
||||
data: Data,
|
||||
fn: <T extends Shape>(shape: T) => K
|
||||
): ({ id: string } & K)[]
|
||||
static getSelectedBranchSnapshot(data: Data): Shape[]
|
||||
static getSelectedBranchSnapshot<
|
||||
K,
|
||||
F extends <T extends Shape>(shape: T) => K
|
||||
>(data: Data, fn?: F): (Shape | K)[] {
|
||||
const page = this.getPage(data)
|
||||
|
||||
const copies = setToArray(this.getSelectedIds(data))
|
||||
.flatMap((id) =>
|
||||
this.getDocumentBranch(data, id).map((id) => page.shapes[id])
|
||||
)
|
||||
.filter((shape) => !shape.isLocked)
|
||||
.map(deepClone)
|
||||
|
||||
if (fn !== undefined) {
|
||||
return copies.map((shape) => ({ id: shape.id, ...fn(shape) }))
|
||||
}
|
||||
|
||||
return copies
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a deep-cloned array of shapes
|
||||
* @param data
|
||||
*/
|
||||
static getSelectedShapeSnapshot(data: Data): Shape[]
|
||||
static getSelectedShapeSnapshot<K>(
|
||||
data: Data,
|
||||
|
@ -337,7 +371,7 @@ export default class ProjectUtils {
|
|||
>(data: Data, fn?: F): (Shape | K)[] {
|
||||
const copies = this.getSelectedShapes(data)
|
||||
.filter((shape) => !shape.isLocked)
|
||||
.map((shape) => deepClone(shape))
|
||||
.map(deepClone)
|
||||
|
||||
if (fn !== undefined) {
|
||||
return copies.map((shape) => ({ id: shape.id, ...fn(shape) }))
|
||||
|
@ -346,6 +380,17 @@ export default class ProjectUtils {
|
|||
return copies
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of all unique parentIds among a set of shapes.
|
||||
* @param data
|
||||
* @param shapes
|
||||
*/
|
||||
static getUniqueParentIds(data: Data, shapes: Shape[]): string[] {
|
||||
return Array.from(new Set(shapes.map((s) => s.parentId)).values()).filter(
|
||||
(id) => id !== data.currentPageId
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an arbitrary change to shape.
|
||||
* @param data
|
||||
|
|
Loading…
Reference in a new issue