diff --git a/src/App.js b/src/App.js index 5ef3f10..e129a67 100644 --- a/src/App.js +++ b/src/App.js @@ -3,19 +3,22 @@ import { addTask, addAll, completeBenchmarkTask, benchmarkItem, emptyList, isAct import { startReview, handleReviewDecision, isPrioritizableList, genQuestion, getInitialCursor } from './core/reviewManager'; import { getFromLocalStorage, saveToLocalStorage } from './core/localStorageAdapter'; import { exportTasksToJSON, importTasksFromJSON } from './core/tasksIO'; +import { objectArraysAreEqual } from './core/logicUtils'; import TodoItem from './TodoItem'; -import {saveDisk, infoCircle} from './core/icons' +import {saveDisk, infoCircle, lightbulbSolid, lightbulbRegular} from './core/icons' import './App.css'; // TODO: refactor all buttons to change color on hover, focus, active rather than grow +// TODO: implement crossing out of items to mark them as 'won't do' where they retain their mark/symbol but have a status of 'cancelled' (i.e. "won't do") +// TODO: implement a 'clone' button that renders only for completed or cancelled items, which clones the item and adds it to the end of the list const activeListOffset = 0; const queryStringListOffset = 100; const initialTasksListOffset = 200; const appName = "AutoFocus"; -const infoString1 = "AutoFocus was designed by Mark Forster. This web app was built by Avi Drucker."; -const infoString2 = "The AutoFocus algorithm was designed as a pen and paper method to help increase productivity. It does so by limiting list interaction to a minimum, and by providing a simple (binary) decision-making framework."; +const infoString2 = "AutoFocus was designed by Mark Forster. This web app was built by Avi Drucker using ReactJS, Font Awesome, and Tachyons."; +const infoString1 = "The AutoFocus algorithm was designed as a pen and paper method to help increase productivity. It does so by limiting list interaction to a minimum, and by providing a simple (binary) decision-making framework."; const saveInfo1 = "You can import and export JSON lists into and out of AutoFocus."; const saveInfo2 = "You can also import a list by pasting in raw text below, and then clicking the 'Submit' button."; const emptyInputErrMsg1 = "New items cannot be empty or whitespace only, please type some text into the text input above and then tap 'Add Task'."; @@ -25,42 +28,6 @@ const badJSONimportErrMsg1 = "Failed to import tasks. Ensure the JSON file has t const nonJSONimportAttemptedErrMsg1 = "Please select a valid JSON file."; const mismatchDetectedMsg1 = "There is a mismatch between the list loaded from the link address and what is saved locally. Which list would you like to continue using?"; -// TODO: extract out following logic into separate module as per hexagonal architecture -function objectsAreEqual(obj1, obj2) { - const keys1 = Object.keys(obj1); - const keys2 = Object.keys(obj2); - - if (keys1.length !== keys2.length) { - return false; // If the objects have different numbers of properties, they are not equal - } - - for (const key of keys1) { - if (obj1[key] !== obj2[key]) { - return false; // If any property values are different, the objects are not equal - } - } - - return true; // If no differences were found, the objects are equal -} - -// TODO: extract out following logic into separate module as per hexagonal architecture -function arraysAreEqual(arr1, arr2) { - if (arr1.length !== arr2.length) { - return false; // If the lengths are different, the arrays are not equal - } - - // Sort the arrays by a unique property to ensure the order doesn't affect the comparison - const sortedArr1 = arr1.slice().sort((a, b) => a.id - b.id); - const sortedArr2 = arr2.slice().sort((a, b) => a.id - b.id); - - for (let i = 0; i < sortedArr1.length; i++) { - if (!objectsAreEqual(sortedArr1[i], sortedArr2[i])) { - return false; // If any objects are not equal, the arrays are not equal - } - } - - return true; // If no differences were found, the arrays are equal -} function App() { const initialTasks = getFromLocalStorage('tasks', []); @@ -78,6 +45,10 @@ function App() { const inputRef = useRef(null); const [showingConflictModal, setShowingConflictModal] = useState(false); const [textAreaValue, setTextAreaValue] = useState(''); + // TODO: implement light/dark mode, toggle, and saving to local storage + const initialTheme = getFromLocalStorage('theme', 'dark'); + const [theme, setTheme] = useState(initialTheme); + // This effect runs only once after the initial render // because of the empty dependency array []. @@ -102,7 +73,7 @@ function App() { handleListChange(initialTasks); } else if (listStateWrapperFromURL.result.length !== 0 && initialTasks.length !== 0) { - if(arraysAreEqual(listStateWrapperFromURL.result, initialTasks)) { + if(objectArraysAreEqual(listStateWrapperFromURL.result, initialTasks)) { // we don't have to do anything if the query params and local storage list match // console.log("list from query params and list from local storage are the same") } else { @@ -252,6 +223,11 @@ function App() { setErrMsg(""); } + const handleToggleTheme = () => { + setTheme(theme === 'light' ? 'dark' : 'light'); + saveToLocalStorage('theme', theme === 'light' ? 'dark' : 'light'); + } + // Function to handle exporting tasks to a JSON file const handleExportTasks = () => { setImportErrMsg(""); @@ -266,6 +242,7 @@ function App() { a.click(); URL.revokeObjectURL(url); } else { + // TODO: move error string to top of file setErrMsg("Failed to export tasks."); } }; @@ -366,7 +343,7 @@ function App() { return ( -
+

{appName}

@@ -374,7 +351,7 @@ function App() { @@ -383,10 +360,19 @@ function App() { + +
+ +
@@ -397,7 +383,7 @@ function App() { ref={inputRef} id="todo-input" disabled={isPrioritizing || showingDeleteModal || showingMoreInfo || showingConflictModal || showingSaveModal} - className="todo-input pa2 w-100 input-reset br3 ba bw1 b--gray hover-bg-light-gray active-bg-white" + className={`todo-input pa2 w-100 input-reset br3 ba bw1 b--gray ${theme === 'light' ? 'black hover-bg-light-gray active-bg-white' : 'white bg-black hover-bg-dark-gray active-bg-black'}'}`} type="text" placeholder="Add a task..." value={inputValue} @@ -414,24 +400,24 @@ function App() {
@@ -452,29 +438,29 @@ function App() { {/*prioritization review modal*/} {(isPrioritizing && cursor !== -1 && cursor < tasks.length) && -
+

{genQuestion(tasks, cursor)}

- - -
} {/*'are you sure you want to delete your list?' modal*/} {showingDeleteModal && -
+

Are you sure you want to delete your list?

- -
} {/*save modal*/} {showingSaveModal && -
+

{saveInfo1}

@@ -484,12 +470,12 @@ function App() { tabIndex="0" onKeyDown={handleLabelKeyPress} htmlFor="file-upload" - className="br3 grow dib button-reset border-box w4 f5 fw6 ba bw1 b--gray bg-moon-gray pa2 pointer ma1"> + className={`br3 grow dib button-reset border-box w4 f5 fw6 ba bw1 b--gray ${theme === 'light' ? 'bg-moon-gray' : 'bg-dark-gray white'} pa2 pointer ma1`}> Import - @@ -500,7 +486,7 @@ function App() {