From 4cae19c2fdaa3988b4194076eb686d8e65a5508c Mon Sep 17 00:00:00 2001 From: Mingze Xiao Date: Wed, 27 May 2020 15:16:02 -0700 Subject: [PATCH] feat(region): Retain annotation message between pages --- src/region/RegionAnnotations.tsx | 36 +++++++++---------- src/region/RegionContainer.tsx | 5 +++ src/region/RegionCreator.scss | 2 +- src/region/RegionCreator.tsx | 32 ++++++++--------- .../__tests__/RegionAnnotations-test.tsx | 24 +++++++------ src/region/__tests__/RegionContainer-test.tsx | 2 ++ src/region/__tests__/RegionCreator-test.tsx | 29 ++++----------- src/store/creator/__mocks__/creatorState.ts | 2 +- src/store/creator/__tests__/reducer-test.ts | 24 +++++++++++-- src/store/creator/__tests__/selectors-test.ts | 9 +++-- src/store/creator/actions.ts | 4 ++- src/store/creator/reducer.ts | 10 +++++- src/store/creator/selectors.ts | 1 + src/store/creator/types.ts | 2 +- 14 files changed, 104 insertions(+), 78 deletions(-) diff --git a/src/region/RegionAnnotations.tsx b/src/region/RegionAnnotations.tsx index 261740b94..a4b67cd9c 100644 --- a/src/region/RegionAnnotations.tsx +++ b/src/region/RegionAnnotations.tsx @@ -1,10 +1,10 @@ -import merge from 'lodash/merge'; import * as React from 'react'; import PopupReply from '../components/Popups/PopupReply'; import RegionCreator from './RegionCreator'; import RegionList from './RegionList'; import RegionRect, { RegionRectRef } from './RegionRect'; import { AnnotationRegion, Rect } from '../@types'; +import { CreateArg } from './actions'; import { CreatorItem, CreatorStatus } from '../store/creator'; import { scaleShape } from './regionUtil'; import './RegionAnnotations.scss'; @@ -12,11 +12,13 @@ import './RegionAnnotations.scss'; type Props = { activeAnnotationId: string | null; annotations: AnnotationRegion[]; - createRegion: (arg: { location: number; message: string; shape: Rect }) => void; + createRegion: (arg: CreateArg) => void; isCreating: boolean; + message: string; page: number; scale: number; setActiveAnnotationId: (annotationId: string | null) => void; + setMessage: (message: string) => void; setStaged: (staged: CreatorItem | null) => void; setStatus: (status: CreatorStatus) => void; staged?: CreatorItem | null; @@ -36,11 +38,6 @@ export default class RegionAnnotations extends React.PureComponent state: State = {}; - setStaged(data: Partial | null): void { - const { page, setStaged, staged } = this.props; - setStaged(data ? merge({ location: page, message: '' }, staged, data) : data); - } - setStatus(status: CreatorStatus): void { const { setStatus } = this.props; setStatus(status); @@ -53,34 +50,37 @@ export default class RegionAnnotations extends React.PureComponent }; handleCancel = (): void => { - this.setStaged(null); + const { setMessage, setStaged } = this.props; + setMessage(''); + setStaged(null); this.setStatus(CreatorStatus.init); }; handleChange = (text?: string): void => { - this.setStaged({ message: text }); + const { setMessage } = this.props; + setMessage(text || ''); }; handleStart = (): void => { - this.setStaged(null); + const { setStaged } = this.props; + setStaged(null); this.setStatus(CreatorStatus.init); }; handleStop = (shape: Rect): void => { - const { scale } = this.props; - - this.setStaged({ shape: scaleShape(shape, scale, true) }); + const { page, scale, setStaged } = this.props; + setStaged({ location: page, shape: scaleShape(shape, scale, true) }); this.setStatus(CreatorStatus.staged); }; handleSubmit = (): void => { - const { createRegion, staged } = this.props; + const { createRegion, message, staged } = this.props; if (!staged) { return; } - createRegion(staged); + createRegion({ ...staged, message }); }; setRectRef = (rectRef: RegionRectRef): void => { @@ -88,9 +88,8 @@ export default class RegionAnnotations extends React.PureComponent }; render(): JSX.Element { - const { activeAnnotationId, annotations, isCreating, scale, staged, status } = this.props; + const { activeAnnotationId, annotations, isCreating, message, scale, staged, status } = this.props; const { rectRef } = this.state; - const canDraw = !staged || !staged.message; const canReply = status !== CreatorStatus.init; const isPending = status === CreatorStatus.pending; @@ -108,7 +107,6 @@ export default class RegionAnnotations extends React.PureComponent {/* Layer 2: Drawn (unsaved) incomplete annotation target, if any */} {isCreating && ( onChange={this.handleChange} onSubmit={this.handleSubmit} reference={rectRef} - value={staged.message} + value={message} /> )} diff --git a/src/region/RegionContainer.tsx b/src/region/RegionContainer.tsx index a5570c948..9a5c472e3 100644 --- a/src/region/RegionContainer.tsx +++ b/src/region/RegionContainer.tsx @@ -7,9 +7,11 @@ import { getActiveAnnotationId, getAnnotationMode, getAnnotationsForLocation, + getCreatorMessage, getCreatorStagedForLocation, getCreatorStatus, setActiveAnnotationIdAction, + setMessageAction, setStagedAction, setStatusAction, } from '../store'; @@ -22,6 +24,7 @@ export type Props = { activeAnnotationId: string | null; annotations: AnnotationRegion[]; isCreating: boolean; + message: string; staged: CreatorItem | null; status: CreatorStatus; }; @@ -30,6 +33,7 @@ export const mapStateToProps = (state: AppState, { page }: { page: number }): Pr activeAnnotationId: getActiveAnnotationId(state), annotations: getAnnotationsForLocation(state, page).filter(isRegion), isCreating: getAnnotationMode(state) === 'region', + message: getCreatorMessage(state), staged: getCreatorStagedForLocation(state, page), status: getCreatorStatus(state), }); @@ -37,6 +41,7 @@ export const mapStateToProps = (state: AppState, { page }: { page: number }): Pr export const mapDispatchToProps = { createRegion: createRegionAction, setActiveAnnotationId: setActiveAnnotationIdAction, + setMessage: setMessageAction, setStaged: setStagedAction, setStatus: setStatusAction, }; diff --git a/src/region/RegionCreator.scss b/src/region/RegionCreator.scss index 0941fd076..d8e202a2d 100644 --- a/src/region/RegionCreator.scss +++ b/src/region/RegionCreator.scss @@ -2,7 +2,7 @@ $region_cursor_32: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAA $region_cursor_32_2x: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAQKADAAQAAAABAAAAQAAAAABlmWCKAAABWUlEQVR4Ae2YOxKDIBCGNaM5R4pYWOT+B0lhYQrPEZ1JGGcoZIy6Lj4IH40DLAt8+8srSUgQgAAEIAABCEAAAnESSM867Y9JmrGlJi1pf1li9M822dknd7/dREN8NY3IHgWIcB1oPBdZqVLsVFCAJRHK113dtbsFCtg78iZib9Nn7vTbmshenbJdsihgF8wjnZRF0ZdWdT1S+7tI+8+7nlGAS+Ss+bX7/Nx8UMAcoaPr506A2vGhAC3Brdq7J76t+oleAQDYSlqh+FW/CHk6meVSP77WiOh/gUUvp1NytpF7lGXSdd2U6aCubds+n+fuxXBgNshkWZY8q6ovQwEDNOsz3hQgHcLa26DtBwVYEsqveheQRsKsGbwIKYPmtXn02yAAvOopQGcoIMCgeR0yCvCKM0Bn0StAfReQBp2ToJQY9hCAAAQgAAEIQAACEIAABCAAAQhAwDOBLxaXQ2IYNsXoAAAAAElFTkSuQmCC'; $region_cursor_32_3x: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAYKADAAQAAAABAAAAYAAAAACK+310AAACaUlEQVR4Ae3bwUrDQBDG8VTE+Bg9CD0IBd//IYSCh4KHPobxopuDYQgGOtNvdhvy39O2zc5uf9Odpol2HQ0BBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAgXSBXfoMdzDBT2mZy9iVFo3/EB3IOI0ACdA4hqM8hkeudODLfi9Z+eflIonDDpAwxoOQgLidZOTmSpBV85YRVfmya2AHWI0GfRLQAN1OuekSZCGWfkxl/4hjB9gsNOiTgAbodsrVlKBSCr7Kwnu7+Fl/KGXkefbc3T9kBzROEQlonIDVlCDr9Ho4dMMwdH3fdx/ns30p3M8+21laGDtgSabS8ySgEvTSNKssQUtvxvt8xrUd7xrYAV4x8fEkQAzqDbe5EuS9BO0F9R7PDvCKiY8nAWJQb7hNlKClS81erIzj2QEZqo6YJMCBlXEoCchQdcQkAQ6sjENJQIaqI2b6WVDFy7y9aq6aZ03sAMenNeNQEpCh6ogZ/seCa+ewZeHteOy+y52sW9t4N+yvjXfFbm1PJcb76TSFqVmC0r8DpndVOiO+xbOvRfvqeNF1RMdVTcD4SVM0i67aAYp1RWJULUGRBf43JuOmvJ2nZgniS9jKN+iTgAbodsr07wDVdi5nU/xpos0cfY0AJUjjGI5CAsJ0moEkQOMYjkICwnSagSRA4xiOQgLCdJqBJEDjGI5CAsJ0moEkQOMYjkICwnSagSRA4xiOQgLCdJqBJEDjGI6SfkcsvLLZQC5Hz0B4qBGgBGkciYIAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIITAK/BlNVeN572e8AAAAASUVORK5CYII='; -.ba-RegionCreator.is-active { +.ba-RegionCreator { cursor: url($region_cursor_32) 16 16, crosshair; /* Legacy */ cursor: image-set(url($region_cursor_32) 1x, url($region_cursor_32_2x) 2x, url($region_cursor_32_3x) 3x) 16 16, crosshair; /* Webkit */ /* stylelint-disable-line */ } diff --git a/src/region/RegionCreator.tsx b/src/region/RegionCreator.tsx index 693fc352e..6621585b4 100644 --- a/src/region/RegionCreator.tsx +++ b/src/region/RegionCreator.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import classNames from 'classnames'; +import noop from 'lodash/noop'; import PopupCursor from '../components/Popups/PopupCursor'; import RegionRect, { RegionRectRef } from './RegionRect'; import useAutoScroll from '../common/useAutoScroll'; @@ -8,7 +9,6 @@ import { styleShape } from './regionUtil'; import './RegionCreator.scss'; type Props = { - canDraw: boolean; className?: string; onStart: () => void; onStop: (shape: Rect) => void; @@ -19,7 +19,7 @@ const MIN_Y = 0; // Minimum region y position must be positive const MIN_SIZE = 10; // Minimum region size must be large enough to be clickable const MOUSE_PRIMARY = 1; // Primary mouse button -export default function RegionCreator({ canDraw, className, onStart, onStop }: Props): JSX.Element { +export default function RegionCreator({ className, onStart, onStop }: Props): JSX.Element { const [isDrawing, setIsDrawing] = React.useState(false); const [isHovering, setIsHovering] = React.useState(false); const creatorElRef = React.useRef(null); @@ -154,18 +154,6 @@ export default function RegionCreator({ canDraw, className, onStart, onStop }: P const handleTouchStart = ({ targetTouches }: React.TouchEvent): void => { startDraw(targetTouches[0].clientX, targetTouches[0].clientY); }; - const eventHandlers = canDraw - ? { - onClick: handleClick, - onMouseDown: handleMouseDown, - onMouseOut: handleMouseOut, - onMouseOver: handleMouseOver, - onTouchCancel: handleTouchCancel, - onTouchEnd: handleTouchEnd, - onTouchMove: handleTouchMove, - onTouchStart: handleTouchStart, - } - : {}; const renderStep = (callback: () => void): void => { renderHandleRef.current = window.requestAnimationFrame(callback); @@ -218,12 +206,22 @@ export default function RegionCreator({ canDraw, className, onStart, onStop }: P return (
{isDrawing && } - {!isDrawing && isHovering && canDraw && } + {!isDrawing && isHovering && }
); } diff --git a/src/region/__tests__/RegionAnnotations-test.tsx b/src/region/__tests__/RegionAnnotations-test.tsx index e169a5418..bcd0646d2 100644 --- a/src/region/__tests__/RegionAnnotations-test.tsx +++ b/src/region/__tests__/RegionAnnotations-test.tsx @@ -18,13 +18,15 @@ jest.mock('../regionUtil', () => ({ scaleShape: jest.fn(value => value), })); -describe('RegionAnnotations', () => { +describe('components/region/RegionAnnotations', () => { const defaults = { activeAnnotationId: null, createRegion: jest.fn(), + message: 'test', page: 1, scale: 1, setActiveAnnotationId: jest.fn(), + setMessage: jest.fn(), setStaged: jest.fn(), setStatus: jest.fn(), staged: null, @@ -40,7 +42,6 @@ describe('RegionAnnotations', () => { const getRectRef = (): HTMLDivElement => document.createElement('div'); const getStaged = (): CreatorItem => ({ location: 1, - message: 'test', shape: getRect(), }); const getWrapper = (props = {}): ShallowWrapper => shallow(); @@ -60,6 +61,7 @@ describe('RegionAnnotations', () => { test('should reset the staged state and status', () => { instance.handleCancel(); + expect(defaults.setMessage).toHaveBeenCalledWith(''); expect(defaults.setStaged).toHaveBeenCalledWith(null); expect(defaults.setStatus).toHaveBeenCalledWith(CreatorStatus.init); }); @@ -69,11 +71,7 @@ describe('RegionAnnotations', () => { test('should set the staged state with the new message', () => { instance.handleChange('test'); - expect(defaults.setStaged).toHaveBeenCalledWith( - expect.objectContaining({ - message: 'test', - }), - ); + expect(defaults.setMessage).toHaveBeenCalledWith('test'); }); }); @@ -81,6 +79,7 @@ describe('RegionAnnotations', () => { test('should reset the creator status', () => { instance.handleStart(); + expect(defaults.setStaged).toHaveBeenCalledWith(null); expect(defaults.setStatus).toHaveBeenCalledWith(CreatorStatus.init); }); }); @@ -98,7 +97,10 @@ describe('RegionAnnotations', () => { instance.handleStop(shape); expect(scaleShape).toHaveBeenCalledWith(shape, defaults.scale, true); // Inverse scale - expect(defaults.setStaged).toHaveBeenCalledWith(expect.objectContaining({ shape })); + expect(defaults.setStaged).toHaveBeenCalledWith({ + location: defaults.page, + shape, + }); expect(defaults.setStatus).toHaveBeenCalledWith(CreatorStatus.staged); }); }); @@ -107,7 +109,10 @@ describe('RegionAnnotations', () => { test('should save the staged annotation', () => { instance.handleSubmit(); - expect(defaults.createRegion).toHaveBeenCalledWith(getStaged()); + expect(defaults.createRegion).toHaveBeenCalledWith({ + ...getStaged(), + message: defaults.message, + }); }); }); @@ -134,7 +139,6 @@ describe('RegionAnnotations', () => { const creator = wrapper.find(RegionCreator); expect(creator.hasClass('ba-RegionAnnotations-creator')).toBe(true); - expect(creator.prop('canDraw')).toBe(true); }); test('should scale and render a RegionAnnotation if one has been drawn', () => { diff --git a/src/region/__tests__/RegionContainer-test.tsx b/src/region/__tests__/RegionContainer-test.tsx index 00dade608..333ef0d89 100644 --- a/src/region/__tests__/RegionContainer-test.tsx +++ b/src/region/__tests__/RegionContainer-test.tsx @@ -27,11 +27,13 @@ describe('RegionContainer', () => { annotations: [], createRegion: expect.any(Function), isCreating: false, + message: '', page: defaults.page, scale: defaults.scale, staged: null, status: CreatorStatus.init, setActiveAnnotationId: expect.any(Function), + setMessage: expect.any(Function), setStaged: expect.any(Function), setStatus: expect.any(Function), store: defaults.store, diff --git a/src/region/__tests__/RegionCreator-test.tsx b/src/region/__tests__/RegionCreator-test.tsx index 92334f644..a7fb10be6 100644 --- a/src/region/__tests__/RegionCreator-test.tsx +++ b/src/region/__tests__/RegionCreator-test.tsx @@ -13,7 +13,6 @@ jest.mock('../regionUtil'); describe('RegionCreator', () => { const defaults = { - canDraw: true, onDraw: jest.fn(), onStart: jest.fn(), onStop: jest.fn(), @@ -278,36 +277,22 @@ describe('RegionCreator', () => { }); }); - test('should add event listeners if canDraw is true', () => { + test('should add class and event listeners', () => { const wrapper = getWrapper(); const rootEl = getWrapperRoot(wrapper); + expect(rootEl.hasClass('ba-RegionCreator')).toBe(true); + expect(rootEl.prop('onClick')).toBeDefined(); + expect(rootEl.prop('onFocus')).toBeDefined(); expect(rootEl.prop('onMouseDown')).toBeDefined(); + expect(rootEl.prop('onMouseOut')).toBeDefined(); + expect(rootEl.prop('onMouseOver')).toBeDefined(); + expect(rootEl.prop('onTouchCancel')).toBeDefined(); expect(rootEl.prop('onTouchCancel')).toBeDefined(); expect(rootEl.prop('onTouchEnd')).toBeDefined(); expect(rootEl.prop('onTouchMove')).toBeDefined(); expect(rootEl.prop('onTouchStart')).toBeDefined(); }); - - test('should not add event listeners if canDraw is false', () => { - const wrapper = getWrapper({ canDraw: false }); - const rootEl = getWrapperRoot(wrapper); - - expect(rootEl.prop('onClick')).not.toBeDefined(); - expect(rootEl.prop('onMouseDown')).not.toBeDefined(); - expect(rootEl.prop('onTouchCancel')).not.toBeDefined(); - expect(rootEl.prop('onTouchEnd')).not.toBeDefined(); - expect(rootEl.prop('onTouchMove')).not.toBeDefined(); - expect(rootEl.prop('onTouchStart')).not.toBeDefined(); - }); - - test.each([true, false])('should add the is-active class based on canDraw: %s', canDraw => { - const wrapper = getWrapper({ canDraw }); - const rootEl = getWrapperRoot(wrapper); - - expect(rootEl.hasClass('ba-RegionCreator')).toBe(true); - expect(rootEl.hasClass('is-active')).toBe(canDraw); - }); }); }); diff --git a/src/store/creator/__mocks__/creatorState.ts b/src/store/creator/__mocks__/creatorState.ts index a7ffe0b6a..d86181101 100644 --- a/src/store/creator/__mocks__/creatorState.ts +++ b/src/store/creator/__mocks__/creatorState.ts @@ -3,9 +3,9 @@ import { CreatorStatus } from '../types'; export default { cursor: 0, error: null, + message: 'test', staged: { location: 1, - message: 'test', shape: { height: 100, width: 100, diff --git a/src/store/creator/__tests__/reducer-test.ts b/src/store/creator/__tests__/reducer-test.ts index bc500a54d..7491be5f8 100644 --- a/src/store/creator/__tests__/reducer-test.ts +++ b/src/store/creator/__tests__/reducer-test.ts @@ -2,8 +2,8 @@ import { NewAnnotation } from '../../../@types'; import { createAnnotationAction } from '../../annotations'; import reducer from '../reducer'; import state from '../__mocks__/creatorState'; -import { CreatorItem, CreatorStatus } from '../types'; -import { setStagedAction, setCursorAction, setStatusAction } from '../actions'; +import { CreatorStatus } from '../types'; +import { setMessageAction, setCursorAction, setStagedAction, setStatusAction, updateStagedAction } from '../actions'; describe('store/creator/reducer', () => { describe('createAnnotationAction', () => { @@ -32,9 +32,18 @@ describe('store/creator/reducer', () => { }); }); + describe('setMessageAction', () => { + test('should set the message in state', () => { + const payload = 'message'; + const newState = reducer(state, setMessageAction(payload)); + + expect(newState.message).toEqual(payload); + }); + }); + describe('setStagedAction', () => { test('should set the staged item in state', () => { - const payload = { location: 2 } as CreatorItem; + const payload = { ...state.staged, location: 2 }; const newState = reducer(state, setStagedAction(payload)); expect(newState.staged).toEqual(payload); @@ -56,4 +65,13 @@ describe('store/creator/reducer', () => { expect(newState.cursor).toEqual(2); }); }); + + describe('updateStagedAction', () => { + test('should update the staged item in state', () => { + const payload = { location: 2 }; + const newState = reducer(state, updateStagedAction(payload)); + + expect(newState.staged).toEqual({ ...state.staged, location: 2 }); + }); + }); }); diff --git a/src/store/creator/__tests__/selectors-test.ts b/src/store/creator/__tests__/selectors-test.ts index 9e665e8d1..5e88cfffa 100644 --- a/src/store/creator/__tests__/selectors-test.ts +++ b/src/store/creator/__tests__/selectors-test.ts @@ -1,6 +1,6 @@ import creatorState from '../__mocks__/creatorState'; import { CreatorStatus } from '../types'; -import { getCreatorStatus, getCreatorStaged, getCreatorStagedForLocation } from '../selectors'; +import { getCreatorMessage, getCreatorStaged, getCreatorStagedForLocation, getCreatorStatus } from '../selectors'; describe('store/annotations/selectors', () => { const state = { creator: creatorState }; @@ -16,7 +16,6 @@ describe('store/annotations/selectors', () => { expect(getCreatorStaged(state)).toMatchInlineSnapshot(` Object { "location": 1, - "message": "test", "shape": Object { "height": 100, "type": "rect", @@ -35,4 +34,10 @@ describe('store/annotations/selectors', () => { expect(getCreatorStagedForLocation(state, 2)).toEqual(null); }); }); + + describe('getCreatorMessage', () => { + test('should return creator message', () => { + expect(getCreatorMessage(state)).toEqual('test'); + }); + }); }); diff --git a/src/store/creator/actions.ts b/src/store/creator/actions.ts index a73395f05..72ace4ca8 100644 --- a/src/store/creator/actions.ts +++ b/src/store/creator/actions.ts @@ -1,6 +1,8 @@ import { createAction } from '@reduxjs/toolkit'; import { CreatorItem, CreatorStatus } from './types'; +export const setCursorAction = createAction('SET_CREATOR_CURSOR'); +export const setMessageAction = createAction('SET_CREATOR_MESSAGE'); export const setStagedAction = createAction('SET_CREATOR_STAGED'); export const setStatusAction = createAction('SET_CREATOR_STATUS'); -export const setCursorAction = createAction('SET_CREATOR_CURSOR'); +export const updateStagedAction = createAction>('UPDATE_CREATOR_STAGED'); diff --git a/src/store/creator/reducer.ts b/src/store/creator/reducer.ts index 076117cd6..77aacae53 100644 --- a/src/store/creator/reducer.ts +++ b/src/store/creator/reducer.ts @@ -1,11 +1,13 @@ +import merge from 'lodash/merge'; import { createReducer } from '@reduxjs/toolkit'; import { CreatorState, CreatorStatus } from './types'; import { createAnnotationAction } from '../annotations'; -import { setStagedAction, setStatusAction, setCursorAction } from './actions'; +import { setMessageAction, setCursorAction, setStagedAction, setStatusAction, updateStagedAction } from './actions'; export const initialState = { cursor: 0, error: null, + message: '', staged: null, status: CreatorStatus.init, }; @@ -25,6 +27,9 @@ export default createReducer(initialState, builder => state.error = error; state.status = CreatorStatus.rejected; }) + .addCase(setMessageAction, (state, { payload }) => { + state.message = payload; + }) .addCase(setStagedAction, (state, { payload }) => { state.staged = payload; }) @@ -33,5 +38,8 @@ export default createReducer(initialState, builder => }) .addCase(setCursorAction, (state, { payload }) => { state.cursor = payload; + }) + .addCase(updateStagedAction, (state, { payload }) => { + state.staged = merge(state.staged, payload); }), ); diff --git a/src/store/creator/selectors.ts b/src/store/creator/selectors.ts index 89522c8b0..fbd829203 100644 --- a/src/store/creator/selectors.ts +++ b/src/store/creator/selectors.ts @@ -10,3 +10,4 @@ export const getCreatorStagedForLocation = (state: State, location: number): Cre }; export const getCreatorStatus = (state: State): CreatorStatus => state.creator.status; export const getCreatorCursor = (state: State): number => state.creator.cursor; +export const getCreatorMessage = (state: State): string => state.creator.message; diff --git a/src/store/creator/types.ts b/src/store/creator/types.ts index 275094907..6a0391f93 100644 --- a/src/store/creator/types.ts +++ b/src/store/creator/types.ts @@ -9,13 +9,13 @@ export enum CreatorStatus { export type CreatorItem = { location: number; - message: string; shape: Rect; }; export type CreatorState = { cursor: number; error: SerializedError | null; + message: string; staged: CreatorItem | null; status: CreatorStatus; };