Compare commits

..

No commits in common. "40f7e25d8f1b5702f0d6eb09a8996440f5e1771e" and "d22dbd7629102e907ebf7e43f40d24b98cb92969" have entirely different histories.

6 changed files with 11 additions and 71 deletions

BIN
.nova/Artwork Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

5
.nova/Configuration.json Normal file
View File

@ -0,0 +1,5 @@
{
"workspace.art_style" : 1,
"workspace.color" : 10,
"workspace.name" : "Treadl"
}

View File

@ -10,8 +10,6 @@ export default {
UPDATE_EDITOR: 'UPDATE_EDITOR', UPDATE_EDITOR: 'UPDATE_EDITOR',
RECEIVE_SNIPPET: 'RECEIVE_SNIPPET', RECEIVE_SNIPPET: 'RECEIVE_SNIPPET',
DELETE_SNIPPET: 'DELETE_SNIPPET', DELETE_SNIPPET: 'DELETE_SNIPPET',
RECEIVE_SNAPSHOT: 'RECEIVE_SNAPSHOT',
TRAVERSE_SNAPSHOTS: 'TRAVERSE_SNAPSHOTS',
RECEIVE_COMMENT: 'RECEIVE_COMMENT', RECEIVE_COMMENT: 'RECEIVE_COMMENT',
DELETE_COMMENT: 'DELETE_COMMENT', DELETE_COMMENT: 'DELETE_COMMENT',
@ -57,14 +55,6 @@ export default {
return { type: this.DELETE_SNIPPET, snippetId }; return { type: this.DELETE_SNIPPET, snippetId };
}, },
receiveSnapshot(snapshot) {
return { type: this.RECEIVE_SNAPSHOT, snapshot };
},
traverseSnapshots(direction) {
return { type: this.TRAVERSE_SNAPSHOTS, direction };
},
receiveComment(comment) { receiveComment(comment) {
return { type: this.RECEIVE_COMMENT, comment }; return { type: this.RECEIVE_COMMENT, comment };
}, },

View File

@ -5,7 +5,6 @@ import { useSelector, useDispatch } from 'react-redux';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useBlocker } from 'react-router'; import { useBlocker } from 'react-router';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { useDebouncedCallback } from 'use-debounce';
import styled from 'styled-components'; import styled from 'styled-components';
import Tour from '../../../includes/Tour'; import Tour from '../../../includes/Tour';
import ElementPan from '../../../includes/ElementPan'; import ElementPan from '../../../includes/ElementPan';
@ -44,8 +43,6 @@ function Draft() {
dispatch(actions.objects.receive(o)); dispatch(actions.objects.receive(o));
setObject(o); setObject(o);
setPattern(o.pattern); setPattern(o.pattern);
// Create a snapshot on first load (so undo has somewhere to revert to)
setTimeout(() => createSnapshot(o.pattern), 1000);
}); });
}, [objectId]); }, [objectId]);
@ -54,29 +51,15 @@ function Draft() {
currentLocation.pathname !== nextLocation.pathname currentLocation.pathname !== nextLocation.pathname
); );
const createSnapshot = (snapshotPattern) => {
const deepCopy = JSON.parse(JSON.stringify(snapshotPattern));
const snapshot = {
objectId: objectId,
createdAt: new Date(),
warp: deepCopy.warp,
weft: deepCopy.weft,
tieups: deepCopy.tieups,
};
dispatch(actions.objects.receiveSnapshot(snapshot));
};
const debouncedSnapshot = useDebouncedCallback(createSnapshot, 1000);
const updateObject = (update) => { const updateObject = (update) => {
setObject(Object.assign({}, object, update)); setObject(Object.assign({}, object, update));
setUnsaved(true); setUnsaved(true);
}; };
const updatePattern = (update, withoutSnapshot = false) => { const updatePattern = (update) => {
const newPattern = Object.assign({}, pattern, update); const newPattern = Object.assign({}, pattern, update);
setPattern(newPattern); setPattern(Object.assign({}, pattern, newPattern));
setUnsaved(true); setUnsaved(true);
if (!withoutSnapshot) debouncedSnapshot(newPattern);
}; };
const saveObject = () => { const saveObject = () => {

View File

@ -63,15 +63,13 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
const { objectId, username, projectPath } = useParams(); const { objectId, username, projectPath } = useParams();
const { colours } = pattern; const { colours } = pattern;
const { user, project, editor, snapshots, currentSnapshotIndex } = useSelector(state => { const { user, project, editor } = useSelector(state => {
const user = state.users.users.filter(u => state.auth.currentUserId === u._id)[0]; const user = state.users.users.filter(u => state.auth.currentUserId === u._id)[0];
let project = {}; let project = {};
state.projects.projects.forEach((p) => { state.projects.projects.forEach((p) => {
if (p.path === projectPath && p.owner && p.owner.username === username) project = p; if (p.path === projectPath && p.owner && p.owner.username === username) project = p;
}); });
const snapshots = state.objects.snapshots.filter(s => s.objectId === objectId); return { user, project, editor: state.objects.editor };
const currentSnapshotIndex = state.objects.currentSnapshotIndex;
return { user, project, editor: state.objects.editor, snapshots, currentSnapshotIndex };
}); });
useEffect(() => { useEffect(() => {
@ -87,22 +85,6 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
if (colours?.length && !editor.colour) setColour(colours[colours.length - 1]); if (colours?.length && !editor.colour) setColour(colours[colours.length - 1]);
}, [colours]); }, [colours]);
const applyNextHistory = direction => {
const snapshot = snapshots[currentSnapshotIndex + direction];
if (!snapshot) return;
const newWarp = Object.assign({}, snapshot.warp);
const newWeft = Object.assign({}, snapshot.weft);
const newTieups = Object.assign([], snapshot.tieups);
updatePattern({ warp: newWarp, weft: newWeft, tieups: newTieups }, true);
dispatch(actions.objects.traverseSnapshots(direction));
};
const undo = () => {
applyNextHistory(1);
};
const redo = () => {
applyNextHistory(-1);
};
const enableTool = (tool) => { const enableTool = (tool) => {
dispatch(actions.objects.updateEditor({ tool, colour: editor.colour })); dispatch(actions.objects.updateEditor({ tool, colour: editor.colour }));
}; };
@ -192,11 +174,11 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
toast('🗑️ Pattern deleted'); toast('🗑️ Pattern deleted');
dispatch(actions.objects.delete(objectId)); dispatch(actions.objects.delete(objectId));
navigate(`/${project.fullName}`); navigate(`/${project.fullName}`);
}); }, err => console.log(err));
} }
const revertChanges = () => { const revertChanges = () => {
const sure = window.confirm('Really revert your changes to your last save point?\n\nAny updates to your pattern since you last saved, along with your "undo" history, will be lost.') const sure = window.confirm('Really revert your changes to your last save point?\n\nAny updates to your pattern since you last saved will be lost.')
if (sure) { if (sure) {
window.location.reload(); window.location.reload();
} }
@ -265,12 +247,6 @@ function Tools({ object, pattern, warp, weft, unsaved, saving, baseSize, updateP
</div> </div>
: :
<div style={{display: 'flex', alignItems: 'end'}}> <div style={{display: 'flex', alignItems: 'end'}}>
<div style={{marginRight: 10}}>
<Button.Group size="tiny">
<Button disabled={currentSnapshotIndex >= (snapshots?.length - 1)} data-tooltip="Undo" size="tiny" icon onClick={undo}><Icon name="undo" /></Button>
<Button disabled={!currentSnapshotIndex} data-tooltip="Redo" size="tiny" icon onClick={redo}><Icon name="redo" /></Button>
</Button.Group>
</div>
<div> <div>
<ToolLabel>View</ToolLabel> <ToolLabel>View</ToolLabel>
<Popup hoverable on='click' <Popup hoverable on='click'

View File

@ -9,8 +9,6 @@ const initialState = {
selected: null, selected: null,
editor: { tool: 'straight', colour: null, view: 'interlacement', autoExtend: true }, editor: { tool: 'straight', colour: null, view: 'interlacement', autoExtend: true },
snippets: [], snippets: [],
snapshots: [],
currentSnapshotIndex: 0,
}; };
function objects(state = initialState, action) { function objects(state = initialState, action) {
@ -96,18 +94,6 @@ function objects(state = initialState, action) {
return Object.assign({}, state, { snippets: state.snippets.filter(e => e._id !== action.snippetId) }); return Object.assign({}, state, { snippets: state.snippets.filter(e => e._id !== action.snippetId) });
} }
case actions.objects.RECEIVE_SNAPSHOT: {
const snapshots = Object.assign([], state.snapshots);
snapshots.splice(0, 0, action.snapshot);
if (snapshots.length > 10) snapshots.pop(); // Only keep the latest 10 snapshots
return Object.assign({}, state, { snapshots, currentSnapshotIndex: 0});
}
case actions.objects.TRAVERSE_SNAPSHOTS: {
const currentSnapshotIndex = state.currentSnapshotIndex + action.direction;
if (currentSnapshotIndex < 0 || currentSnapshotIndex >= state.snapshots.length) return state;
return Object.assign({}, state, { currentSnapshotIndex });
}
case actions.objects.RECEIVE_COMMENT: { case actions.objects.RECEIVE_COMMENT: {
let found = false; let found = false;
const comments = state.comments.map(e => { const comments = state.comments.map(e => {