feat: feature/mvp-sprint-1 Added undo/redo
This commit is contained in:
parent
7eecc21173
commit
efcda254eb
@ -114,23 +114,137 @@ initialThingtime.thingtime = initialThingtime
|
|||||||
initialThingtime.tt = initialThingtime
|
initialThingtime.tt = initialThingtime
|
||||||
|
|
||||||
export const ThingtimeProvider = (props: any): JSX.Element => {
|
export const ThingtimeProvider = (props: any): JSX.Element => {
|
||||||
const [thingtime, rawSet] = React.useState()
|
const [thingtimeReference, rawSet] = React.useState()
|
||||||
|
|
||||||
const thingtimeRef = React.useRef(thingtime)
|
const thingtimeRef = React.useRef(thingtimeReference)
|
||||||
const stateRef = React.useRef({
|
const stateRef = React.useRef({
|
||||||
c: 1,
|
c: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [loading, setLoading] = React.useState(true)
|
const [loading, setLoading] = React.useState(true)
|
||||||
|
|
||||||
const set = React.useCallback((newThingtime) => {
|
const set = React.useCallback((newThingtime, ignoreUndoRedo?: any) => {
|
||||||
const thingtimeReference = {
|
const newThingtimeReference = {
|
||||||
...newThingtime,
|
...newThingtime,
|
||||||
}
|
}
|
||||||
|
|
||||||
thingtimeReference.tt = thingtimeReference
|
newThingtimeReference.tt = newThingtimeReference
|
||||||
thingtimeReference.thingtime = thingtimeReference
|
newThingtimeReference.thingtime = newThingtimeReference
|
||||||
rawSet(thingtimeReference)
|
|
||||||
|
// store undo/redo history
|
||||||
|
if (!ignoreUndoRedo) {
|
||||||
|
try {
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider setting thingtime to localStorage",
|
||||||
|
newThingtimeReference
|
||||||
|
)
|
||||||
|
// setTimeout(() => {
|
||||||
|
const stringified = stringify(newThingtimeReference)
|
||||||
|
let undoHistory = []
|
||||||
|
try {
|
||||||
|
const undoHistoryString = window.localStorage.getItem("undoHistory")
|
||||||
|
const parsedUndoHistory = JSON.parse(undoHistoryString)
|
||||||
|
if (parsedUndoHistory instanceof Array) {
|
||||||
|
undoHistory = parsedUndoHistory
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
// if last undoHistory does not equal new undo history
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider saving to undo history undoHistory[undoHistory.length - 1]?.value",
|
||||||
|
undoHistory[undoHistory.length - 1]?.value
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider saving to undo history stringified",
|
||||||
|
stringified
|
||||||
|
)
|
||||||
|
if (undoHistory[undoHistory.length - 1]?.value !== stringified) {
|
||||||
|
try {
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider saving to undo history undoHistory",
|
||||||
|
undoHistory
|
||||||
|
)
|
||||||
|
const limit = newThingtimeReference?.settings?.undoLimit || 999
|
||||||
|
|
||||||
|
if (undoHistory?.length > limit) {
|
||||||
|
undoHistory = undoHistory.slice(undoHistory.length - limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
undoHistory.push({
|
||||||
|
timestamp: Date.now(),
|
||||||
|
value: stringify(newThingtimeReference),
|
||||||
|
})
|
||||||
|
const undoHistoryNewString = JSON.stringify(undoHistory)
|
||||||
|
window.localStorage.setItem("undoHistory", undoHistoryNewString)
|
||||||
|
} catch {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// window.localStorage.setItem("thingtime", stringified)
|
||||||
|
// }, 600)
|
||||||
|
} catch (err) {
|
||||||
|
console.error("There was an error saving thingtime to localStorage")
|
||||||
|
}
|
||||||
|
const saveRedo = false
|
||||||
|
if (saveRedo) {
|
||||||
|
try {
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider setting thingtime to localStorage",
|
||||||
|
newThingtimeReference
|
||||||
|
)
|
||||||
|
// setTimeout(() => {
|
||||||
|
const stringified = stringify(newThingtimeReference)
|
||||||
|
let redoHistory = []
|
||||||
|
try {
|
||||||
|
const redoHistoryString = window.localStorage.getItem("redoHistory")
|
||||||
|
const parsedRedoHistory = JSON.parse(redoHistoryString)
|
||||||
|
if (parsedRedoHistory instanceof Array) {
|
||||||
|
redoHistory = parsedRedoHistory
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
// if last redoHistory does not equal new redo history
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider saving to redo history redoHistory[redoHistory.length - 1]?.value",
|
||||||
|
redoHistory[redoHistory.length - 1]?.value
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider saving to redo history stringified",
|
||||||
|
stringified
|
||||||
|
)
|
||||||
|
if (redoHistory[redoHistory.length - 1]?.value !== stringified) {
|
||||||
|
try {
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider saving to redo history redoHistory",
|
||||||
|
redoHistory
|
||||||
|
)
|
||||||
|
const limit = newThingtimeReference?.settings?.redoLimit || 999
|
||||||
|
|
||||||
|
if (redoHistory?.length > limit) {
|
||||||
|
redoHistory = redoHistory.slice(redoHistory.length - limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
redoHistory.push({
|
||||||
|
timestamp: Date.now(),
|
||||||
|
value: stringify(newThingtimeReference),
|
||||||
|
})
|
||||||
|
const redoHistoryNewString = JSON.stringify(redoHistory)
|
||||||
|
window.localStorage.setItem("redoHistory", redoHistoryNewString)
|
||||||
|
} catch {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// window.localStorage.setItem("thingtime", stringified)
|
||||||
|
// }, 600)
|
||||||
|
} catch (err) {
|
||||||
|
console.error("There was an error saving thingtime to localStorage")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rawSet(newThingtimeReference)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const setThingtime = React.useCallback(
|
const setThingtime = React.useCallback(
|
||||||
@ -143,7 +257,7 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const newThingtime = thingtime
|
const newThingtime = thingtimeReference
|
||||||
|
|
||||||
const paths = smarts.parsePropertyPath(path)
|
const paths = smarts.parsePropertyPath(path)
|
||||||
|
|
||||||
@ -193,11 +307,9 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
|||||||
|
|
||||||
smarts.setsmart(newThingtime, path, value)
|
smarts.setsmart(newThingtime, path, value)
|
||||||
|
|
||||||
console.log("nik set(newThingtime)", newThingtime)
|
|
||||||
|
|
||||||
set(newThingtime)
|
set(newThingtime)
|
||||||
},
|
},
|
||||||
[thingtime, set]
|
[thingtimeReference, set]
|
||||||
)
|
)
|
||||||
|
|
||||||
const getThingtime = React.useCallback(
|
const getThingtime = React.useCallback(
|
||||||
@ -206,16 +318,16 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
|||||||
const path = rawPath
|
const path = rawPath
|
||||||
|
|
||||||
if (!path) {
|
if (!path) {
|
||||||
return thingtime
|
return thingtimeReference
|
||||||
}
|
}
|
||||||
|
|
||||||
// do we need to sanitise?
|
// do we need to sanitise?
|
||||||
// const path = sanitise(rawPath)
|
// const path = sanitise(rawPath)
|
||||||
console.log("ThingtimeProvider getting thingtime at path", path)
|
console.log("ThingtimeProvider getting thingtime at path", path)
|
||||||
// console.trace("Getting thingtime at path", path)
|
// console.trace("Getting thingtime at path", path)
|
||||||
return smarts.getsmart(thingtime, path)
|
return smarts.getsmart(thingtimeReference, path)
|
||||||
},
|
},
|
||||||
[thingtime]
|
[thingtimeReference]
|
||||||
)
|
)
|
||||||
|
|
||||||
const populatePaths = React.useCallback((obj, path, paths, seen = []) => {
|
const populatePaths = React.useCallback((obj, path, paths, seen = []) => {
|
||||||
@ -243,10 +355,10 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
|||||||
const paths = []
|
const paths = []
|
||||||
|
|
||||||
// populatePaths(thingtime, commandPath)
|
// populatePaths(thingtime, commandPath)
|
||||||
populatePaths(thingtime, "", paths)
|
populatePaths(thingtimeReference, "", paths)
|
||||||
|
|
||||||
return paths
|
return paths
|
||||||
}, [populatePaths, thingtime])
|
}, [populatePaths, thingtimeReference])
|
||||||
|
|
||||||
// get thingtime from localstorage
|
// get thingtime from localstorage
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -288,86 +400,173 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
try {
|
try {
|
||||||
window.setThingtime = setThingtime
|
window.setThingtime = setThingtime
|
||||||
window.thingtime = thingtime
|
window.thingtime = thingtimeReference
|
||||||
window.tt = thingtime
|
window.tt = thingtimeReference
|
||||||
} catch {
|
} catch {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stateRef.current.initialized) {
|
if (stateRef.current.initialized) {
|
||||||
if (thingtime.thingtime !== thingtime || thingtime.tt !== thingtime) {
|
try {
|
||||||
if (!(stateRef?.current?.c >= 10)) {
|
console.log(
|
||||||
stateRef.current.c++
|
"ThingtimeProvider setting thingtime to localStorage",
|
||||||
const newThingtime = {
|
thingtimeReference
|
||||||
...thingtime,
|
)
|
||||||
}
|
// setTimeout(() => {
|
||||||
newThingtime.thingtime = newThingtime
|
const stringified = stringify(thingtimeReference)
|
||||||
newThingtime.tt = newThingtime
|
window.localStorage.setItem("thingtime", stringified)
|
||||||
set(newThingtime)
|
// }, 600)
|
||||||
}
|
} catch (err) {
|
||||||
} else {
|
console.error("There was an error saving thingtime to localStorage")
|
||||||
try {
|
|
||||||
console.log(
|
|
||||||
"ThingtimeProvider setting thingtime to localStorage",
|
|
||||||
thingtime
|
|
||||||
)
|
|
||||||
// setTimeout(() => {
|
|
||||||
const stringified = stringify(thingtime)
|
|
||||||
let thingtimeHistory = []
|
|
||||||
try {
|
|
||||||
const thingtimeHistoryString =
|
|
||||||
window.localStorage.getItem("thingtimeHistory")
|
|
||||||
const parsedThingtimeHistory = JSON.parse(thingtimeHistoryString)
|
|
||||||
if (parsedThingtimeHistory instanceof Array) {
|
|
||||||
thingtimeHistory = parsedThingtimeHistory
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const limit = thingtime?.settings?.undoLimit || 999
|
|
||||||
|
|
||||||
if (thingtimeHistory?.length > limit) {
|
|
||||||
thingtimeHistory = thingtimeHistory.slice(
|
|
||||||
thingtimeHistory.length - limit
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
thingtimeHistory.push({
|
|
||||||
timestamp: Date.now(),
|
|
||||||
value: stringify(thingtime),
|
|
||||||
})
|
|
||||||
const thingtimeHistoryNewString = JSON.stringify(thingtimeHistory)
|
|
||||||
window.localStorage.setItem(
|
|
||||||
"thingtimeHistory",
|
|
||||||
thingtimeHistoryNewString
|
|
||||||
)
|
|
||||||
} catch {
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
window.localStorage.setItem("thingtime", stringified)
|
|
||||||
// }, 600)
|
|
||||||
} catch (err) {
|
|
||||||
console.error("There was an error saving thingtime to localStorage")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stateRef.current.initialized = true
|
stateRef.current.initialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
thingtimeRef.current = thingtime
|
thingtimeRef.current = thingtimeReference
|
||||||
|
|
||||||
const keyListener = (e) => {}
|
const keyListener = (e) => {
|
||||||
|
// if ctrl + z, restore thingtime from localstorage history
|
||||||
|
|
||||||
|
console.log("ThingtimeProvider listened to key event e?.key", e?.key)
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider listened to key event e?.ctrlKey",
|
||||||
|
e?.ctrlKey
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider listened to key event e?.shiftKey",
|
||||||
|
e?.shiftKey
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
"ThingtimeProvider listened to key event e?.metaKey",
|
||||||
|
e?.metaKey
|
||||||
|
)
|
||||||
|
|
||||||
|
if ((e?.ctrlKey || e?.metaKey) && e?.key === "z") {
|
||||||
|
e?.preventDefault()
|
||||||
|
|
||||||
|
console.log("ThingtimeProvider detected undo/redo request")
|
||||||
|
|
||||||
|
if (e.shiftKey) {
|
||||||
|
// redo
|
||||||
|
console.log("ThingtimeProvider redo")
|
||||||
|
const redoHistoryString = window.localStorage.getItem("redoHistory")
|
||||||
|
const parsedRedoHistory = JSON.parse(redoHistoryString)
|
||||||
|
if (parsedRedoHistory instanceof Array) {
|
||||||
|
const last = parsedRedoHistory[parsedRedoHistory.length - 1]
|
||||||
|
if (last) {
|
||||||
|
const parsed = parse(last.value)
|
||||||
|
if (parsed) {
|
||||||
|
// remove restored state from history
|
||||||
|
// const currentHistory = parsedRedoHistory.pop()
|
||||||
|
parsedRedoHistory.pop()
|
||||||
|
// parsedRedoHistory.push(currentHistory)
|
||||||
|
const newRedoHistoryString = JSON.stringify(parsedRedoHistory)
|
||||||
|
window.localStorage.setItem("redoHistory", newRedoHistoryString)
|
||||||
|
|
||||||
|
// save old/current state to undo history
|
||||||
|
let undoHistory = []
|
||||||
|
try {
|
||||||
|
const undoHistoryString =
|
||||||
|
window.localStorage.getItem("undoHistory")
|
||||||
|
const parsedUndoHistory = JSON.parse(undoHistoryString)
|
||||||
|
if (parsedUndoHistory instanceof Array) {
|
||||||
|
undoHistory = parsedUndoHistory
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
undoHistory.push({
|
||||||
|
timestamp: Date.now(),
|
||||||
|
value: stringify(thingtimeReference),
|
||||||
|
})
|
||||||
|
const undoHistoryNewString = JSON.stringify(undoHistory)
|
||||||
|
window.localStorage.setItem(
|
||||||
|
"undoHistory",
|
||||||
|
undoHistoryNewString
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
const newThingtime = parsed
|
||||||
|
set(newThingtime, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// undo
|
||||||
|
console.log("ThingtimeProvider undo")
|
||||||
|
try {
|
||||||
|
const undoHistoryString = window.localStorage.getItem("undoHistory")
|
||||||
|
const parsedUndoHistory = JSON.parse(undoHistoryString)
|
||||||
|
if (parsedUndoHistory instanceof Array) {
|
||||||
|
const last = parsedUndoHistory[parsedUndoHistory.length - 2]
|
||||||
|
if (last) {
|
||||||
|
const parsed = parse(last.value)
|
||||||
|
if (parsed) {
|
||||||
|
// remove restored state from history
|
||||||
|
|
||||||
|
const currentHistory = parsedUndoHistory.pop()
|
||||||
|
parsedUndoHistory.pop()
|
||||||
|
parsedUndoHistory.push(currentHistory)
|
||||||
|
|
||||||
|
const newUndoHistoryString = JSON.stringify(parsedUndoHistory)
|
||||||
|
window.localStorage.setItem(
|
||||||
|
"undoHistory",
|
||||||
|
newUndoHistoryString
|
||||||
|
)
|
||||||
|
|
||||||
|
// save old/current state to redo history
|
||||||
|
let redoHistory = []
|
||||||
|
try {
|
||||||
|
const redoHistoryString =
|
||||||
|
window.localStorage.getItem("redoHistory")
|
||||||
|
const parsedRedoHistory = JSON.parse(redoHistoryString)
|
||||||
|
if (parsedRedoHistory instanceof Array) {
|
||||||
|
redoHistory = parsedRedoHistory
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const newValue = stringify(thingtimeReference)
|
||||||
|
// if last history is not the same as new history
|
||||||
|
redoHistory.push({
|
||||||
|
timestamp: Date.now(),
|
||||||
|
value: newValue,
|
||||||
|
})
|
||||||
|
const redoHistoryNewString = JSON.stringify(redoHistory)
|
||||||
|
window.localStorage.setItem(
|
||||||
|
"redoHistory",
|
||||||
|
redoHistoryNewString
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
const newThingtime = parsed
|
||||||
|
set(newThingtime, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener("keydown", keyListener)
|
window.addEventListener("keydown", keyListener)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("keydown", keyListener)
|
window.removeEventListener("keydown", keyListener)
|
||||||
}
|
}
|
||||||
}, [setThingtime, thingtime, set])
|
}, [setThingtime, thingtimeReference, set])
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
thingtime,
|
thingtime: thingtimeReference,
|
||||||
setThingtime,
|
setThingtime,
|
||||||
getThingtime,
|
getThingtime,
|
||||||
thingtimeRef,
|
thingtimeRef,
|
||||||
|
@ -84,7 +84,7 @@ export const Nav = (props) => {
|
|||||||
<CommanderV1 global id="nav" rainbow></CommanderV1>
|
<CommanderV1 global id="nav" rainbow></CommanderV1>
|
||||||
<Center
|
<Center
|
||||||
className="nav-right-section"
|
className="nav-right-section"
|
||||||
columnGap={[3, 5]}
|
columnGap={[3, 8]}
|
||||||
height="100%"
|
height="100%"
|
||||||
marginLeft="auto"
|
marginLeft="auto"
|
||||||
>
|
>
|
||||||
|
@ -243,9 +243,9 @@ export const Thingtime = (props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (type === "undefined") {
|
} else if (type === "undefined") {
|
||||||
return "undefined"
|
return "Imagine.."
|
||||||
} else {
|
} else {
|
||||||
return "Something!"
|
return "Something.."
|
||||||
}
|
}
|
||||||
}, [thing, thingDep, type, keys])
|
}, [thing, thingDep, type, keys])
|
||||||
|
|
||||||
@ -376,9 +376,6 @@ export const Thingtime = (props) => {
|
|||||||
|
|
||||||
delete clone[path]
|
delete clone[path]
|
||||||
|
|
||||||
console.log("nik parentPath", parentPath)
|
|
||||||
console.log("nik clone", clone)
|
|
||||||
|
|
||||||
setThingtime(parentPath, clone)
|
setThingtime(parentPath, clone)
|
||||||
}, [path, parent, parentPath, setThingtime])
|
}, [path, parent, parentPath, setThingtime])
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user