Skip to content

Commit

Permalink
added undo and redo
Browse files Browse the repository at this point in the history
  • Loading branch information
ragsav committed Apr 30, 2023
1 parent a74abf7 commit b13b3a6
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 9 deletions.
114 changes: 114 additions & 0 deletions js/hooks/useUndo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import {useReducer, useCallback} from 'react';

const ActionType = {
Undo: 'UNDO',
Redo: 'REDO',
Set: 'SET',
Reset: 'RESET',
};

const initialState = {
past: [],
present: null,
future: [],
};

export const useUndo = (initialPresent, opts = {}) => {
const {useCheckpoints} = {
useCheckpoints: false,
...opts,
};

const reducer = (state, action) => {
const {past, present, future} = state;

switch (action.type) {
case ActionType.Undo: {
if (past.length === 0) {
return state;
}

const previous = past[past.length - 1];
const newPast = past.slice(0, past.length - 1);

return {
past: newPast,
present: previous,
future: [present, ...future],
};
}

case ActionType.Redo: {
if (future.length === 0) {
return state;
}
const next = future[0];
const newFuture = future.slice(1);

return {
past: [...past, present],
present: next,
future: newFuture,
};
}

case ActionType.Set: {
const isNewCheckpoint = useCheckpoints
? !!action.historyCheckpoint
: true;
const {newPresent} = action;

if (newPresent === present) {
return state;
}

return {
past: isNewCheckpoint === false ? past : [...past, present],
present: newPresent,
future: [],
};
}

case ActionType.Reset: {
const {newPresent} = action;

return {
past: [],
present: newPresent,
future: [],
};
}
}
};

const [state, dispatch] = useReducer(reducer, {
...initialState,
present: initialPresent,
});

const canUndo = state.past.length !== 0;
const canRedo = state.future.length !== 0;
const undo = useCallback(() => {
if (canUndo) {
dispatch({type: ActionType.Undo});
}
}, [canUndo]);
const redo = useCallback(() => {
if (canRedo) {
dispatch({type: ActionType.Redo});
}
}, [canRedo]);
const set = useCallback((newPresent, checkpoint = false) => {
dispatch({
type: ActionType.Set,
newPresent,
historyCheckpoint: checkpoint,
});
}, []);
const reset = useCallback(
newPresent => dispatch({type: ActionType.Reset, newPresent}),
[],
);

return [state, {set, reset, undo, redo, canUndo, canRedo}];
};
56 changes: 47 additions & 9 deletions js/screens/TaskScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
import {CONSTANTS} from '../../constants';
import {NotePasswordInputScreen} from './NotePasswordInputScreen';
import {Q} from '@nozbe/watermelondb';
import {useUndo} from '../hooks/useUndo';

/**
*
Expand All @@ -72,6 +73,8 @@ const TaskScreen = ({
route,
}) => {
// ref
const titleRef = useRef();
const descriptionRef = useRef();

// variables
const theme = useTheme();
Expand All @@ -85,21 +88,29 @@ const TaskScreen = ({
error: null,
});
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const titleRef = useRef();
const descriptionRef = useRef();

const [
descriptionUndoRedoState,
{
set: setDescription,
reset: resetDescription,
undo: undoDescription,
redo: redoDescription,
canUndo,
canRedo,
},
] = useUndo('');
const {present: presentDescription} = descriptionUndoRedoState;
const [error, setError] = useState(null);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [dueDateString, setDueDateString] = useState('date');
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isDueDateTimePickerVisible, setIsDueDateTimePickerVisible] =
useState(false);

const [reminderDateString, setReminderDateString] = useState('time');
const [isReminderDateTimePickerVisible, setIsReminderDateTimePickerVisible] =
useState(false);
const [urls, setURLs] = useState([]);

const [isImagePickerOpen, setIsImagePickerOpen] = useState(false);
const [imageToView, setImageToView] = useState({
images: [],
Expand Down Expand Up @@ -166,9 +177,9 @@ const TaskScreen = ({

useEffect(() => {
if (descriptionRef) {
descriptionRef.current = description;
descriptionRef.current = presentDescription;
}
}, [description, descriptionRef, descriptionRef.current]);
}, [presentDescription, descriptionRef, descriptionRef.current]);

useEffect(() => {
if (titleRef) {
Expand Down Expand Up @@ -339,7 +350,7 @@ const TaskScreen = ({
try {
const result = await Share.share({
title: title,
message: description,
message: presentDescription,
});
if (result.action === Share.sharedAction) {
if (result.activityType) {
Expand Down Expand Up @@ -595,12 +606,13 @@ const TaskScreen = ({
textAlignVertical: 'top',
padding: 12,
paddingHorizontal: 18,
paddingBottom: 30,
}}
onBlur={_handleDescriptionSave}
onChangeText={_handleDescriptionChange}
placeholder="Add some here..."
placeholderTextColor={theme?.colors.onSurfaceDisabled}
value={description}
value={presentDescription}
multiline={true}
/>

Expand All @@ -623,6 +635,32 @@ const TaskScreen = ({
presentationStyle="fullScreen"
onRequestClose={() => setImageToView({...imageToView, visible: false})}
/>
<Appbar.Header
style={{
backgroundColor: theme?.colors.surface,
}}>
<Appbar.Content />
<Appbar.Action
isLeading={false}
icon={'undo-variant'}
disabled={!canUndo}
// iconColor={theme?.colors.onPrimary}
onPress={undoDescription}
/>
<Appbar.Action
isLeading={false}
icon={'redo-variant'}
disabled={!canRedo}
// iconColor={theme?.colors.onPrimary}
onPress={redoDescription}
/>
<Appbar.Action
isLeading={false}
icon={'restore'}
// iconColor={theme?.colors.onPrimary}
onPress={resetDescription}
/>
</Appbar.Header>
</SafeAreaView>
) : (
<NotePasswordInputScreen
Expand Down

0 comments on commit b13b3a6

Please sign in to comment.