{"version":3,"mappings":"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","names":["relList","link","processPreload","mutations","mutation","node","getFetchOpts","script","fetchOpts","LoadingOrError","error","Box","message","defaultProps","undefined","NO_FILTER","TodosListInMemoryFilterer","todos","filterAst","todo","t","_TagUtil","caseSensitive","accessor","type","id","tag","tags","tagsArr","todoTag","todoTags","contexts","caseInvariant","groups","g","group","first","sorted","rest","result","TagUtil","tagA","tagB","todoA","todoB","INITIAL_DEFAULT_VIEW_PREFERENCES","INITIAL_USER_SETTINGS","INITIAL_STATE","viewDefinitionsAdapter","createEntityAdapter","viewDefinitionsSlice","createSlice","viewDefinitionActions","viewDefinitionReducers","viewDefinitionsSelectors","state","selectProjects","createSelector","list","selectContexts","userPreferences","selectLastViewDefinitionId","lastViewDefinitionId","makeSelectViewTodos","_","viewDefinition","filtered","sortTodos","selectViewDefinitionById","viewDefinitionId","selectViewInstructions","viewPreferences","defaultPreferences","preferences","_a","_b","_c","_e","useMemo","useAppDispatch","useDispatch","useAppSelector","useSelector","useTodosView","selectViewTodos","LoginPage","lazy","__vitePreload","Layout","AddPage","EditPage","EditViewDefinitionPage","useRouterViewDefinition","useParams","useRouterViewInstructions","viewInstructions","LoginRequiredRoutes","user","_jsx","Outlet","Navigate","RouterEditPage","params","todoId","LandingPage","lastUsedViewDefinitionId","RouteLayout","RouteAddPage","RouteEditViewDefinitionPage","AppRoutes","BrowserRouter","Suspense","Routes","Route","_jsxs","browserHistory","createBrowserHistory","reactPlugin","ReactPlugin","appInsights","ApplicationInsights","Logger","input","level","buildTodoItem","item","uuidv4","_d","_f","_g","_h","_i","TodoOperationType","_TodoFileSerializer","raw","lineNumber","date","project","context","body","s","re","match","features","startIndex","length","isCompleted","fileContents","line","sortedByLineNumber","operation","newList","opTodo","updated","newTodo","newLineNumber","apply","targetIndex","originalTodo","modifiedItem","TodoFileSerializer","num","numZeros","zeros","n","zeroString","createAction","saveRefreshedAuth","logout","mutateTodos","syncBegin","payload","syncWork","syncComplete","syncFail","setLastUsedViewDefinition","rootReducer","createReducer","builder","setViewDefinition","action","setUser","logger","refreshedAccessToken","accessTokenExpiresAt","lastHistoryEntryHasRemoteRevisionId","localRevisionId","lastHistoryEntry","remoteRevisionId","historyEntryWithRemoteRevision","aos","h","lastStateLatestLocalRevision","lastEntry","historyEntryToUpdate","operationApplication","operationApplications","todoFormat","op","appliedOperationSet","indexOfLastRemoteRevision","useEffect","useEventListener","eventName","element","handler","options","savedHandler","useRef","capture","passive","once","useLayoutEffect","current","addEventListener","eventListener","event","opts","removeEventListener","useInterval","callback","delay","savedCallback","setInterval","log","clearInterval","BackgroundSync","syncId","synchronization","dispatch","skipIfSyncedSinceMs","window","verbose","document","hidden","toString","visibilityState","hasFocus","ErrorBoundary","React","Component","constructor","props","hasError","getDerivedStateFromError","componentDidCatch","errorInfo","render","children","appTheme","createTheme","DropboxFileProvider","tokens","authOptions","Dropbox","DropboxAuth","path","resp","content","version","refreshedAuth","auth","accessToken","rev","file","mode","responseStatus","DropboxResponseError","i","lastError","_DropboxOauthPkceProvider","clientId","redirectUri","authUrl","dropboxAuth","searchParams","code","codeVerifier","accessTokenResponse","DropboxOauthPkceProvider","ConflictResolvingSaver","cloudFileProvider","originalVersion","originalContent","shouldContinue","intermediateResolution","conflictingFile","diff3Result","diff3Merge","resolvedResult","merged","source","destination","sourceItem","rawMap","destinationItem","toAdd","addSagas","startListening","isAnyOf","listenerApi","shouldWrite","fileProvider","conflictResolver","errorCode","errorMessage","saveResult","serializer","newFileError","persistConfig","persistedReducer","persistReducer","listenerMiddleware","createListenerMiddleware","reduxPersistActions","FLUSH","REHYDRATE","PAUSE","PERSIST","PURGE","REGISTER","store","configureStore","getDefaultMiddleware","persistor","persistStore","startAppListening","App","Provider","PersistGate","ThemeProvider","CssBaseline","registerSW","wb","updateServiceWorker","reloadPage","Workbox","onOfflineReady","r","onRegistered","e","onRegisterError","createRoot","container","querySelector","StrictMode"],"sources":["../../vite/modulepreload-polyfill","../../src/components/LoadingOrError.tsx","../../src/lib/todotxtformat/filtering.ts","../../src/lib/todotxtformat/tags.ts","../../src/lib/todotxtformat/sorting.ts","../../src/store/model.ts","../../src/store/viewDefinitions.ts","../../src/store/selectors.ts","../../src/store/hooks.ts","../../src/AppRoutes.tsx","../../src/lib/appInsights.ts","../../src/lib/logger.ts","../../src/lib/todotxtformat/model.ts","../../src/lib/todotxtformat/todoFormat.ts","../../src/store/reducers.ts","../../src/BackgroundSync.tsx","../../src/components/ErrorBoundary.tsx","../../src/theme.ts","../../src/lib/fileproviders/dropbox.ts","../../src/lib/todotxtformat/conflictResolver.ts","../../src/store/listeners/listeners.ts","../../src/store/store.ts","../../src/App.tsx","../../../../../../../@vite-plugin-pwa/virtual:pwa-register","../../src/main.tsx"],"sourcesContent":["__VITE_IS_MODERN__&&(function polyfill() {\n const relList = document.createElement('link').relList;\n if (relList && relList.supports && relList.supports('modulepreload')) {\n return;\n }\n for (const link of document.querySelectorAll('link[rel=\"modulepreload\"]')) {\n processPreload(link);\n }\n new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type !== 'childList') {\n continue;\n }\n for (const node of mutation.addedNodes) {\n if (node.tagName === 'LINK' && node.rel === 'modulepreload')\n processPreload(node);\n }\n }\n }).observe(document, { childList: true, subtree: true });\n function getFetchOpts(script) {\n const fetchOpts = {};\n if (script.integrity)\n fetchOpts.integrity = script.integrity;\n if (script.referrerpolicy)\n fetchOpts.referrerPolicy = script.referrerpolicy;\n if (script.crossorigin === 'use-credentials')\n fetchOpts.credentials = 'include';\n else if (script.crossorigin === 'anonymous')\n fetchOpts.credentials = 'omit';\n else\n fetchOpts.credentials = 'same-origin';\n return fetchOpts;\n }\n function processPreload(link) {\n if (link.ep)\n // ep marker = processed\n return;\n link.ep = true;\n // prepopulate the load record\n const fetchOpts = getFetchOpts(link);\n fetch(link.href, fetchOpts);\n }\n}());","import Box from '@mui/material/Box'\nimport type { ReactElement } from 'react'\n\ninterface Properties {\n\terror?: Error\n}\nexport default function LoadingOrError({ error }: Properties): ReactElement {\n\treturn (\n\t\t\n\t\t\t

\n\t\t\t\t{error ? error.message : 'Loading...'}\n\t\t\t

\n\t\t
\n\t)\n}\nLoadingOrError.defaultProps = {\n\terror: undefined\n}\n","/* eslint-disable class-methods-use-this */\r\n\r\nimport type { TodoItem } from './model'\r\n\r\nexport type FilterASTNode = FilterASTEvaluationNode | FilterASTOperatorNode\r\n\r\ninterface FilterASTOperatorNode {\r\n\ttype: 'op'\r\n\top: 'and' | 'or'\r\n\tnodes: FilterASTNode[]\r\n}\r\n\r\ninterface FilterASTEvaluationNode {\r\n\ttype: 'eval'\r\n\tfield: 'contexts' | 'isCompleted' | 'projects' | 'true'\r\n\top: 'contains' | 'equals' | 'notcontains'\r\n\tval: boolean | string | null\r\n}\r\n\r\nexport const NO_FILTER: FilterASTNode = {\r\n\ttype: 'eval',\r\n\tfield: 'true',\r\n\top: 'equals',\r\n\tval: true\r\n}\r\n\r\ninterface IFilterExpressionParser {\r\n\tparseFilterExpression: (filterExpression: string) => FilterASTNode\r\n\r\n\tserializeFilterExpression: (filterAst: FilterASTNode) => string\r\n}\r\n\r\ninterface ITodosListInMemoryFilterer {\r\n\tfilterTodos: (todos: TodoItem[], filterAst: FilterASTNode) => TodoItem[]\r\n\r\n\tevaluateTodo: (todo: TodoItem, filterAst: FilterASTNode) => boolean\r\n}\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\r\nclass FilterExpressionParser implements IFilterExpressionParser {\r\n\tpublic parseFilterExpression(filterExpression: string): FilterASTNode {\r\n\t\tif (filterExpression === '!isCompleted') {\r\n\t\t\treturn { type: 'eval', field: 'isCompleted', op: 'equals', val: false }\r\n\t\t}\r\n\t\tif (filterExpression === 'isCompleted') {\r\n\t\t\treturn { type: 'eval', field: 'isCompleted', op: 'equals', val: true }\r\n\t\t}\r\n\t\tif (filterExpression === '') {\r\n\t\t\treturn NO_FILTER\r\n\t\t}\r\n\t\tthrow new Error('cannot parse yet')\r\n\t}\r\n\r\n\tpublic serializeFilterExpression(filterAst: FilterASTNode): string {\r\n\t\tif (filterAst.type === 'eval') {\r\n\t\t\tif (\r\n\t\t\t\tfilterAst.field === 'isCompleted' &&\r\n\t\t\t\tfilterAst.op === 'equals' &&\r\n\t\t\t\tfilterAst.val === false\r\n\t\t\t) {\r\n\t\t\t\treturn '!isCompleted'\r\n\t\t\t}\r\n\t\t\tif (\r\n\t\t\t\tfilterAst.field === 'isCompleted' &&\r\n\t\t\t\tfilterAst.op === 'equals' &&\r\n\t\t\t\tfilterAst.val === true\r\n\t\t\t) {\r\n\t\t\t\treturn 'isCompleted'\r\n\t\t\t}\r\n\t\t\tif (filterAst.field === 'true' && filterAst.op === 'equals' && filterAst.val === true) {\r\n\t\t\t\treturn ''\r\n\t\t\t}\r\n\t\t}\r\n\t\tthrow new Error('cannot serialize yet')\r\n\t}\r\n}\r\n\r\nexport class TodosListInMemoryFilterer implements ITodosListInMemoryFilterer {\r\n\tpublic filterTodos(todos: TodoItem[], filterAst: FilterASTNode): TodoItem[] {\r\n\t\treturn todos.filter(todo => this.evaluateTodo(todo, filterAst))\r\n\t}\r\n\r\n\t// eslint-disable-next-line consistent-return\r\n\tpublic evaluateTodo(todo: TodoItem, filterAst: FilterASTNode): boolean {\r\n\t\t// eslint-disable-next-line default-case\r\n\t\tswitch (filterAst.type) {\r\n\t\t\tcase 'eval':\r\n\t\t\t\tswitch (filterAst.op) {\r\n\t\t\t\t\tcase 'equals':\r\n\t\t\t\t\t\tswitch (filterAst.field) {\r\n\t\t\t\t\t\t\tcase 'isCompleted':\r\n\t\t\t\t\t\t\t\treturn todo.isCompleted === filterAst.val\r\n\t\t\t\t\t\t\tcase 'true':\r\n\t\t\t\t\t\t\t\treturn !!filterAst.val\r\n\t\t\t\t\t\t\tdefault:\r\n\t\t\t\t\t\t\t\tthrow new Error(`cannot evaluate \"${filterAst.op}}\" yet`)\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\tcase 'contains':\r\n\t\t\t\t\t\tswitch (filterAst.field) {\r\n\t\t\t\t\t\t\tcase 'contexts':\r\n\t\t\t\t\t\t\t\t// eslint-disable-next-line unicorn/prefer-includes\r\n\t\t\t\t\t\t\t\treturn todo.contexts.some(t => t === filterAst.val)\r\n\t\t\t\t\t\t\tcase 'projects':\r\n\t\t\t\t\t\t\t\t// eslint-disable-next-line unicorn/prefer-includes\r\n\t\t\t\t\t\t\t\treturn todo.projects.some(t => t === filterAst.val)\r\n\t\t\t\t\t\t\tdefault:\r\n\t\t\t\t\t\t\t\tthrow new Error(`cannot evaluate contains \"${filterAst.field}}\" yet`)\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\tdefault:\r\n\t\t\t\t\t\tthrow new Error('cannot evaluate \"op\" yet')\r\n\t\t\t\t}\r\n\t\t\tcase 'op':\r\n\t\t\t\tthrow new Error('cannot evaluate \"op\" yet')\r\n\t\t}\r\n\t}\r\n}\r\n","import type { TodoItem } from './model'\r\n\r\nexport interface TodoTag {\r\n\ttype: string\r\n\tid: string\r\n\tname: string\r\n\toccurrences: number\r\n}\r\n\r\n// eslint-disable-next-line @typescript-eslint/no-extraneous-class\r\nexport class TagUtil {\r\n\tpublic static aggregateContexts(todos: TodoItem[], caseSensitive = false): TodoTag[] {\r\n\t\treturn TagUtil.sortTags(TagUtil.aggregateTags(todos, todo => todo.contexts, '@', caseSensitive))\r\n\t}\r\n\r\n\tpublic static aggregateProjects(todos: TodoItem[], caseSensitive = false): TodoTag[] {\r\n\t\treturn TagUtil.sortTags(TagUtil.aggregateTags(todos, todo => todo.projects, '+', caseSensitive))\r\n\t}\r\n\r\n\tpublic static aggregateTags(\r\n\t\ttodos: TodoItem[],\r\n\t\taccessor: (item: TodoItem) => string[],\r\n\t\ttype: string,\r\n\t\tcaseSensitive = false\r\n\t): TodoTag[] {\r\n\t\tlet tags = new Map()\r\n\r\n\t\tfor (const todo of todos) {\r\n\t\t\tfor (const tag of accessor(todo)) {\r\n\t\t\t\tconst id = type + tag\r\n\t\t\t\tif (tags.has(id)) {\r\n\t\t\t\t\tconst t = tags.get(id)\r\n\t\t\t\t\tif (t) {\r\n\t\t\t\t\t\tt.occurrences += 1\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\ttags.set(id, { type, name: tag, id, occurrences: 1 })\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (!caseSensitive) {\r\n\t\t\ttags = TagUtil.collapseDifferentCasing(tags)\r\n\t\t}\r\n\r\n\t\tconst tagsArr: TodoTag[] = []\r\n\t\tfor (const [, todoTag] of tags) {\r\n\t\t\ttagsArr.push(todoTag)\r\n\t\t}\r\n\r\n\t\treturn tagsArr\r\n\t}\r\n\r\n\tpublic static filterTagByType(todoTags: TodoTag[], type: string): TodoTag[] {\r\n\t\tconst contexts: TodoTag[] = []\r\n\t\tfor (const tag of todoTags) {\r\n\t\t\tif (tag.type === type) {\r\n\t\t\t\tcontexts.push(tag)\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn contexts\r\n\t}\r\n\r\n\tpublic static sortTags(tags: TodoTag[]): TodoTag[] {\r\n\t\treturn tags.sort(TagUtil.compareTagsByOccurrences)\r\n\t}\r\n\r\n\tpublic static compareTagsByOccurrences = (tagA: TodoTag, tagB: TodoTag): number =>\r\n\t\ttagB.occurrences - tagA.occurrences\r\n\r\n\tprivate static collapseDifferentCasing(tags: Map): Map {\r\n\t\tconst groups: Map = new Map()\r\n\r\n\t\tfor (const [, tag] of tags) {\r\n\t\t\tconst caseInvariant = tag.id.toLowerCase()\r\n\t\t\tif (groups.has(caseInvariant)) {\r\n\t\t\t\tconst g = groups.get(caseInvariant)\r\n\t\t\t\tif (g) {\r\n\t\t\t\t\tg.push(tag)\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tgroups.set(caseInvariant, [tag])\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst result = new Map()\r\n\t\tfor (const [, group] of groups) {\r\n\t\t\tconst sorted = TagUtil.sortTags(group)\r\n\t\t\tconst first = sorted[0]\r\n\t\t\tfor (const rest of sorted.slice(1)) {\r\n\t\t\t\tfirst.occurrences += rest.occurrences\r\n\t\t\t}\r\n\t\t\tresult.set(first.id, first)\r\n\t\t}\r\n\t\treturn result\r\n\t}\r\n}\r\n","import type { TodoItem } from './model'\r\n\r\n// eslint-disable-next-line import/prefer-default-export\r\nexport const sortTodos = (todoA: TodoItem, todoB: TodoItem): number => {\r\n\tif (todoA.isCompleted !== todoB.isCompleted) {\r\n\t\tif (!todoA.isCompleted) {\r\n\t\t\treturn -1\r\n\t\t}\r\n\t\tif (!todoB.isCompleted) {\r\n\t\t\treturn 1\r\n\t\t}\r\n\t}\r\n\r\n\tif (todoA.priority !== todoB.priority) {\r\n\t\tif (todoA.priority == null) {\r\n\t\t\treturn -1\r\n\t\t}\r\n\t\tif (todoB.priority == null) {\r\n\t\t\treturn 1\r\n\t\t}\r\n\t\t// eslint-disable-next-line unicorn/prefer-code-point\r\n\t\treturn todoA.priority.toUpperCase().charCodeAt(0) - todoB.priority.toUpperCase().charCodeAt(0)\r\n\t}\r\n\treturn todoA.lineNumber - todoB.lineNumber\r\n}\r\n","import type { EntityState } from '@reduxjs/toolkit'\r\nimport type { FilterASTNode } from 'lib/todotxtformat/filtering'\r\nimport type { TodoPriority } from 'lib/todotxtformat/model'\r\nimport type { TodoItem, TodoOperationApplication } from '../lib/todotxtformat'\r\n\r\ntype PriorityAliases = { [key in TodoPriority]: string }\r\n\r\nexport interface ViewPreferences {\r\n\tdefaultPriority: TodoPriority\r\n\tdefaultProjects: string[]\r\n\tdefaultContexts: string[]\r\n\tpriorityAliases: Partial\r\n}\r\n\r\nexport interface ViewDefinition {\r\n\tid: string\r\n\tname: string\r\n\tfilter: FilterASTNode\r\n\r\n\tpreferences: Partial\r\n}\r\n\r\n// Not actually in the state, but is a selector result\r\nexport interface ViewInstructions extends ViewDefinition {\r\n\tpreferences: ViewPreferences\r\n}\r\n\r\nexport interface UserSettingsState {\r\n\ttodoFilePath: string\r\n\t// useVirtualScroll: boolean\r\n\tdefaultViewPreferences: ViewPreferences\r\n}\r\n\r\nexport interface UserState {\r\n\tname: string\r\n\taccessToken: string\r\n\trefreshToken: string\r\n\taccessTokenExpiresAt: number\r\n\tclientId: string\r\n\tsettings: UserSettingsState\r\n}\r\n\r\nexport interface AppliedTodoOperationSet {\r\n\tlocalRevisionId: string\r\n\tremoteRevisionId?: string\r\n\tlocalTime: number\r\n\toperationApplications: TodoOperationApplication[]\r\n}\r\n\r\nexport interface SynchronizationState {\r\n\tinProgress: boolean\r\n\tsyncId: string\r\n\tlocalRevisionId: string | null\r\n\tremoteRevisionId: string | null\r\n\tlastCompleteTime: number\r\n\ttodos: TodoItem[] | null\r\n\tlastSyncedTodoFileContent: string | null\r\n}\r\n\r\nexport interface InterfaceState {\r\n\tlastViewDefinitionId: string\r\n}\r\n\r\nexport interface AppState {\r\n\tschemaVersion: number\r\n\tlist: TodoItem[]\r\n\tuser?: UserState\r\n\t// router?: any\r\n\thistory: AppliedTodoOperationSet[]\r\n\tsynchronization: SynchronizationState\r\n\tinterface: InterfaceState\r\n\r\n\tviewDefinitions: EntityState\r\n}\r\n\r\n/*\r\n# Mutating the list\r\nAction is a list of ops => store these in a list for undo?\r\nsomething to transform a list of ops into a list of string insert/deletes (also their inverse for undo?)\r\nopgroup: listop[], strop[], localversion, remoteversion\r\nlistopgroup: listop[], localVersion: string\r\nlistop: enum type,\r\nstrop: type: 'insert' | 'delete', pos: int, val: str\r\n'ComputeTransform' listop[] => strop[] + TodosCollection\r\n\r\n*/\r\n\r\nexport const INITIAL_DEFAULT_VIEW_PREFERENCES: ViewPreferences = {\r\n\tdefaultPriority: 'A',\r\n\tdefaultContexts: [],\r\n\tdefaultProjects: [],\r\n\tpriorityAliases: {\r\n\t\tA: 'A - Next 30m',\r\n\t\tB: 'B - Next 1h',\r\n\t\tC: 'C - Next 2h',\r\n\t\tD: 'D - Next 4h',\r\n\t\tE: 'E - Next 8h',\r\n\t\tF: 'F - Next 24h',\r\n\t\tG: 'G - Next 48h',\r\n\t\tH: 'H - Next 7d',\r\n\t\tI: 'I - Next 14d',\r\n\t\tJ: 'J - Next 1 month',\r\n\t\tK: 'K - Next 2 months',\r\n\t\tL: 'L - Next 4 months',\r\n\t\tM: 'M - Next 6 months',\r\n\t\tN: 'N - Next 1 year',\r\n\t\tO: 'O - Next 2 years',\r\n\t\tP: 'P - Next 5 years',\r\n\t\tQ: 'Q - Someday'\r\n\t}\r\n}\r\n\r\nexport const INITIAL_USER_SETTINGS: UserSettingsState = {\r\n\t// useVirtualScroll: false,\r\n\ttodoFilePath: '/todo/todo.txt',\r\n\tdefaultViewPreferences: INITIAL_DEFAULT_VIEW_PREFERENCES\r\n}\r\n\r\nexport const INITIAL_STATE: AppState = {\r\n\t/*\r\n text file : string\r\n archive file: string\r\n parsed objects\r\n [todo]\r\n oplog[opgroup]\r\n opgroup[op]\r\n\r\n */\r\n\tschemaVersion: 2,\r\n\tlist: [],\r\n\tuser: undefined,\r\n\thistory: [],\r\n\tsynchronization: {\r\n\t\tinProgress: false,\r\n\t\tsyncId: '',\r\n\t\tlocalRevisionId: null,\r\n\t\tremoteRevisionId: null,\r\n\t\ttodos: null,\r\n\t\tlastCompleteTime: 0,\r\n\t\tlastSyncedTodoFileContent: null\r\n\t},\r\n\tinterface: {\r\n\t\tlastViewDefinitionId: 'all'\r\n\t},\r\n\tviewDefinitions: {\r\n\t\tids: [],\r\n\t\tentities: {}\r\n\t}\r\n}\r\n","import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'\r\nimport type { AppState, ViewDefinition } from './model'\r\n\r\nconst viewDefinitionsAdapter = createEntityAdapter({})\r\n\r\nconst viewDefinitionsSlice = createSlice({\r\n\tname: 'viewDefinitions',\r\n\tinitialState: viewDefinitionsAdapter.getInitialState(),\r\n\treducers: {\r\n\t\tsetViewDefinition: viewDefinitionsAdapter.setOne,\r\n\t\tdeleteViewDefinition: viewDefinitionsAdapter.removeOne\r\n\t}\r\n})\r\n\r\nexport const viewDefinitionActions = viewDefinitionsSlice.actions\r\nexport const viewDefinitionReducers = viewDefinitionsSlice.caseReducers\r\nexport const viewDefinitionsSelectors =\r\n\tviewDefinitionsAdapter.getSelectors(state => state.viewDefinitions)\r\n","import { createSelector } from '@reduxjs/toolkit'\r\nimport type { TodoItem } from 'lib/todotxtformat'\r\nimport { NO_FILTER, TodosListInMemoryFilterer } from 'lib/todotxtformat/filtering'\r\nimport { TagUtil } from 'lib/todotxtformat/tags'\r\nimport { sortTodos } from '../lib/todotxtformat/sorting'\r\nimport type {\r\n\tAppState,\r\n\tViewDefinition,\r\n\tViewInstructions,\r\n\tViewPreferences\r\n} from './model';\nimport {\r\n\tINITIAL_DEFAULT_VIEW_PREFERENCES\r\n} from './model'\r\nimport { viewDefinitionsSelectors } from './viewDefinitions'\r\n\r\nexport const selectProjects = createSelector(\r\n\t(state: AppState): TodoItem[] => state.list,\r\n\t(list: TodoItem[]) => TagUtil.aggregateProjects(list, false)\r\n)\r\n\r\nexport const selectContexts = createSelector(\r\n\t(state: AppState): TodoItem[] => state.list,\r\n\t(list: TodoItem[]) => TagUtil.aggregateContexts(list, false)\r\n)\r\n\r\nexport const selectViewPreferences = createSelector(\r\n\t(state: AppState): ViewPreferences | undefined => state.user?.settings.defaultViewPreferences,\r\n\tuserPreferences => userPreferences ?? INITIAL_DEFAULT_VIEW_PREFERENCES\r\n)\r\n\r\nexport const selectLastViewDefinitionId = createSelector(\r\n\t(state: AppState): string => state.interface.lastViewDefinitionId,\r\n\tlastViewDefinitionId => lastViewDefinitionId\r\n)\r\n\r\nexport const makeSelectViewTodos = () =>\r\n\tcreateSelector(\r\n\t\t(state: AppState) => state.list,\r\n\t\t(_: AppState, viewDefinition: ViewDefinition) => viewDefinition,\r\n\t\t(todos, viewDefinition) => {\r\n\t\t\tconst filtered = viewDefinition\r\n\t\t\t\t? new TodosListInMemoryFilterer().filterTodos(todos, viewDefinition.filter)\r\n\t\t\t\t: [...todos] // copy for in-place sort\r\n\r\n\t\t\tfiltered.sort(sortTodos)\r\n\r\n\t\t\treturn filtered\r\n\t\t}\r\n\t)\r\n\r\nexport const selectViewDefinitionById = createSelector(\r\n\t[\r\n\t\t// Usual first input - extract value from `state`\r\n\t\tviewDefinitionsSelectors.selectById,\r\n\t\t// Take the second arg, `category`, and forward to the output selector\r\n\t\t(_, viewDefinitionId: string | undefined) => viewDefinitionId\r\n\t],\r\n\t// Output selector gets (`items, category)` as args\r\n\t(viewDefinition, viewDefinitionId): ViewDefinition => {\r\n\t\tif (!viewDefinitionId || viewDefinitionId === 'all') {\r\n\t\t\treturn {\r\n\t\t\t\tname: 'All Todos',\r\n\t\t\t\tfilter: NO_FILTER,\r\n\t\t\t\tpreferences: {},\r\n\t\t\t\tid: 'all'\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (!viewDefinition) {\r\n\t\t\tif (viewDefinitionId.startsWith('@')) {\r\n\t\t\t\treturn {\r\n\t\t\t\t\tname: viewDefinitionId,\r\n\t\t\t\t\tfilter: {\r\n\t\t\t\t\t\ttype: 'eval',\r\n\t\t\t\t\t\tfield: 'contexts',\r\n\t\t\t\t\t\top: 'contains',\r\n\t\t\t\t\t\tval: viewDefinitionId.slice(1)\r\n\t\t\t\t\t},\r\n\t\t\t\t\tpreferences: { defaultContexts: [viewDefinitionId.slice(1)] },\r\n\t\t\t\t\tid: viewDefinitionId\r\n\t\t\t\t}\r\n\t\t\t} if (viewDefinitionId.startsWith('+')) {\r\n\t\t\t\treturn {\r\n\t\t\t\t\tname: viewDefinitionId,\r\n\t\t\t\t\tfilter: {\r\n\t\t\t\t\t\ttype: 'eval',\r\n\t\t\t\t\t\tfield: 'projects',\r\n\t\t\t\t\t\top: 'contains',\r\n\t\t\t\t\t\tval: viewDefinitionId.slice(1)\r\n\t\t\t\t\t},\r\n\t\t\t\t\tpreferences: { defaultProjects: [viewDefinitionId.slice(1)] },\r\n\t\t\t\t\tid: viewDefinitionId\r\n\t\t\t\t}\r\n\t\t\t} \r\n\t\t\t\tthrow new Error(\"Can't synthesize view definition\")\r\n\t\t\t\r\n\t\t}\r\n\r\n\t\treturn viewDefinition\r\n\t}\r\n)\r\n\r\nexport const selectViewInstructions = createSelector(\r\n\t[selectViewDefinitionById, (state: AppState) => state.user?.settings.defaultViewPreferences],\r\n\t({ id, name, filter, preferences: viewPreferences }, userPreferences): ViewInstructions => {\r\n\t\tconst defaultPreferences = userPreferences ?? INITIAL_DEFAULT_VIEW_PREFERENCES\r\n\r\n\t\tconst preferences: ViewPreferences = {\r\n\t\t\tdefaultPriority: viewPreferences.defaultPriority ?? defaultPreferences.defaultPriority,\r\n\t\t\tdefaultContexts: viewPreferences.defaultContexts ?? defaultPreferences.defaultContexts,\r\n\t\t\tdefaultProjects: viewPreferences.defaultProjects ?? defaultPreferences.defaultProjects,\r\n\t\t\tpriorityAliases: viewPreferences.priorityAliases ?? defaultPreferences.priorityAliases ?? {}\r\n\t\t}\r\n\r\n\t\treturn { id, name, filter, preferences }\r\n\t}\r\n)\r\n","import type { TodoItem } from 'lib/todotxtformat'\r\nimport { useMemo } from 'react'\r\nimport type { TypedUseSelectorHook } from 'react-redux'\r\nimport { useDispatch, useSelector } from 'react-redux'\r\nimport type { ViewDefinition } from './model'\r\nimport { makeSelectViewTodos } from './selectors'\r\nimport type { AppDispatch, RootState } from './store'\r\n\r\n// Use throughout your app instead of plain `useDispatch` and `useSelector`\r\nexport const useAppDispatch: () => AppDispatch = useDispatch\r\nexport const useAppSelector: TypedUseSelectorHook = useSelector\r\n\r\nexport function useTodosView(viewDefinition: ViewDefinition): TodoItem[] {\r\n\tconst selectViewTodos = useMemo(makeSelectViewTodos, [])\r\n\r\n\tconst todos = useAppSelector(state => selectViewTodos(state, viewDefinition))\r\n\r\n\treturn todos\r\n}\r\n","import LoadingOrError from 'components/LoadingOrError'\r\nimport type { ReactElement } from 'react'\r\nimport { lazy, Suspense } from 'react'\r\nimport { BrowserRouter, Navigate, Outlet, Route, Routes, useParams } from 'react-router-dom'\r\nimport { useAppSelector } from 'store/hooks'\r\nimport type { UserState, ViewDefinition, ViewInstructions } from 'store/model'\r\nimport {\r\n\tselectLastViewDefinitionId,\r\n\tselectViewDefinitionById,\r\n\tselectViewInstructions\r\n} from 'store/selectors'\r\n\r\nconst LoginPage = lazy(async () => import('components/LoginPage'))\r\nconst Layout = lazy(async () => import('components/Layout'))\r\nconst AddPage = lazy(async () => import('components/author/AddPage'))\r\nconst EditPage = lazy(async () => import('components/author/EditPage'))\r\nconst EditViewDefinitionPage = lazy(async () => import('components/EditViewDefinitionPage'))\r\n\r\nimport('components/author/AddPage')\r\n\r\nexport function useRouterViewDefinition(): ViewDefinition {\r\n\tconst { viewDefinitionId } = useParams()\r\n\tconst viewDefinition: ViewDefinition = useAppSelector(state =>\r\n\t\tselectViewDefinitionById(state, viewDefinitionId ?? 'all')\r\n\t)\r\n\treturn viewDefinition\r\n}\r\n\r\nexport function useRouterViewInstructions(): ViewInstructions {\r\n\tconst { viewDefinitionId } = useParams()\r\n\tconst viewInstructions = useAppSelector(state =>\r\n\t\tselectViewInstructions(state, viewDefinitionId ?? 'all')\r\n\t)\r\n\treturn viewInstructions\r\n}\r\n\r\nfunction LoginRequiredRoutes({ user }: { user: UserState | undefined }): ReactElement {\r\n\tif (!user) {\r\n\t\treturn \r\n\t}\r\n\treturn \r\n}\r\n\r\nfunction RouterEditPage(): ReactElement {\r\n\tconst params = useParams()\r\n\r\n\treturn params.todoId ? : \r\n}\r\n\r\nfunction LandingPage(): ReactElement {\r\n\tconst lastUsedViewDefinitionId = useAppSelector(selectLastViewDefinitionId)\r\n\r\n\treturn \r\n}\r\n\r\nfunction RouteLayout(): ReactElement {\r\n\tconst viewInstructions = useRouterViewInstructions()\r\n\r\n\treturn \r\n}\r\n\r\nfunction RouteAddPage(): ReactElement {\r\n\tconst viewInstructions = useRouterViewInstructions()\r\n\r\n\treturn \r\n}\r\n\r\nfunction RouteEditViewDefinitionPage(): ReactElement {\r\n\tconst viewDefinition = useRouterViewDefinition()\r\n\tconst viewInstructions = useRouterViewInstructions()\r\n\r\n\treturn (\r\n\t\t\r\n\t)\r\n}\r\n\r\nexport default function AppRoutes(): ReactElement {\r\n\tconst user = useAppSelector(state => state.user)\r\n\r\n\treturn (\r\n\t\t\r\n\t\t\t}>\r\n\t\t\t\t\r\n\t\t\t\t\t} />\r\n\t\t\t\t\t} />\r\n\r\n\t\t\t\t\t}>\r\n\t\t\t\t\t\t} />\r\n\t\t\t\t\t\t} />\r\n\t\t\t\t\t\t} />\r\n\t\t\t\t\t\t} />\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t} />\r\n\t\t\t\t\t\t} />\r\n\t\t\t\t\t\r\n\t\t\t\t\t} />\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t)\r\n}\r\n","import { ReactPlugin } from '@microsoft/applicationinsights-react-js'\r\nimport { ApplicationInsights } from '@microsoft/applicationinsights-web'\r\nimport { createBrowserHistory } from 'history'\n\r\nconst browserHistory = createBrowserHistory() // ({ basename: '' })\r\nconst reactPlugin = new ReactPlugin()\r\nconst appInsights = new ApplicationInsights({\r\n\tconfig: {\r\n\t\tconnectionString:\r\n\t\t\t'InstrumentationKey=cf10994d-3f29-481a-814a-7cc3a95d2eae;IngestionEndpoint=https://eastus-3.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/',\r\n\t\textensions: [reactPlugin],\r\n\t\t// enableAutoRouteTracking: true,\r\n\t\textensionConfig: {\r\n\t\t\t[reactPlugin.identifier]: { history: browserHistory }\r\n\t\t}\r\n\t}\r\n})\r\nappInsights.loadAppInsights()\r\n\r\nexport { reactPlugin, appInsights }\r\n","import { appInsights } from './appInsights'\r\n\r\nclass Logger {\r\n\tpublic verbose(message: string): void {\r\n\t\tLogger.trackTrace(message, 0)\r\n\t\t// eslint-disable-next-line no-console\r\n\t\tconsole.log.apply(\r\n\t\t\tconsole,\r\n\t\t\targuments as unknown as [message?: any, ...optionalParams: any[]]\r\n\t\t)\r\n\t\t// console.log(message)\r\n\t}\r\n\r\n\tpublic info(message: string): void {\r\n\t\tLogger.trackTrace(message, 1)\r\n\t\t// eslint-disable-next-line no-console\r\n\t\tconsole.log.apply(\r\n\t\t\tconsole,\r\n\t\t\targuments as unknown as [message?: any, ...optionalParams: any[]]\r\n\t\t)\r\n\t}\r\n\r\n\tpublic warn = (input: any): void => {\r\n\t\tconst message = Logger.buildMessage(input)\r\n\t\tLogger.trackTrace(message, 2)\r\n\t\t// eslint-disable-next-line no-console\r\n\t\tconsole.warn(message)\r\n\t}\r\n\r\n\tpublic error = (input: any): void => {\r\n\t\tconst message = Logger.buildMessage(input)\r\n\t\tLogger.trackTrace(message, 3)\r\n\t\t// eslint-disable-next-line no-console\r\n\t\tconsole.error(message)\r\n\t}\r\n\r\n\tpublic critical = (message: string): void => {\r\n\t\tLogger.trackTrace(message, 4)\r\n\t\t// eslint-disable-next-line no-console\r\n\t\tconsole.error(message)\r\n\t}\r\n\r\n\tprivate static trackTrace(message: string, level: number): void {\r\n\t\tappInsights.trackTrace({ message, severityLevel: level })\r\n\t}\r\n\r\n\tprivate static buildMessage(input: any): string {\r\n\t\tif (input instanceof Error) {\r\n\t\t\tappInsights.trackException({ exception: input })\r\n\t\t\tconsole.error(input)\r\n\t\t\treturn input.message\r\n\t\t}\r\n\r\n\t\tif (input) {\r\n\t\t\treturn input.toString()\r\n\t\t}\r\n\r\n\t\treturn ''\r\n\t}\r\n}\r\n\r\n// eslint-disable-next-line import/prefer-default-export\r\nexport const logger = new Logger()\r\n","import { v4 as uuidv4 } from 'uuid'\r\n\r\n// prettier-ignore\r\nexport type TodoPriority = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' \r\n| 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' \r\n| 'Y' | 'Z'\r\n// export type TodoPriority = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k'\r\n// | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x'\r\n// | 'y' | 'z' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K'\r\n// | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X'\r\n// | 'Y' | 'Z'\r\n\r\nexport interface TodoItem {\r\n\tid: string\r\n\r\n\tlineNumber: number\r\n\traw: string\r\n\r\n\tisCompleted: boolean\r\n\tpriority: TodoPriority | null\r\n\r\n\tbody: string\r\n\r\n\tprojects: string[]\r\n\tcontexts: string[]\r\n\r\n\tdueDate: string | null\r\n\tcompletedDate: string | null\r\n}\r\n\r\nexport function buildTodoItem(item?: Partial): TodoItem {\r\n\t// eslint-disable-next-line no-param-reassign\r\n\titem = item ?? {}\r\n\treturn {\r\n\t\tid: item.id ?? uuidv4(),\r\n\t\tlineNumber: item.lineNumber === undefined ? -1 : item.lineNumber,\r\n\t\traw: item.raw ?? '',\r\n\t\tisCompleted: item.isCompleted ?? false,\r\n\t\tpriority: item.priority ?? null,\r\n\t\tbody: item.body ?? '',\r\n\t\tprojects: [...(item.projects ?? [])],\r\n\t\tcontexts: [...(item.contexts ?? [])],\r\n\t\tdueDate: item.dueDate ?? null,\r\n\t\tcompletedDate: item.completedDate ?? null\r\n\t}\r\n}\r\n\r\nexport enum TodoOperationType {\r\n\tSET_IS_COMPLETED = 'SET_IS_COMPLETED',\r\n\r\n\tUPSERT_TODO = 'UPSERT_TODO',\r\n\r\n\tDELETE_TODO = 'DELETE_TODO'\r\n}\r\n\r\nexport interface BaseTodoOperation {\r\n\ttype: string\r\n}\r\n\r\nexport interface TodoOperationOnExisting extends BaseTodoOperation {\r\n\ttodoId: string\r\n}\r\n\r\nexport interface SetIsCompletedTodoOperation extends TodoOperationOnExisting {\r\n\ttype: TodoOperationType.SET_IS_COMPLETED\r\n\r\n\tisCompleted: boolean\r\n}\r\n\r\nexport interface DeleteTodoOperation extends TodoOperationOnExisting {\r\n\ttype: TodoOperationType.DELETE_TODO\r\n}\r\n\r\nexport interface UpsertTodoOperation extends BaseTodoOperation {\r\n\ttype: TodoOperationType.UPSERT_TODO\r\n\r\n\ttodo: TodoItem\r\n}\r\n\r\nexport type TodoOperation = DeleteTodoOperation | SetIsCompletedTodoOperation | UpsertTodoOperation\r\n\r\nexport interface TodoOperationApplication {\r\n\toperation: TodoOperation\r\n}\r\n","import type { TodoItem } from '.'\r\nimport type {\r\n\tTodoOperation,\r\n\tTodoOperationApplication,\r\n\tTodoPriority\r\n} from './model';\nimport {\r\n\tbuildTodoItem,\r\n\tTodoOperationType\r\n} from './model'\r\n\r\nexport default class TodoFileSerializer {\r\n\tstatic todoItemFromRaw(raw: string, lineNumber: number): TodoItem {\r\n\t\t// check(fileSetId, String);\r\n\t\t// check(userId, String);\r\n\t\t// check(raw, String);\r\n\t\t// check(lineNumber, Number);\r\n\r\n\t\tconst todo = buildTodoItem({\r\n\t\t\traw,\r\n\t\t\tlineNumber\r\n\t\t})\r\n\t\tTodoFileSerializer.fillComputedPropertiesFromRaw(todo)\r\n\t\treturn todo\r\n\t}\r\n\r\n\tstatic _todayInYyyyMmDd(): string {\r\n\t\treturn TodoFileSerializer._dateInYyyyMmDd(new Date())\r\n\t}\r\n\r\n\tstatic _dateInYyyyMmDd(date: Date): string {\r\n\t\tconst dateStr =\r\n\t\t\t`${date.getFullYear().toString() \r\n\t\t\t}-${ \r\n\t\t\tTodoFileSerializer._zeroPad(date.getMonth() + 1, 2) \r\n\t\t\t}-${ \r\n\t\t\tTodoFileSerializer._zeroPad(date.getDate(), 2)}`\r\n\r\n\t\treturn dateStr\r\n\t}\r\n\r\n\tstatic buildRawFromTodo(todo: TodoItem): string {\r\n\t\tlet raw = ''\r\n\r\n\t\tif (todo.isCompleted) {\r\n\t\t\traw += 'x '\r\n\t\t}\r\n\r\n\t\tif (todo.completedDate) {\r\n\t\t\traw += `${todo.completedDate } `\r\n\t\t}\r\n\r\n\t\tif (todo.priority) {\r\n\t\t\traw += `(${ todo.priority }) `\r\n\t\t}\r\n\r\n\t\tif (todo.body) {\r\n\t\t\traw += `${todo.body.trim() } `\r\n\t\t}\r\n\r\n\t\tfor (const project of todo.projects) {\r\n\t\t\traw += `+${ project } `\r\n\t\t}\r\n\r\n\t\tfor (const context of todo.contexts) {\r\n\t\t\traw += `@${ context } `\r\n\t\t}\r\n\r\n\t\tif (todo.dueDate) {\r\n\t\t\traw += `due:${ todo.dueDate } `\r\n\t\t}\r\n\r\n\t\treturn raw.trim()\r\n\t}\r\n\r\n\tprivate static fillComputedPropertiesFromRaw(todo: TodoItem): void {\r\n\t\tlet body = todo.raw\r\n\t\tlet result\r\n\r\n\t\t// isCompleted\r\n\t\tresult = TodoFileSerializer.extractFeature(\r\n\t\t\tbody,\r\n\t\t\t/^(x\\s\\d{2,4}(?:-\\d{1,2}){2})\\s+/i\r\n\t\t)\r\n\t\tbody = result[0]\r\n\t\ttodo.isCompleted = !!result[1]\r\n\t\ttodo.completedDate = todo.isCompleted && result[1] ? result[1].slice(2) : null;\r\n\r\n\t\t// priority\r\n\t\tresult = TodoFileSerializer.extractFeature(body, /^\\(([a-z])\\)\\s/i)\r\n\t\tbody = result[0]\r\n\t\ttodo.priority = result[1] as TodoPriority\r\n\r\n\t\t// dueDate\r\n\t\tresult = TodoFileSerializer.extractFeature(body, /due:(\\d{4}-\\d{2}-\\d{2})/i)\r\n\t\tbody = result[0]\r\n\t\ttodo.dueDate = result[1]\r\n\r\n\t\t// projects\r\n\t\tresult = TodoFileSerializer.extractAllFeatures(body, /(?:^|\\s)\\+(\\S+)/i)\r\n\t\tbody = result[0]\r\n\t\ttodo.projects = result[1]\r\n\r\n\t\t// contexts\r\n\t\tresult = TodoFileSerializer.extractAllFeatures(body, /(?:^|\\s)@(\\S+)/i)\r\n\t\tbody = result[0]\r\n\t\ttodo.contexts = result[1]\r\n\r\n\t\ttodo.body = body\r\n\t}\r\n\r\n\tprivate static extractFeature(\r\n\t\ts: string,\r\n\t\tre: RegExp\r\n\t): [string, string | null] {\r\n\t\tconst match = re.exec(s)\r\n\t\tif (match) {\r\n\t\t\treturn [this.removeRange(s, match.index, match[0].length), match[1]]\r\n\t\t}\r\n\t\treturn [s, null]\r\n\t}\r\n\r\n\tprivate static extractAllFeatures(s: string, re: RegExp): [string, string[]] {\r\n\t\tconst features = []\r\n\t\tlet result = this.extractFeature(s, re)\r\n\r\n\t\twhile (result[1]) {\r\n\t\t\ts = result[0]\r\n\t\t\tfeatures.push(result[1] )\r\n\t\t\tresult = this.extractFeature(s, re)\r\n\t\t}\r\n\t\treturn [s, features]\r\n\t}\r\n\r\n\tprivate static removeRange(\r\n\t\ts: string,\r\n\t\tstartIndex: number,\r\n\t\tlength: number\r\n\t): string {\r\n\t\treturn (\r\n\t\t\ts.slice(0, Math.max(0, startIndex)) + s.slice(Math.max(0, startIndex + length))\r\n\t\t).trim()\r\n\t}\r\n\r\n\tprivate static readonly _zeroPad = (num: number, numZeros: number): string => {\r\n\t\tconst n = Math.abs(num)\r\n\t\tconst zeros = Math.max(0, numZeros - Math.floor(n).toString().length)\r\n\t\tlet zeroString = (10**zeros).toString().slice(1)\r\n\t\tif (num < 0) {\r\n\t\t\tzeroString = `-${ zeroString}`\r\n\t\t}\r\n\r\n\t\treturn zeroString + n\r\n\t}\r\n\r\n\tpublic static setIsCompleted(todo: TodoItem, isCompleted: boolean): TodoItem {\r\n\t\t// todo, more robust\r\n\t\tif (todo.isCompleted !== isCompleted) {\r\n\t\t\ttodo.raw = isCompleted ? `x ${ this._todayInYyyyMmDd() } ${ todo.raw}` : todo.raw.replace(/^x\\s\\d{2,4}(?:-\\d{1,2}){2}\\s+/i, '');\r\n\t\t\ttodo.isCompleted = isCompleted\r\n\t\t}\r\n\t\treturn todo\r\n\t}\n\r\n\tpublic fromFile(fileContents: string): TodoItem[] {\r\n\t\tconst todos = fileContents\r\n\t\t\t.split('\\n')\r\n\t\t\t.map(line => line.trim())\r\n\t\t\t.filter(line => !!line)\r\n\t\t\t.map((raw, lineNumber) =>\r\n\t\t\t\tTodoFileSerializer.todoItemFromRaw(raw, lineNumber)\r\n\t\t\t)\r\n\r\n\t\treturn todos\r\n\t}\r\n\r\n\tpublic toFile(todos: TodoItem[]): string {\r\n\t\tlet result = ''\r\n\t\tconst todosCopy = [...todos] // .sort() is an in-place sort, don't want to mutate redux immer state\r\n\t\tconst sortedByLineNumber = todosCopy.sort(\r\n\t\t\t(todoA, todoB) => todoA.lineNumber - todoB.lineNumber\r\n\t\t)\r\n\r\n\t\tfor (const t of sortedByLineNumber) {\r\n\t\t\tresult += `${t.raw }\\n`\r\n\t\t}\r\n\r\n\t\treturn result\r\n\t}\r\n\r\n\tpublic applyOperation(\r\n\t\toperation: TodoOperation,\r\n\t\ttodos: TodoItem[]\r\n\t): { todos: TodoItem[]; operationApplication: TodoOperationApplication } {\r\n\t\tlet newList: TodoItem[]\r\n\r\n\t\tswitch (operation.type) {\n\t\tcase TodoOperationType.SET_IS_COMPLETED: {\r\n\t\t\tnewList = this.createWithReplacedItem(operation.todoId, todos, todo =>\r\n\t\t\t\tTodoFileSerializer.setIsCompleted(todo, operation.isCompleted)\r\n\t\t\t)\r\n\t\t\n\t\tbreak;\n\t\t}\n\t\tcase TodoOperationType.DELETE_TODO: {\r\n\t\t\t// this leaves missing line numbers, does that mattter?\r\n\t\t\tnewList = todos.filter(t => t.id != operation.todoId)\r\n\t\t\n\t\tbreak;\n\t\t}\n\t\tcase TodoOperationType.UPSERT_TODO: {\r\n\t\t\tconst opTodo = operation.todo\r\n\t\t\tif (todos.find(t => t.id === opTodo.id)) {\r\n\t\t\t\tnewList = this.createWithReplacedItem(\r\n\t\t\t\t\toperation.todo.id,\r\n\t\t\t\t\ttodos,\r\n\t\t\t\t\ttodo => {\r\n\t\t\t\t\t\tconst updated = {\r\n\t\t\t\t\t\t\t...todo,\r\n\t\t\t\t\t\t\tbody: opTodo.body,\r\n\t\t\t\t\t\t\tpriority: opTodo.priority,\r\n\t\t\t\t\t\t\tcontexts: opTodo.contexts,\r\n\t\t\t\t\t\t\tprojects: opTodo.projects\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tupdated.raw = TodoFileSerializer.buildRawFromTodo(operation.todo)\r\n\t\t\t\t\t\treturn updated\r\n\t\t\t\t\t}\r\n\t\t\t\t)\r\n\t\t\t} else {\r\n\t\t\t\tconst newLineNumber = Math.max(...todos.map(t => t.lineNumber)) + 1\r\n\t\t\t\tconst newTodo = buildTodoItem({\r\n\t\t\t\t\t...operation.todo,\r\n\t\t\t\t\tlineNumber: newLineNumber,\r\n\t\t\t\t\traw: TodoFileSerializer.buildRawFromTodo(operation.todo)\r\n\t\t\t\t})\r\n\t\t\t\tnewList = [...todos, newTodo]\r\n\t\t\t}\r\n\t\t\n\t\tbreak;\n\t\t}\n\t\tdefault: {\r\n\t\t\tthrow new Error('unknown operation')\r\n\t\t}\n\t\t}\r\n\r\n\t\treturn { todos: newList, operationApplication: { operation } }\r\n\t}\r\n\r\n\tprivate createWithReplacedItem(\r\n\t\ttodoId: string,\r\n\t\ttodos: TodoItem[],\r\n\t\tapply: (todo: TodoItem) => TodoItem\r\n\t) {\r\n\t\tconst targetIndex = todos.findIndex(t => t.id === todoId)\r\n\t\tif (targetIndex === -1) {\r\n\t\t\tthrow new Error()\r\n\t\t}\r\n\t\tconst originalTodo = todos[targetIndex]\r\n\r\n\t\tlet modifiedItem = buildTodoItem(originalTodo)\r\n\t\tmodifiedItem = apply(modifiedItem)\r\n\r\n\t\tconst newList = [...todos\r\n\t\t\t.slice(0, targetIndex), modifiedItem]\r\n\t\t\t.concat(todos.slice(targetIndex + 1, todos.length))\r\n\r\n\t\treturn newList\r\n\t}\r\n}\r\n","import { createAction, createReducer } from '@reduxjs/toolkit'\r\nimport { logger } from 'lib/logger'\r\nimport type { TodoItem, TodoOperation, TodoOperationApplication } from 'lib/todotxtformat/model'\r\nimport TodoFileSerializer from 'lib/todotxtformat/todoFormat'\r\nimport { v4 as uuidv4 } from 'uuid'\r\nimport type { AppliedTodoOperationSet } from './model'\r\nimport { INITIAL_STATE, INITIAL_USER_SETTINGS } from './model'\r\nimport { viewDefinitionActions, viewDefinitionReducers } from './viewDefinitions'\r\n\r\nexport const SET_LAST_USED_FILTER = '@txtodo/SET_LAST_USED_FILTER'\r\n\r\nexport const startup = createAction('@txtodo/STARTUP')\r\n\r\nexport const setUser = createAction<{\r\n\taccessToken: string\r\n\taccessTokenExpiresAt: number\r\n\trefreshToken: string\r\n\tclientId: string\r\n}>('@txtodo/SET_USER')\r\n\r\nexport const saveRefreshedAuth = createAction('@txtodo/SAVE_REFRESHED_AUTH')\r\n\r\nexport const logout = createAction('@txtodo/LOGOUT')\r\n\r\nexport const mutateTodos = createAction<{\r\n\tops: TodoOperation[]\r\n}>('@txtodo/MUTATE_TODOS')\r\n\r\nexport const syncBegin = createAction(\r\n\t'@txtodo/SYNC_BEGIN',\r\n\t(payload: { skipIfSyncedSinceMs?: number; syncId?: string }) => ({\r\n\t\tpayload: {\r\n\t\t\tskipIfSyncedSinceMs: payload.skipIfSyncedSinceMs,\r\n\t\t\tsyncId: payload.syncId ?? uuidv4()\r\n\t\t}\r\n\t})\r\n)\r\n\r\nexport const syncWork = createAction<{\r\n\tsyncId: string\r\n}>('@txtodo/SYNC_WORK')\r\n\r\nexport const syncComplete = createAction<{\r\n\tsyncId: string\r\n\ttodoFileContent: string\r\n\ttodos: TodoItem[] | null\r\n\tlocalRevisionId: string | null\r\n\tremoteRevisionId: string\r\n\thadConflict: boolean\r\n\thadUnresolvableConflict: boolean\r\n}>('@txtodo/SYNC_COMPLETE')\r\n\r\nexport const syncFail = createAction<{\r\n\tsyncId: string\r\n\tcode?: string\r\n\tmessage?: string\r\n}>('@txtodo/SYNC_FAIL')\r\n\r\nexport const setLastUsedViewDefinition = createAction<{\r\n\tviewDefinitionId: string\r\n}>('@txtodo/SET_LAST_USED_VIEW_DEFINITION')\r\n\r\nexport const { setViewDefinition } = viewDefinitionActions\r\n\r\nexport default createReducer(INITIAL_STATE, builder => {\r\n\tbuilder.addCase(setViewDefinition, (state, action) => {\r\n\t\tviewDefinitionReducers.setViewDefinition(state.viewDefinitions, action.payload)\r\n\t})\r\n\r\n\tbuilder.addCase(setUser, (state, { payload }) => {\r\n\t\tif (state.user) {\r\n\t\t\tlogger.info('LOGIN: A user is already logged in, should log out first.')\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tlogger.info('LOGIN: Setting new user token.')\r\n\r\n\t\tstate.user = {\r\n\t\t\tname: 'bob',\r\n\t\t\taccessToken: payload.accessToken,\r\n\t\t\taccessTokenExpiresAt: payload.accessTokenExpiresAt,\r\n\t\t\trefreshToken: payload.refreshToken,\r\n\t\t\tclientId: payload.clientId,\r\n\t\t\tsettings: INITIAL_USER_SETTINGS\r\n\t\t}\r\n\t\t// state.router = '/home'\r\n\t})\r\n\r\n\tbuilder.addCase(saveRefreshedAuth, (state, { payload }) => {\r\n\t\tif (!payload) {\r\n\t\t\tlogger.info('SAVE_REFRESHED_AUTH: payload falsy')\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tif (!state.user) {\r\n\t\t\tlogger.info('SAVE_REFRESHED_AUTH: user falsy')\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tconst refreshedAccessToken: string | undefined = payload.accessToken\r\n\t\tconst { accessTokenExpiresAt } = payload\r\n\r\n\t\tif (refreshedAccessToken && accessTokenExpiresAt) {\r\n\t\t\tlogger.info('SAVE_REFRESHED_AUTH: Saving refreshed accessToken')\r\n\t\t\tstate.user.accessToken = refreshedAccessToken\r\n\t\t\tstate.user.accessTokenExpiresAt = accessTokenExpiresAt\r\n\t\t}\r\n\t})\r\n\r\n\tbuilder.addCase(logout, (state, action) => {\r\n\t\tlogger.info(`LOGOUT: Setting back to INITIAL_STATE`)\r\n\t\treturn { ...INITIAL_STATE, router: '/login' }\r\n\t})\r\n\r\n\tbuilder.addCase(syncBegin, (state, action) => {\r\n\t\tif (!state.user || !state.user.accessToken) {\r\n\t\t\tlogger.verbose(`SYNC: Not starting sync because user or token is falsy`)\r\n\t\t\treturn\r\n\t\t}\r\n\t\tif (\r\n\t\t\tstate.synchronization.inProgress &&\r\n\t\t\tstate.synchronization.syncId !== action.payload.syncId\r\n\t\t) {\r\n\t\t\tlogger.verbose(\r\n\t\t\t\t`SYNC: Not starting sync because another sync ${action.payload.syncId} is already in progress.`\r\n\t\t\t)\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tif (\r\n\t\t\taction.payload.skipIfSyncedSinceMs &&\r\n\t\t\tDate.now() - state.synchronization.lastCompleteTime < action.payload.skipIfSyncedSinceMs &&\r\n\t\t\tstate.synchronization.syncId !== action.payload.syncId\r\n\t\t) {\r\n\t\t\tlogger.verbose(\r\n\t\t\t\t`SYNC: Not starting sync because last synchonization was at ${state.synchronization.lastCompleteTime}.`\r\n\t\t\t)\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tlogger.info(\r\n\t\t\t`SYNC: Will start. Setting state.synchronization: { inProgress: true, syncId: ${action.payload.syncId} }`\r\n\t\t)\r\n\r\n\t\tstate.synchronization.syncId = action.payload.syncId\r\n\t\tstate.synchronization.inProgress = true\r\n\t})\r\n\r\n\tbuilder.addCase(syncWork, (state, action) => {\r\n\t\tlet { localRevisionId } = state.synchronization\r\n\t\tlet lastHistoryEntryHasRemoteRevisionId = false\r\n\t\tif (localRevisionId) {\r\n\t\t\tlogger.verbose(\r\n\t\t\t\t`SYNC: Use existing intermediate localRevisionId from previous sync attempt: ${localRevisionId}.`\r\n\t\t\t)\r\n\t\t} else if (state.history.length > 0) {\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-magic-numbers\r\n\t\t\tconst lastHistoryEntry = (state.history as any).at(-1)\r\n\t\t\tif (lastHistoryEntry === undefined) {\r\n\t\t\t\tthrow new Error('undefined lastHistoryEntry')\r\n\t\t\t}\r\n\t\t\tlocalRevisionId = lastHistoryEntry.localRevisionId\r\n\t\t\tlastHistoryEntryHasRemoteRevisionId = !!lastHistoryEntry.remoteRevisionId\r\n\t\t\tlogger.verbose(`SYNC: Will use most recent localRevisionId from history: ${localRevisionId}.`)\r\n\t\t} else {\r\n\t\t\tlogger.verbose(`SYNC: Will use empty localRevisionId.`)\r\n\t\t}\r\n\r\n\t\tlet { remoteRevisionId } = state.synchronization\r\n\t\tif (remoteRevisionId) {\r\n\t\t\tlogger.verbose(\r\n\t\t\t\t`SYNC: Will use existing intermediate remoteRevisionId from previous sync attempt.`\r\n\t\t\t)\r\n\t\t} else if (state.history.length > 0 && !lastHistoryEntryHasRemoteRevisionId) {\r\n\t\t\tconst historyEntryWithRemoteRevision = [...state.history]\r\n\t\t\t\t.reverse()\r\n\t\t\t\t.find(aos => !!aos.remoteRevisionId)\r\n\t\t\tif (historyEntryWithRemoteRevision) {\r\n\t\t\t\tremoteRevisionId = historyEntryWithRemoteRevision.remoteRevisionId || null\r\n\t\t\t\tlogger.verbose(\r\n\t\t\t\t\t`SYNC: Will attempt to write using most recent remoteRevisionId ${remoteRevisionId}.`\r\n\t\t\t\t)\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tlogger.verbose(`SYNC: Nothing to write, will read.`)\r\n\t\t}\r\n\r\n\t\tlet { todos } = state.synchronization\r\n\t\tif (todos) {\r\n\t\t\tlogger.verbose(`SYNC: will use existing intermediate todos from previous sync attempt.`)\r\n\t\t} else {\r\n\t\t\ttodos = state.list\r\n\t\t\tlogger.verbose(`SYNC: will use todos from active list.`)\r\n\t\t}\r\n\r\n\t\tlogger.info(\r\n\t\t\t`SYNC: Setting sync inProgress to state with syncId=${action.payload.syncId},` +\r\n\t\t\t\t` localRevisionId=${localRevisionId}, hasRemoteRevisionId=${!!remoteRevisionId}`\r\n\t\t)\r\n\r\n\t\tstate.synchronization.localRevisionId = localRevisionId\r\n\t\tstate.synchronization.remoteRevisionId = remoteRevisionId\r\n\t\tstate.synchronization.todos = todos\r\n\t})\r\n\r\n\tbuilder.addCase(syncComplete, (state, { payload }) => {\r\n\t\tif (state.history.some(h => h.remoteRevisionId === payload.remoteRevisionId)) {\r\n\t\t\tlogger.verbose(\r\n\t\t\t\t`SYNC: Not changing list or history on sync complete because remoteRevisionId already in history`\r\n\t\t\t)\r\n\t\t\tstate.synchronization = {\r\n\t\t\t\t...INITIAL_STATE.synchronization,\r\n\t\t\t\tlastCompleteTime: Date.now(),\r\n\t\t\t\tlastSyncedTodoFileContent: payload.todoFileContent\r\n\t\t\t}\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tlet lastStateLatestLocalRevision: string | null = null\r\n\t\tif (state.history.length > 0) {\r\n\t\t\tconst lastEntry = (state.history as any).at(-1)\r\n\t\t\tif (lastEntry === undefined) {\r\n\t\t\t\tthrow new Error('undefined in history with length > 0')\r\n\t\t\t}\r\n\t\t\tlastStateLatestLocalRevision = lastEntry.localRevisionId\r\n\t\t}\r\n\r\n\t\tif (lastStateLatestLocalRevision !== payload.localRevisionId) {\r\n\t\t\tlogger.verbose(\r\n\t\t\t\t`SYNC: Will trigger another round of sync because ` +\r\n\t\t\t\t\t`new localRevisionId ${lastStateLatestLocalRevision} has supplanted ${payload.localRevisionId}`\r\n\t\t\t)\r\n\r\n\t\t\tstate.synchronization.localRevisionId = lastStateLatestLocalRevision\r\n\t\t\t;(state.synchronization.remoteRevisionId = payload.hadConflict\r\n\t\t\t\t? 'deadbeefdeadbeef'\r\n\t\t\t\t: payload.remoteRevisionId),\r\n\t\t\t\t(state.synchronization.todos = state.list)\r\n\t\t\tstate.synchronization.lastSyncedTodoFileContent = payload.todoFileContent\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tif (state.history.length > 0) {\r\n\t\t\tconst targetIndex = state.history.findIndex(\r\n\t\t\t\tt => t.localRevisionId === payload.localRevisionId\r\n\t\t\t)\r\n\t\t\tif (targetIndex === -1) {\r\n\t\t\t\tthrow new Error(\r\n\t\t\t\t\t`Could not find matching localRevisionId ${payload.localRevisionId} in history`\r\n\t\t\t\t)\r\n\t\t\t}\r\n\t\t\tconst historyEntryToUpdate = state.history[targetIndex]\r\n\r\n\t\t\tlogger.verbose(\r\n\t\t\t\t`SYNC: Adding new remoteRevisionId to history entry at ` +\r\n\t\t\t\t\t`index ${targetIndex} with localRevisionId ${historyEntryToUpdate.localRevisionId}`\r\n\t\t\t)\r\n\r\n\t\t\thistoryEntryToUpdate.remoteRevisionId = payload.remoteRevisionId\r\n\t\t} else {\r\n\t\t\tlogger.verbose(`History is empty at sync complete so adding new entry.`)\r\n\t\t\tstate.history.push({\r\n\t\t\t\tremoteRevisionId: payload.remoteRevisionId,\r\n\t\t\t\tlocalRevisionId: uuidv4(),\r\n\t\t\t\toperationApplications: [],\r\n\t\t\t\tlocalTime: Date.now()\r\n\t\t\t})\r\n\t\t}\r\n\r\n\t\tif (!payload.todos) {\r\n\t\t\tthrow new Error(`consistency error: payload.todos is falsy`)\r\n\t\t}\r\n\r\n\t\tstate.list = payload.todos\r\n\t\tstate.synchronization = {\r\n\t\t\t...INITIAL_STATE.synchronization,\r\n\t\t\tlastCompleteTime: Date.now(),\r\n\t\t\tlastSyncedTodoFileContent: payload.todoFileContent\r\n\t\t}\r\n\t})\r\n\r\n\tbuilder.addCase(syncFail, (state, action) => {\r\n\t\tstate.synchronization = {\r\n\t\t\t...INITIAL_STATE.synchronization,\r\n\t\t\tlastCompleteTime: state.synchronization.lastCompleteTime,\r\n\t\t\tlastSyncedTodoFileContent: state.synchronization.lastSyncedTodoFileContent\r\n\t\t}\r\n\t})\r\n\r\n\tbuilder.addCase(mutateTodos, (state, action) => {\r\n\t\tlet todos: TodoItem[] = state.list\r\n\t\tlet operationApplication: TodoOperationApplication\r\n\t\tconst operationApplications: TodoOperationApplication[] = []\r\n\r\n\t\tconst todoFormat = new TodoFileSerializer()\r\n\t\tfor (const op of action.payload.ops) {\r\n\t\t\t;({ todos, operationApplication } = todoFormat.applyOperation(op, todos))\r\n\t\t\toperationApplications.push(operationApplication)\r\n\t\t}\r\n\r\n\t\tconst appliedOperationSet: AppliedTodoOperationSet = {\r\n\t\t\tlocalRevisionId: uuidv4(),\r\n\t\t\toperationApplications,\r\n\t\t\tlocalTime: Date.now()\r\n\t\t}\r\n\r\n\t\tconst indexOfLastRemoteRevision = [...state.history]\r\n\t\t\t.reverse()\r\n\t\t\t.findIndex(aos => !!aos.remoteRevisionId)\r\n\t\tif (indexOfLastRemoteRevision === -1) {\r\n\t\t\tthrow new Error(`Could not find any remoteRevisionId when saving.`)\r\n\t\t} else {\r\n\t\t\tstate.history = state.history\r\n\t\t\t\t.slice(Math.max(0, indexOfLastRemoteRevision - 99), state.history.length)\r\n\t\t\t\t.concat(appliedOperationSet)\r\n\t\t}\r\n\r\n\t\tlogger.verbose(\r\n\t\t\t`Applied todolist mutation with ${appliedOperationSet.operationApplications.length} ops ` +\r\n\t\t\t\t` creating new localRevisionId ${appliedOperationSet.localRevisionId}`\r\n\t\t)\r\n\r\n\t\tstate.list = todos\r\n\t})\r\n\r\n\tbuilder.addCase(setLastUsedViewDefinition, (state, action) => {\r\n\t\tif (state.interface.lastViewDefinitionId != action.payload.viewDefinitionId) {\r\n\t\t\tstate.interface.lastViewDefinitionId = action.payload.viewDefinitionId\r\n\t\t}\r\n\t})\r\n})\r\n","import { logger } from 'lib/logger'\r\nimport type { ReactElement } from 'react'\r\nimport { useEffect, useLayoutEffect, useRef } from 'react'\r\nimport { useAppDispatch, useAppSelector } from 'store/hooks'\r\nimport { syncBegin } from 'store/reducers'\r\n\r\nconst useEventListener = (\r\n\teventName: string,\r\n\telement: EventTarget | null,\r\n\thandler: EventListener,\r\n\toptions?: AddEventListenerOptions\r\n): void => {\r\n\tconst savedHandler = useRef(handler)\r\n\tconst { capture, passive, once } = options ?? {}\r\n\r\n\tuseLayoutEffect(() => {\r\n\t\tsavedHandler.current = handler\r\n\t}, [handler])\r\n\r\n\tuseEffect(() => {\r\n\t\tconst isSupported = element?.addEventListener\r\n\t\tif (!isSupported) {\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tconst eventListener = (event: Event): void => savedHandler?.current(event) // eslint-disable-line @typescript-eslint/no-unnecessary-condition\r\n\r\n\t\tconst opts = { capture, passive, once }\r\n\t\telement.addEventListener(eventName, eventListener, opts)\r\n\t\t// console.log('attached eventListener to window')\r\n\t\treturn (): void => {\r\n\t\t\telement.removeEventListener(eventName, eventListener, opts)\r\n\t\t\t// console.log('removed eventListener to window')\r\n\t\t}\r\n\t}, [eventName, element, capture, passive, once])\r\n}\r\n\r\nfunction useInterval(callback: () => void, delay: number | null): void {\r\n\tconst savedCallback = useRef(callback)\r\n\r\n\t// Remember the latest callback if it changes.\r\n\tuseLayoutEffect(() => {\r\n\t\tsavedCallback.current = callback\r\n\t}, [callback])\r\n\r\n\t// Set up the interval.\r\n\tuseEffect(() => {\r\n\t\t// Don't schedule if no delay is specified.\r\n\t\t// Note: 0 is a valid value for delay.\r\n\t\tif (!delay && delay !== 0) {\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tconst id = setInterval(() => savedCallback.current(), delay)\r\n\t\tconsole.log(`setInterval ${id}`)\r\n\r\n\t\treturn () => {\r\n\t\t\tclearInterval(id)\r\n\t\t\tconsole.log(`clearInterval ${id}`)\r\n\t\t}\r\n\t}, [delay])\r\n}\r\n\r\nexport default function BackgroundSync(): ReactElement | null {\r\n\tconst syncId = useAppSelector(state => state.synchronization.syncId)\r\n\tconst dispatch = useAppDispatch()\r\n\r\n\tuseEffect(() => {\r\n\t\tconst newSyncId = syncId === '' ? undefined : syncId\r\n\t\tdispatch(\r\n\t\t\tsyncBegin({\r\n\t\t\t\tsyncId: newSyncId,\r\n\t\t\t\tskipIfSyncedSinceMs: 10 * 60 * 1000 // 10 minutes\r\n\t\t\t})\r\n\t\t)\r\n\t}, [])\r\n\r\n\tuseEventListener('visibilitychange', window, () => {\r\n\t\tlogger.verbose(\r\n\t\t\t`BACKGROUND_SYNC: visibilitychange - WillDispatch=${!document.hidden.toString()}, hidden=${\r\n\t\t\t\tdocument.hidden\r\n\t\t\t}, visibilityState=${document.visibilityState}` + ` hasFocus=${document.hasFocus()}`\r\n\t\t)\r\n\t\tif (!document.hidden) {\r\n\t\t\tdispatch(\r\n\t\t\t\tsyncBegin({\r\n\t\t\t\t\tsyncId: undefined,\r\n\t\t\t\t\tskipIfSyncedSinceMs: 10 * 60 * 1000 // 10 minutes\r\n\t\t\t\t})\r\n\t\t\t)\r\n\t\t}\r\n\t})\r\n\r\n\tuseInterval(() => {\r\n\t\tlogger.verbose(\r\n\t\t\t`BACKGROUND_SYNC: Interval - WillDispatch=${!document.hidden}, hidden=${\r\n\t\t\t\tdocument.hidden\r\n\t\t\t}, visibilityState=${document.visibilityState}` + ` hasFocus=${document.hasFocus()}`\r\n\t\t)\r\n\t\tif (!document.hidden) {\r\n\t\t\tdispatch(\r\n\t\t\t\tsyncBegin({\r\n\t\t\t\t\tsyncId: undefined,\r\n\t\t\t\t\tskipIfSyncedSinceMs: 10 * 60 * 1000 // 10 minutes\r\n\t\t\t\t})\r\n\t\t\t)\r\n\t\t}\r\n\t}, 10 * 60 * 1000)\r\n\r\n\treturn null\r\n}\r\n","import type { ReactNode } from 'react';\nimport React from 'react'\r\nimport { logger } from '../lib/logger'\r\n\r\nexport class ErrorBoundary extends React.Component<{ children: ReactNode }, { hasError: boolean }> {\r\n\tconstructor(props: { children: ReactNode }) {\r\n\t\tsuper(props)\r\n\t\tthis.state = { hasError: false }\r\n\t}\r\n\r\n\tstatic getDerivedStateFromError(error: any) {\r\n\t\t// Update state so the next render will show the fallback UI.\r\n\t\treturn { hasError: true }\r\n\t}\r\n\r\n\tcomponentDidCatch(error: any, errorInfo: any) {\r\n\t\tlogger.error(error)\r\n\t\tlogger.error(errorInfo)\r\n\t}\r\n\r\n\trender() {\r\n\t\tif (this.state.hasError) {\r\n\t\t\t// You can render any custom fallback UI\r\n\t\t\treturn

Something went wrong

\r\n\t\t}\r\n\r\n\t\treturn this.props.children\r\n\t}\r\n}\r\n","import { createTheme } from '@mui/material/styles'\r\n\r\nconst appTheme = createTheme({\r\n\tpalette: {\r\n\t\tprimary: {\r\n\t\t\tmain: '#16a085'\r\n\t\t}\r\n\t\t// #ffc107\r\n\t}\r\n})\r\n\r\nexport default appTheme\r\n","import type { DropboxAuthOptions, DropboxResponse, files } from 'dropbox'\r\nimport { Dropbox, DropboxAuth, DropboxResponseError } from 'dropbox'\r\nimport type {\r\n\tCloudConnection,\r\n\tCloudFile,\r\n\tCloudFileProvider,\r\n\tCloudProviderResult,\r\n\tPutCloudFile\r\n} from './types'\r\n\r\ninterface DropboxPkceCodeResult {\r\n\taccess_token: string\r\n\trefresh_token: string\r\n\taccount_id: string\r\n\ttoken_type: string\r\n\tuid: string\r\n\texpires_in: number\r\n}\r\n\r\ninterface DropboxAuthTokens {\r\n\trefreshToken: string\r\n\taccessToken: string\r\n\tclientId: string\r\n\taccessTokenExpiresAt: number\r\n}\r\n\r\nexport class DropboxFileProvider implements CloudFileProvider {\r\n\tprivate readonly dropboxClient: Dropbox\r\n\r\n\tpublic constructor(\r\n\t\tprivate readonly tokens: DropboxAuthTokens,\r\n\t\tprivate readonly logger?: (message: string) => void\r\n\t) {\r\n\t\tconst authOptions: DropboxAuthOptions = {\r\n\t\t\taccessToken: this.tokens.accessToken,\r\n\t\t\trefreshToken: this.tokens.refreshToken,\r\n\t\t\tclientId: this.tokens.clientId,\r\n\t\t\taccessTokenExpiresAt: new Date(this.tokens.accessTokenExpiresAt)\r\n\t\t}\r\n\t\tthis.dropboxClient = new Dropbox({ auth: new DropboxAuth(authOptions) })\r\n\t}\r\n\r\n\tpublic async getFile(\r\n\t\tpath: string\r\n\t): Promise>> {\r\n\t\tconst resp = await this.dropboxClient.filesDownload({ path })\r\n\t\t// console.log(resp)\r\n\r\n\t\tthis.log(`DROPBOX: GET got ${resp.status}`)\r\n\r\n\t\tif (resp.status === 409) {\r\n\t\t\t// console.log(resp)\r\n\t\t\tthrow new Error('notfound')\r\n\t\t}\r\n\r\n\t\tif (resp.status === 401) {\r\n\t\t\tthrow new Error('401')\r\n\t\t}\r\n\r\n\t\tif (resp.status !== 200) {\r\n\t\t\tthrow new Error(resp.status.toString())\r\n\t\t}\r\n\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\r\n\t\tconst content = await ((resp.result as any).fileBlob as Blob).text()\r\n\t\tconst version = resp.result.rev\r\n\r\n\t\tconst result = { content, version }\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\r\n\t\tconst refreshedAuth = this.computeRefreshedAuth((this.dropboxClient as any).auth)\r\n\r\n\t\treturn { result, refreshedAuth }\r\n\t}\r\n\r\n\tprivate computeRefreshedAuth(auth: DropboxAuth): Partial | undefined {\r\n\t\tconst accessToken = auth.getAccessToken()\r\n\t\tif (accessToken !== this.tokens.accessToken) {\r\n\t\t\tthis.tokens.accessToken = accessToken\r\n\t\t\treturn {\r\n\t\t\t\taccessToken,\r\n\t\t\t\taccessTokenExpiresAt: auth.getAccessTokenExpiresAt().getTime()\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn undefined\r\n\t}\r\n\r\n\tprivate static getWriteMode(rev: string | null): files.UploadArg['mode'] {\r\n\t\tif (rev) {\r\n\t\t\treturn {\r\n\t\t\t\t'.tag': 'update',\r\n\t\t\t\tupdate: rev\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn { '.tag': 'add' }\r\n\t}\r\n\r\n\tpublic async putFile(\r\n\t\tpath: string,\r\n\t\tfile: PutCloudFile\r\n\t): Promise>> {\r\n\t\tconst mode = DropboxFileProvider.getWriteMode(file.version)\r\n\r\n\t\tlet responseStatus = 500\r\n\t\ttry {\r\n\t\t\tconst resp = await DropboxFileProvider.retryTypeErrorFailedToFetch(async () =>\r\n\t\t\t\tthis.dropboxClient.filesUpload({\r\n\t\t\t\t\tpath,\r\n\t\t\t\t\tmode,\r\n\t\t\t\t\tautorename: false,\r\n\t\t\t\t\tmute: true,\r\n\t\t\t\t\tcontents: file.content\r\n\t\t\t\t})\r\n\t\t\t)\r\n\t\t\tthis.log(`DROPBOX: GET got ${resp.status}`)\r\n\t\t\tresponseStatus = resp.status\r\n\r\n\t\t\tconst result = resp.result.rev\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any\r\n\t\t\tconst refreshedAuth = this.computeRefreshedAuth((this.dropboxClient as any).auth)\r\n\r\n\t\t\treturn { result, refreshedAuth }\r\n\t\t} catch (error) {\r\n\t\t\tif (error instanceof DropboxResponseError) {\r\n\t\t\t\tresponseStatus = error.status\r\n\t\t\t\t// console.log(error)\r\n\t\t\t} else {\r\n\t\t\t\tthrow error\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.log(`DROPBOX: Upload got ${responseStatus}`)\r\n\r\n\t\tif (responseStatus === 409) {\r\n\t\t\tthrow new Error('conflict')\r\n\t\t}\r\n\r\n\t\tif (responseStatus === 401) {\r\n\t\t\tthrow new Error('401')\r\n\t\t}\r\n\r\n\t\tif (responseStatus !== 200) {\r\n\t\t\tthrow new Error(`Upload got ${responseStatus}`)\r\n\t\t}\r\n\t\t/*\r\n if (error.stack.includes('[507]')){\r\n throw new Meteor.Error(\r\n 'bad-request',\r\n 'Dropbox was unable to complete this action because of a storage quota limit.');\r\n } */\r\n\t\tthrow new Error(responseStatus.toString())\r\n\t}\r\n\r\n\tprivate log(message: string): void {\r\n\t\tif (this.logger) {\r\n\t\t\tthis.logger(message)\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static async retryTypeErrorFailedToFetch(action: () => Promise): Promise {\r\n\t\tlet lastError: unknown\r\n\t\tfor (let i = 0; i < 3; i += 1) {\r\n\t\t\ttry {\r\n\t\t\t\t// eslint-disable-next-line no-await-in-loop\r\n\t\t\t\treturn await action()\r\n\t\t\t} catch (error) {\r\n\t\t\t\tlastError = error\r\n\t\t\t\tif (error instanceof TypeError && error.message === 'Failed to fetch') {\r\n\t\t\t\t\t// eslint-disable-next-line no-continue\r\n\t\t\t\t\tcontinue\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthrow error\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tthrow lastError\r\n\t}\r\n}\r\n\r\nexport class DropboxOauthPkceProvider {\r\n\tprivate static readonly SESSION_STORAGE_KEY = 'dropboxoauth.codeVerifier'\r\n\r\n\tpublic constructor(\r\n\t\tprivate readonly clientId: string,\r\n\t\tprivate readonly redirectUri: string,\r\n\t\tprivate readonly logger?: (message: string) => void\r\n\t) {}\r\n\r\n\tpublic async redirectToLogin(): Promise {\r\n\t\tconst dropboxAuth = this.getDropboxAuth()\r\n\r\n\t\tconst authUrl = await dropboxAuth.getAuthenticationUrl(\r\n\t\t\tthis.redirectUri,\r\n\t\t\tundefined,\r\n\t\t\t'code',\r\n\t\t\t'offline',\r\n\t\t\tundefined,\r\n\t\t\tundefined,\r\n\t\t\ttrue\r\n\t\t)\r\n\r\n\t\twindow.sessionStorage.setItem(\r\n\t\t\tDropboxOauthPkceProvider.SESSION_STORAGE_KEY,\r\n\t\t\tdropboxAuth.getCodeVerifier()\r\n\t\t)\r\n\t\twindow.open(authUrl.valueOf(), '_self', 'beforeload=yes,location=yes')\r\n\t}\r\n\r\n\tpublic async handleOauthResponse(\r\n\t\tsearchParams: URLSearchParams\r\n\t): Promise> {\r\n\t\tconst code = searchParams.get('code')\r\n\t\tif (!code) {\r\n\t\t\tthrow new Error('No code in search params')\r\n\t\t}\r\n\r\n\t\tconst dropboxAuth = this.getDropboxAuth()\r\n\r\n\t\tconst codeVerifier = window.sessionStorage.getItem(DropboxOauthPkceProvider.SESSION_STORAGE_KEY)\r\n\t\tif (!codeVerifier) {\r\n\t\t\tthrow new Error('No dropbox codeVerifier in session storage')\r\n\t\t}\r\n\t\tdropboxAuth.setCodeVerifier(codeVerifier)\r\n\r\n\t\tconst accessTokenResponse = (await dropboxAuth.getAccessTokenFromCode(\r\n\t\t\tthis.redirectUri,\r\n\t\t\tcode\r\n\t\t)) as DropboxResponse\r\n\t\t// console.log(accessTokenResponse)\r\n\r\n\t\twindow.sessionStorage.removeItem(DropboxOauthPkceProvider.SESSION_STORAGE_KEY)\r\n\r\n\t\tconst tokens: DropboxAuthTokens = {\r\n\t\t\trefreshToken: accessTokenResponse.result.refresh_token,\r\n\t\t\taccessToken: accessTokenResponse.result.access_token,\r\n\t\t\tclientId: this.clientId,\r\n\t\t\taccessTokenExpiresAt: Date.now() + accessTokenResponse.result.expires_in * 1000\r\n\t\t}\r\n\r\n\t\treturn { id: accessTokenResponse.result.account_id, auth: tokens }\r\n\r\n\t\t// const testInstance = new Dropbox(tokens)\r\n\r\n\t\t// await (await testInstance.filesGetMetadata({ path: '/todo/todo.txt' }))\r\n\r\n\t\t/* dropboxAuth.setAccessToken(accessTokenResponse.result.access_token)\r\n\t\t\t\tvar dbx = new Dropbox.Dropbox({\r\n\t\t\t\t\tauth: dbxAuth\r\n\t\t\t\t}) */\r\n\t}\r\n\r\n\tprivate getDropboxAuth(): DropboxAuth {\r\n\t\treturn new DropboxAuth({\r\n\t\t\tclientId: this.clientId\r\n\t\t})\r\n\t}\r\n}\r\n","/* eslint-disable class-methods-use-this */\r\n/* eslint-disable no-await-in-loop */\r\n/* eslint-disable @typescript-eslint/no-parameter-properties */\r\nimport { diff3Merge } from 'node-diff3'\r\nimport type { CloudFile, CloudFileProvider } from '../fileproviders'\r\nimport type { TodoItem } from './model'\r\nimport TodoFileSerializer from './todoFormat'\r\n// import { logger } from 'src/app/logger';\r\n\r\nexport interface SaveResult {\r\n\ttodos: TodoItem[] | null\r\n\tcloudFile: CloudFile\r\n\thadConflict: boolean\r\n\thadUnresolvableConflict: boolean\r\n\trefreshedAuth: any\r\n}\r\n\r\nexport class ConflictResolvingSaver {\r\n\tprivate readonly serializer = new TodoFileSerializer()\r\n\r\n\tpublic constructor(\r\n\t\tprivate readonly cloudFileProvider: CloudFileProvider,\r\n\t\tprivate readonly logger?: (message: string) => void\r\n\t) {}\r\n\r\n\tpublic async saveTodos(\r\n\t\tpath: string,\r\n\t\toriginalVersion: string,\r\n\t\ttodos: TodoItem[],\r\n\t\toriginalContent: string\r\n\t): Promise {\r\n\t\tlet shouldContinue = true\r\n\t\tlet version = originalVersion\r\n\t\tlet intermediateResolution: SaveResult\r\n\t\t// TODO doesn't allows send up refreshedAuth, only when no conflict\r\n\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n\t\twhile (shouldContinue) {\r\n\t\t\tshouldContinue = false\r\n\r\n\t\t\tlet content = this.serializer.toFile(todos)\r\n\t\t\ttry {\r\n\t\t\t\tconst { result, refreshedAuth } = await this.cloudFileProvider.putFile(path, {\r\n\t\t\t\t\tcontent,\r\n\t\t\t\t\tversion\r\n\t\t\t\t})\r\n\r\n\t\t\t\treturn {\r\n\t\t\t\t\thadConflict: false,\r\n\t\t\t\t\thadUnresolvableConflict: false,\r\n\t\t\t\t\tcloudFile: { content, version: result },\r\n\t\t\t\t\ttodos:\r\n\t\t\t\t\t\tversion === originalVersion\r\n\t\t\t\t\t\t\t? todos // this is really just an optimization to not allocate and to allow object refequals prevention of ui update\r\n\t\t\t\t\t\t\t: this.conserveTodosAndIds(todos, this.serializer.fromFile(content)),\r\n\t\t\t\t\trefreshedAuth\r\n\t\t\t\t}\r\n\t\t\t} catch (error) {\r\n\t\t\t\tif (error instanceof Error) {\r\n\t\t\t\t\tif (error.message === 'conflict') {\r\n\t\t\t\t\t\tthis.log('CONFLICT: Got conflict attempting to write file to file provider')\r\n\t\t\t\t\t\tintermediateResolution = await this.attemptResolve(path, content, originalContent)\r\n\t\t\t\t\t\tif (!intermediateResolution.hadUnresolvableConflict) {\r\n\t\t\t\t\t\t\tthis.log(\r\n\t\t\t\t\t\t\t\t'CONFLICT: Server-side changes can be merged with local changes without conflict.'\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t;({ content, version } = intermediateResolution.cloudFile)\r\n\t\t\t\t\t\t\tshouldContinue = true\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\treturn intermediateResolution\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tthrow error\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthrow new Error('unreachable code')\r\n\t}\r\n\r\n\tprivate async attemptResolve(\r\n\t\tpath: string,\r\n\t\tcontent: string,\r\n\t\toriginalContent: string\r\n\t): Promise {\r\n\t\t// get latest from server = B\r\n\t\t// need to get base O - either store it or get it again from the server\r\n\t\tconst { result: conflictingFile, refreshedAuth } = await this.cloudFileProvider.getFile(path)\r\n\r\n\t\tconst diff3Result = diff3Merge(conflictingFile.content, originalContent, content)\r\n\t\tthis.log(`CONFLICT: Finished diff3 on AOB changes. result has length ${diff3Result.length}`)\r\n\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-magic-numbers, @typescript-eslint/no-unnecessary-condition\r\n\t\tif (diff3Result.length === 1 && diff3Result[0].ok) {\r\n\t\t\tconst merged = diff3Result[0].ok\r\n\t\t\tconst resolvedResult = {\r\n\t\t\t\thadConflict: true,\r\n\t\t\t\thadUnresolvableConflict: false,\r\n\t\t\t\tcloudFile: {\r\n\t\t\t\t\tcontent: merged.join(''),\r\n\t\t\t\t\tversion: conflictingFile.version\r\n\t\t\t\t},\r\n\t\t\t\ttodos: null,\r\n\t\t\t\trefreshedAuth\r\n\t\t\t}\r\n\t\t\tthis.log(`CONFLICT: diff3 ok ${diff3Result.length}`)\r\n\t\t\t// console.log(diff3Result)\r\n\t\t\tconsole.log(resolvedResult)\r\n\r\n\t\t\t// TODO - this would sometimes revert\r\n\t\t\t// return resolvedResult\r\n\t\t}\r\n\r\n\t\treturn {\r\n\t\t\thadConflict: true,\r\n\t\t\thadUnresolvableConflict: true,\r\n\t\t\tcloudFile: conflictingFile,\r\n\t\t\ttodos: null,\r\n\t\t\trefreshedAuth\r\n\t\t}\r\n\t}\r\n\r\n\t// Attempt to match up new TodoItems with previous after conflict resolution\r\n\tprivate conserveTodosAndIds(source: TodoItem[], destination: TodoItem[]): TodoItem[] {\r\n\t\t// Build a lookup map using the raw (unparsed line string) of each item\r\n\t\tconst rawMap = new Map()\r\n\t\tfor (const sourceItem of source) {\r\n\t\t\trawMap.set(sourceItem.raw, sourceItem)\r\n\t\t}\r\n\r\n\t\tconst result = []\r\n\t\tfor (const destinationItem of destination) {\r\n\t\t\tconst match = rawMap.get(destinationItem.raw)\r\n\t\t\tlet toAdd = destinationItem\r\n\r\n\t\t\tif (match) {\r\n\t\t\t\tif (match.lineNumber === destinationItem.lineNumber) {\r\n\t\t\t\t\t// if line number also matches, just re-use the previous object\r\n\t\t\t\t\ttoAdd = match\r\n\t\t\t\t} else {\r\n\t\t\t\t\t// if the todo item was moved to a different line, just re-use the ID\r\n\t\t\t\t\tdestinationItem.id = match.id\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// Remove this item from consideration for other matches\r\n\t\t\t\trawMap.delete(destinationItem.raw)\r\n\t\t\t}\r\n\r\n\t\t\tresult.push(toAdd)\r\n\t\t}\r\n\r\n\t\treturn result\r\n\t}\r\n\r\n\tprivate log(message: string) {\r\n\t\tif (this.logger) {\r\n\t\t\tthis.logger(message)\r\n\t\t}\r\n\t}\r\n}\r\n","import { isAnyOf } from '@reduxjs/toolkit'\r\nimport { DropboxFileProvider } from 'lib/fileproviders'\r\nimport { logger } from 'lib/logger'\r\nimport { ConflictResolvingSaver } from 'lib/todotxtformat/conflictResolver'\r\nimport TodoFileSerializer from 'lib/todotxtformat/todoFormat'\r\nimport {\r\n\tlogout,\r\n\tmutateTodos,\r\n\tsaveRefreshedAuth,\r\n\tsetUser,\r\n\tsyncBegin,\r\n\tsyncComplete,\r\n\tsyncFail,\r\n\tsyncWork\r\n} from '../reducers'\r\nimport type { AppStartListening } from '../store'\r\n\r\nexport default function addSagas(startListening: AppStartListening) {\r\n\t// on startup, login, or after any todos change,\r\n\t// dispatch action to see if we should start a synchronization\r\n\tstartListening({\r\n\t\tmatcher: isAnyOf(setUser, mutateTodos),\r\n\t\teffect: async (action, listenerApi) => {\r\n\t\t\tconst state = listenerApi.getState()\r\n\t\t\tif (!!state.user && !!state.user.accessToken) {\r\n\t\t\t\tlistenerApi.dispatch(syncBegin({}))\r\n\t\t\t}\r\n\t\t}\r\n\t})\r\n\r\n\tstartListening({\r\n\t\tactionCreator: syncBegin,\r\n\t\teffect: async (action, listenerApi) => {\r\n\t\t\tconst state = listenerApi.getState()\r\n\t\t\tif (\r\n\t\t\t\t!state.synchronization.inProgress ||\r\n\t\t\t\taction.payload.syncId !== state.synchronization.syncId\r\n\t\t\t) {\r\n\t\t\t\tlogger.verbose(\r\n\t\t\t\t\t`SYNC: syncBegin Listener not dispatching syncWork. syncInProgress=${state.synchronization.inProgress}, action.syncId=${action.payload.syncId}, state.syncId=${state.synchronization.syncId}`\r\n\t\t\t\t)\r\n\t\t\t} else {\r\n\t\t\t\tlogger.verbose(\r\n\t\t\t\t\t`SYNC: Dispatching syncWork because syncId ${action.payload.syncId} == ${state.synchronization.syncId}`\r\n\t\t\t\t)\r\n\t\t\t\tlistenerApi.dispatch(\r\n\t\t\t\t\tsyncWork({\r\n\t\t\t\t\t\tsyncId: action.payload.syncId\r\n\t\t\t\t\t})\r\n\t\t\t\t)\r\n\t\t\t}\r\n\t\t}\r\n\t})\r\n\r\n\tstartListening({\r\n\t\tactionCreator: syncWork,\r\n\t\teffect: async (action, listenerApi) => {\r\n\t\t\tconst state = listenerApi.getState()\r\n\r\n\t\t\tif (!state.user || !state.synchronization.todos) {\r\n\t\t\t\tthrow new Error(\r\n\t\t\t\t\t`SYNC: consistency error: user: ${!!state.user}, synchronization.todos: ${!!state\r\n\t\t\t\t\t\t.synchronization.todos}`\r\n\t\t\t\t)\r\n\t\t\t}\r\n\r\n\t\t\tconst shouldWrite = !!state.synchronization.remoteRevisionId\r\n\r\n\t\t\tconst fileProvider = new DropboxFileProvider(\r\n\t\t\t\t{\r\n\t\t\t\t\taccessToken: state.user.accessToken,\r\n\t\t\t\t\taccessTokenExpiresAt: state.user.accessTokenExpiresAt,\r\n\t\t\t\t\trefreshToken: state.user.refreshToken,\r\n\t\t\t\t\tclientId: state.user.clientId\r\n\t\t\t\t},\r\n\t\t\t\tlogger.info\r\n\t\t\t)\r\n\r\n\t\t\tif (shouldWrite) {\r\n\t\t\t\tconst conflictResolver = new ConflictResolvingSaver(fileProvider, logger.info)\r\n\r\n\t\t\t\tif (\r\n\t\t\t\t\t!state.synchronization.remoteRevisionId ||\r\n\t\t\t\t\t!state.synchronization.lastSyncedTodoFileContent\r\n\t\t\t\t) {\r\n\t\t\t\t\tlogger.warn(\r\n\t\t\t\t\t\t`SYNC: Consistency error- remoteRevisionId:${state.synchronization.remoteRevisionId}, lastSyncedTodoFileContent: ${state.synchronization.lastSyncedTodoFileContent}`\r\n\t\t\t\t\t)\r\n\t\t\t\t\tlistenerApi.dispatch(syncFail({ syncId: action.payload.syncId }))\r\n\t\t\t\t\treturn\r\n\t\t\t\t}\r\n\r\n\t\t\t\tlet errorCode = 'unknownSyncWriteError'\r\n\t\t\t\tlet errorMessage = 'Unknown Sync Write error'\r\n\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconst saveResult = await conflictResolver.saveTodos(\r\n\t\t\t\t\t\tstate.user.settings.todoFilePath,\r\n\t\t\t\t\t\tstate.synchronization.remoteRevisionId,\r\n\t\t\t\t\t\tstate.synchronization.todos,\r\n\t\t\t\t\t\tstate.synchronization.lastSyncedTodoFileContent\r\n\t\t\t\t\t)\r\n\r\n\t\t\t\t\tif (!saveResult.hadUnresolvableConflict) {\r\n\t\t\t\t\t\tlistenerApi.dispatch(\r\n\t\t\t\t\t\t\tsyncComplete({\r\n\t\t\t\t\t\t\t\tsyncId: action.payload.syncId,\r\n\t\t\t\t\t\t\t\tlocalRevisionId: state.synchronization.localRevisionId || '',\r\n\t\t\t\t\t\t\t\tremoteRevisionId: saveResult.cloudFile.version,\r\n\t\t\t\t\t\t\t\ttodos: saveResult.todos,\r\n\t\t\t\t\t\t\t\ttodoFileContent: saveResult.cloudFile.content,\r\n\t\t\t\t\t\t\t\thadConflict: saveResult.hadConflict,\r\n\t\t\t\t\t\t\t\thadUnresolvableConflict: saveResult.hadUnresolvableConflict\r\n\t\t\t\t\t\t\t})\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t\treturn\r\n\t\t\t\t\t}\r\n\t\t\t\t\terrorCode = 'conflict'\r\n\t\t\t\t\terrorMessage = 'Sync unresolvable conflict'\r\n\t\t\t\t} catch (error) {\r\n\t\t\t\t\tlogger.warn(`SYNC: Failed ${error}`)\r\n\t\t\t\t\tlogger.warn(error)\r\n\t\t\t\t\tif (error instanceof Error) {\r\n\t\t\t\t\t\terrorCode = error.toString()\r\n\t\t\t\t\t\terrorMessage = error.message\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tlistenerApi.dispatch(\r\n\t\t\t\t\tsyncFail({\r\n\t\t\t\t\t\tsyncId: action.payload.syncId,\r\n\t\t\t\t\t\tcode: errorCode,\r\n\t\t\t\t\t\tmessage: errorMessage\r\n\t\t\t\t\t})\r\n\t\t\t\t)\r\n\t\t\t} else {\r\n\t\t\t\tconst serializer = new TodoFileSerializer()\r\n\r\n\t\t\t\ttry {\r\n\t\t\t\t\tconst { result, refreshedAuth } = await fileProvider.getFile(\r\n\t\t\t\t\t\tstate.user.settings.todoFilePath\r\n\t\t\t\t\t)\r\n\r\n\t\t\t\t\tif (refreshedAuth) {\r\n\t\t\t\t\t\tlistenerApi.dispatch(saveRefreshedAuth(refreshedAuth))\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tlistenerApi.dispatch(\r\n\t\t\t\t\t\tsyncComplete({\r\n\t\t\t\t\t\t\tsyncId: action.payload.syncId,\r\n\t\t\t\t\t\t\tlocalRevisionId: state.synchronization.localRevisionId,\r\n\t\t\t\t\t\t\ttodos: serializer.fromFile(result.content),\r\n\t\t\t\t\t\t\tremoteRevisionId: result.version,\r\n\t\t\t\t\t\t\thadConflict: false,\r\n\t\t\t\t\t\t\thadUnresolvableConflict: false,\r\n\t\t\t\t\t\t\ttodoFileContent: result.content\r\n\t\t\t\t\t\t})\r\n\t\t\t\t\t)\r\n\t\t\t\t} catch (error: unknown) {\r\n\t\t\t\t\tif (error instanceof Error && error.message == '401') {\r\n\t\t\t\t\t\tlistenerApi.dispatch(logout())\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (error instanceof Error && error.message === 'notfound') {\r\n\t\t\t\t\t\tlogger.info(\r\n\t\t\t\t\t\t\t'SYNC: Got a notfound response while retrieving todo file, will create an initial file'\r\n\t\t\t\t\t\t)\r\n\t\t\t\t\t\tif (!state.user) {\r\n\t\t\t\t\t\t\tthrow new Error('consistency error')\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tconst content = '(A) This is your first example todo item'\r\n\t\t\t\t\t\ttry {\r\n\t\t\t\t\t\t\tconst { result, refreshedAuth } = await fileProvider.putFile(\r\n\t\t\t\t\t\t\t\tstate.user.settings.todoFilePath,\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tcontent,\r\n\t\t\t\t\t\t\t\t\tversion: null\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\tif (\r\n\t\t\t\t\t\t\t\t!state.user ||\r\n\t\t\t\t\t\t\t\t!state.synchronization.localRevisionId ||\r\n\t\t\t\t\t\t\t\t!state.synchronization.remoteRevisionId\r\n\t\t\t\t\t\t\t) {\r\n\t\t\t\t\t\t\t\tthrow new Error('consistency error')\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tif (refreshedAuth) {\r\n\t\t\t\t\t\t\t\tlistenerApi.dispatch(saveRefreshedAuth(refreshedAuth))\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tlistenerApi.dispatch(\r\n\t\t\t\t\t\t\t\tsyncComplete({\r\n\t\t\t\t\t\t\t\t\tsyncId: action.payload.syncId,\r\n\t\t\t\t\t\t\t\t\tlocalRevisionId: state.synchronization.localRevisionId,\r\n\t\t\t\t\t\t\t\t\ttodos: serializer.fromFile(content),\r\n\t\t\t\t\t\t\t\t\tremoteRevisionId: result,\r\n\t\t\t\t\t\t\t\t\thadConflict: false,\r\n\t\t\t\t\t\t\t\t\thadUnresolvableConflict: false,\r\n\t\t\t\t\t\t\t\t\ttodoFileContent: content\r\n\t\t\t\t\t\t\t\t})\r\n\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\treturn\r\n\t\t\t\t\t\t} catch (newFileError) {\r\n\t\t\t\t\t\t\tlogger.warn(newFileError)\r\n\t\t\t\t\t\t\tlistenerApi.dispatch(syncFail({ syncId: action.payload.syncId }))\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tlistenerApi.dispatch(syncFail({ syncId: action.payload.syncId }))\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t})\r\n\r\n\tstartListening({\r\n\t\tactionCreator: syncComplete,\r\n\t\teffect: async ({ payload }, listenerApi) => {\r\n\t\t\tconst state = listenerApi.getState()\r\n\t\t\tif (!state.synchronization.inProgress || payload.syncId !== state.synchronization.syncId) {\r\n\t\t\t\tlogger.verbose(\r\n\t\t\t\t\t`SYNC: completeSync Listener WON'T start a new sync because sync inProgress==${state.synchronization.inProgress} or ` +\r\n\t\t\t\t\t\t`action syncId \"${payload.syncId}\" != \"${state.synchronization.syncId}\"`\r\n\t\t\t\t)\r\n\t\t\t\treturn\r\n\t\t\t}\r\n\r\n\t\t\tconst lastHistoryEntry = state.history.at(-1)\r\n\r\n\t\t\tif (!lastHistoryEntry.remoteRevisionId) {\r\n\t\t\t\tlogger.verbose(\r\n\t\t\t\t\t`SYNC: completeSync Listener WILL start a new sync because newest localRevision ` +\r\n\t\t\t\t\t\t`${lastHistoryEntry.localRevisionId} has no remoteRevisionId `\r\n\t\t\t\t)\r\n\t\t\t\tlistenerApi.dispatch(syncWork({ syncId: payload.syncId }))\r\n\t\t\t}\r\n\t\t}\r\n\t})\r\n\r\n\tstartListening({\r\n\t\tactionCreator: syncFail,\r\n\t\teffect: async ({ payload }, listenerApi) => {\r\n\t\t\talert(`sync failed: ${payload.message}`)\r\n\t\t}\r\n\t})\r\n}\r\n","import type {\r\n\tTypedAddListener,\r\n\tTypedStartListening\r\n} from '@reduxjs/toolkit';\nimport {\r\n\taddListener,\r\n\tconfigureStore,\r\n\tcreateListenerMiddleware\r\n} from '@reduxjs/toolkit'\r\nimport {\r\n\tFLUSH,\r\n\tPAUSE,\r\n\tPERSIST,\r\n\tpersistReducer,\r\n\tpersistStore,\r\n\tPURGE,\r\n\tREGISTER,\r\n\tREHYDRATE\r\n} from 'redux-persist'\r\nimport storage from 'redux-persist/lib/storage'\r\nimport addSagas from './listeners/listeners'\r\nimport rootReducer from './reducers'\r\n\r\nconst persistConfig = {\r\n\tkey: 'root',\r\n\tstorage,\r\n\twhitelist: [\r\n\t\t'user',\r\n\t\t'list',\r\n\t\t'history',\r\n\t\t'synchronization',\r\n\t\t'schemaVersion',\r\n\t\t'interface',\r\n\t\t'viewDefinitions'\r\n\t]\r\n\t// migrate: (state: AppState) => {\r\n\t// if (!state) {\r\n\t// return Promise.resolve(state);\r\n\t// }\r\n\t// if (!state.schemaVersion || state.schemaVersion < INITIAL_STATE.schemaVersion) {\r\n\t// return Promise.resolve(INITIAL_STATE);\r\n\t// }\r\n\r\n\t// let user = state.user;\r\n\t// if (user) {\r\n\t// const userSettings = user.settings || {};\r\n\t// user = { ...user, settings: { ...INITIAL_USER_SETTINGS, ...userSettings } };\r\n\t// }\r\n\t// return Promise.resolve({ ...state, user });\r\n\t// }\r\n}\r\nconst persistedReducer = persistReducer(persistConfig, rootReducer)\r\n\r\nconst listenerMiddleware = createListenerMiddleware()\r\n\r\nconst reduxPersistActions = [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER]\r\n\r\nexport const store = configureStore({\r\n\treducer: persistedReducer,\r\n\tmiddleware: getDefaultMiddleware =>\r\n\t\tgetDefaultMiddleware({\r\n\t\t\tserializableCheck: {\r\n\t\t\t\tignoredActions: [...reduxPersistActions]\r\n\t\t\t}\r\n\t\t}).prepend(listenerMiddleware.middleware)\r\n})\r\n\r\nexport const persistor = persistStore(store)\r\n\r\n// Infer the `RootState` and `AppDispatch` types from the store itself\r\nexport type RootState = ReturnType\r\n// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}\r\nexport type AppDispatch = typeof store.dispatch\r\n\r\nexport type AppStartListening = TypedStartListening\r\n\r\nexport type AppStartListeningParameter = Parameters[0]\r\n\r\nexport const startAppListening =\r\n\tlistenerMiddleware.startListening as AppStartListening\r\n\r\nexport const addAppListener = addListener as TypedAddListener<\r\n\tRootState,\r\n\tAppDispatch\r\n>\r\n\r\naddSagas(startAppListening)\r\n","import CssBaseline from '@mui/material/CssBaseline'\r\nimport { ThemeProvider } from '@mui/material/styles'\r\nimport AppRoutes from 'AppRoutes'\r\nimport BackgroundSync from 'BackgroundSync'\r\nimport { ErrorBoundary } from 'components/ErrorBoundary'\r\nimport LoadingOrError from 'components/LoadingOrError'\r\nimport { Provider } from 'react-redux'\r\nimport { PersistGate } from 'redux-persist/integration/react'\r\nimport appTheme from 'theme'\r\nimport { persistor, store } from './store'\r\n\r\nexport default function App(): React.ReactElement {\r\n\treturn (\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t} persistor={persistor}>\r\n\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\r\n\t\t\t\t\r\n\t\t\t\r\n\t\t\r\n\t)\r\n}\r\n","// src/client/build/register.ts\nimport { Workbox, messageSW } from \"workbox-window\";\nvar autoUpdateMode = \"true\";\nvar selfDestroying = \"false\";\nvar auto = autoUpdateMode === \"true\";\nvar autoDestroy = selfDestroying === \"true\";\nfunction registerSW(options = {}) {\n const {\n immediate = false,\n onNeedRefresh,\n onOfflineReady,\n onRegistered,\n onRegisterError\n } = options;\n let wb;\n let registration;\n const updateServiceWorker = async (reloadPage = true) => {\n if (!auto) {\n if (reloadPage) {\n wb == null ? void 0 : wb.addEventListener(\"controlling\", (event) => {\n if (event.isUpdate)\n window.location.reload();\n });\n }\n if (registration && registration.waiting) {\n await messageSW(registration.waiting, { type: \"SKIP_WAITING\" });\n }\n }\n };\n if (\"serviceWorker\" in navigator) {\n wb = new Workbox(\"/sw.js\", { scope: \"/\", type: \"classic\" });\n wb.addEventListener(\"activated\", (event) => {\n if (event.isUpdate)\n auto && window.location.reload();\n else if (!autoDestroy)\n onOfflineReady == null ? void 0 : onOfflineReady();\n });\n if (!auto) {\n const showSkipWaitingPrompt = () => {\n onNeedRefresh == null ? void 0 : onNeedRefresh();\n };\n wb.addEventListener(\"waiting\", showSkipWaitingPrompt);\n wb.addEventListener(\"externalwaiting\", showSkipWaitingPrompt);\n }\n wb.register({ immediate }).then((r) => {\n registration = r;\n onRegistered == null ? void 0 : onRegistered(r);\n }).catch((e) => {\n onRegisterError == null ? void 0 : onRegisterError(e);\n });\n }\n return updateServiceWorker;\n}\nexport {\n registerSW\n};\n","import '@fontsource/roboto/300.css'\r\nimport '@fontsource/roboto/400.css'\r\nimport '@fontsource/roboto/500.css'\r\nimport '@fontsource/roboto/700.css'\r\nimport App from 'App'\r\nimport { StrictMode } from 'react'\r\nimport { createRoot } from 'react-dom/client'\r\nimport { registerSW } from 'virtual:pwa-register'\r\nimport './index.css'\r\n\r\nregisterSW()\r\n\r\nconst container = document.querySelector('#root')\r\nif (container) {\r\n\tconst root = createRoot(container)\r\n\troot.render(\r\n\t\t\r\n\t\t\t\r\n\t\t\r\n\t)\r\n}\r\n"],"file":"assets/index.3db71a35.js"}