diff --git a/scripts/add-context.js b/scripts/add-context.js index cb1435c4559..6e8deb5afca 100644 --- a/scripts/add-context.js +++ b/scripts/add-context.js @@ -5,13 +5,13 @@ const path = require('path') const murmurHash3 = require('murmurhash3js') const EM_TOKEN = '__EM__' -const ROOT_TOKEN = '__ROOT__' +const HOME_TOKEN = '__ROOT__' const SEPARATOR_TOKEN = '__SEP__' const appendContext = (context, child) => unroot([...context, child]) const escapeRegExp = s => s.replace(/[-[\]{}()*+?.\\^$|#\s]/g, '\\$&') const escapeSelector = s => '_' + s.replace(regExpEscapeSelector, s => `_${s.charCodeAt(0)}`) const regExpEscapeSelector = new RegExp('[' + escapeRegExp(' !"#$%&\'()*+,./:;<=>?@[]^`{|}~') + ']', 'g') -const unroot = context => context[0] === ROOT_TOKEN ? context.slice(1) : context +const unroot = context => context[0] === HOME_TOKEN ? context.slice(1) : context /** Encode the thoughts (and optionally rank) as a string. */ const hashContext = (thoughts, rank) => murmurHash3.x64.hash128(thoughts @@ -46,7 +46,7 @@ const cli = () => { console.info('Reading ' + inputPath) const input = fs.readFileSync(inputPath, 'utf-8') const state = JSON.parse(input) - traverseContext(state, [ROOT_TOKEN], setContext) + traverseContext(state, [HOME_TOKEN], setContext) traverseContext(state, [EM_TOKEN], setContext) const ext = path.extname(inputPath) const outputPath = inputPath.replace(ext, '') + '-with-contexts' + ext diff --git a/scripts/repair.js b/scripts/repair.js index 35df5be3774..cd2f9ceaaab 100644 --- a/scripts/repair.js +++ b/scripts/repair.js @@ -24,14 +24,14 @@ let repair = (maxDepth = 100) => { let inconsistentRanks = 0 let duplicateThoughtContextIds = 0 - const ROOT_TOKEN = '__ROOT__' + const HOME_TOKEN = '__ROOT__' /** Generate a timestamp of now. */ const timestamp = () => new Date().toISOString() /** Remove the root token. */ const unroot = thoughts => - thoughts.length > 0 && (thoughts[0] === ROOT_TOKEN || thoughts[0].value === ROOT_TOKEN) + thoughts.length > 0 && (thoughts[0] === HOME_TOKEN || thoughts[0].value === HOME_TOKEN) ? thoughts.slice(1) : thoughts diff --git a/src/App.css b/src/App.css index ae4ba006632..af82c3e9a8f 100644 --- a/src/App.css +++ b/src/App.css @@ -2891,6 +2891,21 @@ code, .code { left: 19px; } +.quick-add-button { + z-index: 100; + position: absolute; + bottom: 2rem; + right: 3rem; + display: flex; + align-items: center; + justify-content: center; + transition: transform 200ms ease-in-out; +} + +.quick-add-button.rotate { + transform: rotate(135deg); +} + @keyframes ellipsis { to { width: 1.25em; } } diff --git a/src/action-creators/__tests__/cursorNext.ts b/src/action-creators/__tests__/cursorNext.ts index ecb5e06ad2f..863fb3c09a6 100644 --- a/src/action-creators/__tests__/cursorNext.ts +++ b/src/action-creators/__tests__/cursorNext.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' import { cursorNext, importText, setCursor } from '../../action-creators' import { createTestStore } from '../../test-helpers/createTestStore' import { setCursorFirstMatchActionCreator } from '../../test-helpers/setCursorFirstMatch' @@ -11,7 +11,7 @@ describe('normal view', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - a1 @@ -32,7 +32,7 @@ describe('normal view', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b` @@ -52,7 +52,7 @@ describe('normal view', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b` @@ -82,7 +82,7 @@ describe('normal view', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - SORT - a @@ -111,7 +111,7 @@ describe('normal view', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - a1 diff --git a/src/action-creators/__tests__/cursorPrev.ts b/src/action-creators/__tests__/cursorPrev.ts index f30792de956..39bd83f19e4 100644 --- a/src/action-creators/__tests__/cursorPrev.ts +++ b/src/action-creators/__tests__/cursorPrev.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' import { cursorPrev, importText, setCursor } from '../../action-creators' import { createTestStore } from '../../test-helpers/createTestStore' import setCursorFirstMatch, { setCursorFirstMatchActionCreator } from '../../test-helpers/setCursorFirstMatch' @@ -10,7 +10,7 @@ describe('normal view', () => { const store = createTestStore() store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - a1 @@ -31,7 +31,7 @@ describe('normal view', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b` @@ -51,7 +51,7 @@ describe('normal view', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b` @@ -81,7 +81,7 @@ describe('normal view', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - SORT - a @@ -109,7 +109,7 @@ describe('normal view', () => { const store = createTestStore() store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - a1 diff --git a/src/action-creators/cursorNext.ts b/src/action-creators/cursorNext.ts index 72a4d6163ba..9d6ffe5529c 100644 --- a/src/action-creators/cursorNext.ts +++ b/src/action-creators/cursorNext.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { scrollCursorIntoView, setCursor, suppressExpansion } from '../action-creators' import { parentOf } from '../util' import { Thunk } from '../types' @@ -12,7 +12,7 @@ const cursorNext = (): Thunk => (dispatch, getState) => { const { cursor } = state if (!cursor) { - const children = getChildrenSorted(state, [ROOT_TOKEN]) + const children = getChildrenSorted(state, [HOME_TOKEN]) if (children.length > 0) { dispatch(setCursor({ path: [children[0]] })) dispatch(scrollCursorIntoView()) diff --git a/src/action-creators/cursorPrev.ts b/src/action-creators/cursorPrev.ts index d019d7ff111..21069b8a507 100644 --- a/src/action-creators/cursorPrev.ts +++ b/src/action-creators/cursorPrev.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { scrollCursorIntoView, setCursor, suppressExpansion } from '../action-creators' import { getThoughtBefore, simplifyPath, getChildrenSorted } from '../selectors' import { parentOf } from '../util' @@ -10,7 +10,7 @@ const cursorPrev = (): Thunk => (dispatch, getState) => { const { cursor } = state if (!cursor) { - const children = getChildrenSorted(state, [ROOT_TOKEN]) + const children = getChildrenSorted(state, [HOME_TOKEN]) if (children.length > 0) { dispatch(setCursor({ path: [children[0]] })) dispatch(scrollCursorIntoView()) diff --git a/src/action-creators/dataIntegrityCheck.ts b/src/action-creators/dataIntegrityCheck.ts index c222caab40e..947ff88a347 100644 --- a/src/action-creators/dataIntegrityCheck.ts +++ b/src/action-creators/dataIntegrityCheck.ts @@ -13,7 +13,6 @@ import { headRank, headValue, pathToContext, - rootedParentOf, timestamp, unroot, } from '../util' @@ -27,6 +26,7 @@ import { getChildrenRanked, simplifyPath, splitChain, + rootedParentOf, } from '../selectors' const disableAll = true @@ -163,7 +163,7 @@ const dataIntegrityCheck = (path: Path): Thunk => (dispatch, getState) => { // sync divergent ranks if (syncDivergentRanks) { - const contextIndexThoughtsMatchingValue = getChildrenRanked(state, rootedParentOf(pathToContext(simplePath))) + const contextIndexThoughtsMatchingValue = getChildrenRanked(state, rootedParentOf(state, pathToContext(simplePath))) .filter(equalThoughtValue(value)) if (contextIndexThoughtsMatchingValue.length > 0) { diff --git a/src/action-creators/index.ts b/src/action-creators/index.ts index 5fd692667c6..df9a91a8517 100644 --- a/src/action-creators/index.ts +++ b/src/action-creators/index.ts @@ -59,6 +59,7 @@ export const showModal = reducerToThunk('showModal') export const splitSentences = reducerToThunk('splitSentences') export const splitThought = reducerToThunk('splitThought') export const status = reducerToThunk('status') +export const toggleAbsoluteContext = reducerToThunk('toggleAbsoluteContext') export const toggleAttribute = reducerToThunk('toggleAttribute') export const toggleContextView = reducerToThunk('toggleContextView') export const toggleHiddenThoughts = reducerToThunk('toggleHiddenThoughts') diff --git a/src/action-creators/loadFromUrl.ts b/src/action-creators/loadFromUrl.ts index a38e43a249a..1acac76746e 100644 --- a/src/action-creators/loadFromUrl.ts +++ b/src/action-creators/loadFromUrl.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' import { isRoot, pathToContext } from '../util' import { importText, setCursor } from '../action-creators' import { decodeThoughtsUrl, getAllChildren } from '../selectors' @@ -13,7 +13,7 @@ interface Options { * * @param skipRoot See importHtml. */ -const loadFromUrl = (url: string, path = RANKED_ROOT, { skipRoot }: Options = {}): Thunk> => async (dispatch, getState) => { +const loadFromUrl = (url: string, path = HOME_PATH, { skipRoot }: Options = {}): Thunk> => async (dispatch, getState) => { const urlWithProtocol = /^http|localhost/.test(url) ? url : 'https://' + url const response = await fetch(urlWithProtocol) const text = await response.text() diff --git a/src/action-creators/loadPublicThoughts.ts b/src/action-creators/loadPublicThoughts.ts index 11c0a56967c..d70722335c7 100644 --- a/src/action-creators/loadPublicThoughts.ts +++ b/src/action-creators/loadPublicThoughts.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { hashContext, hashThought, owner } from '../util' import { loadRemoteState } from '../action-creators' import { Thunk, Parent, Snapshot } from '../types' @@ -32,7 +32,7 @@ const loadPublicThoughts = (): Thunk => (dispatch, getState) => { thoughts: { contextCache: [], contextIndex: { - [hashContext([ROOT_TOKEN])]: parentEntry + [hashContext([HOME_TOKEN])]: parentEntry }, thoughtCache: parentEntry.children.map(child => hashThought(child.value)), thoughtIndex: parentEntry.children.reduce((accum, child) => ({ @@ -40,7 +40,7 @@ const loadPublicThoughts = (): Thunk => (dispatch, getState) => { [hashThought(child.value)]: { value: child.value, contexts: [{ - context: [ROOT_TOKEN], + context: [HOME_TOKEN], rank: child.rank }], lastUpdated: child.lastUpdated, diff --git a/src/action-creators/newThought.ts b/src/action-creators/newThought.ts index 947dda6235e..6db44e70b07 100644 --- a/src/action-creators/newThought.ts +++ b/src/action-creators/newThought.ts @@ -1,5 +1,5 @@ import { isTouch, isSafari } from '../browser' -import { ROOT_TOKEN, TUTORIAL_STEP_START } from '../constants' +import { HOME_TOKEN, TUTORIAL_STEP_START } from '../constants' import { getSetting, getAllChildren, hasChild, isContextViewActive } from '../selectors' import { asyncFocus, parentOf, ellipsize, headValue, pathToContext } from '../util' import { alert } from '../action-creators' @@ -57,7 +57,7 @@ const newThought = ({ const context = path && (showContexts && path.length > 2 ? pathToContext(parentOf(parentOf(path))) : !showContexts && path.length > 1 ? pathToContext(parentOf(path)) - : [ROOT_TOKEN]) + : [HOME_TOKEN]) // split the thought at the selection // do not split at the beginning of a line as the common case is to want to create a new thought after, and shift + Enter is so near // do not split with gesture, as Enter is avialable and separate in the context of mobile diff --git a/src/action-creators/pull.ts b/src/action-creators/pull.ts index f12366e784d..d2931eb788b 100644 --- a/src/action-creators/pull.ts +++ b/src/action-creators/pull.ts @@ -2,13 +2,13 @@ import _ from 'lodash' import * as db from '../data-providers/dexie' import getFirebaseProvider from '../data-providers/firebase' import getManyDescendants from '../data-providers/data-helpers/getManyDescendants' -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { hashContext, mergeThoughts } from '../util' import { reconcile, updateThoughts } from '../action-creators' import { Thunk, Context, Index, Lexeme, Parent, ThoughtsInterface } from '../types' const BUFFER_DEPTH = 2 -const ROOT_ENCODED = hashContext([ROOT_TOKEN]) +const ROOT_ENCODED = hashContext([HOME_TOKEN]) export interface PullOptions { maxDepth?: number, diff --git a/src/components/AddIcon.tsx b/src/components/AddIcon.tsx new file mode 100644 index 00000000000..ad940533622 --- /dev/null +++ b/src/components/AddIcon.tsx @@ -0,0 +1,23 @@ +import React, { FC } from 'react' +import { connect } from 'react-redux' +import { theme } from '../selectors' +import { Index } from '../types' +import { State } from '../util/initialState' + +interface SearchIconProps { + dark?: boolean, + fill?: string, + size: number, + style?: Index, +} + +// eslint-disable-next-line jsdoc/require-jsdoc +const mapStateToProps = (state: State) => ({ + dark: theme(state) !== 'Light' +}) + +// eslint-disable-next-line jsdoc/require-jsdoc +const AddIcon: FC = ({ dark, fill, size = 20, style }) => + + +export default connect(mapStateToProps)(AddIcon) diff --git a/src/components/AppComponent.tsx b/src/components/AppComponent.tsx index 23affb84061..d0bd75c2039 100644 --- a/src/components/AppComponent.tsx +++ b/src/components/AppComponent.tsx @@ -26,6 +26,7 @@ import Scale from './Scale' import Tutorial from './Tutorial' import Toolbar from './Toolbar' import HamburgerMenu from './HamburgerMenu' +import QuickAddButton from './QuickAddButton' const Content = React.lazy(() => import('./Content')) @@ -166,6 +167,7 @@ const AppComponent: FC = props => { + diff --git a/src/components/Content.tsx b/src/components/Content.tsx index a79146c1630..54406c42194 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -4,22 +4,42 @@ import { connect } from 'react-redux' import classNames from 'classnames' import { isTouch } from '../browser' import { cursorBack as cursorBackActionCreator, expandContextThought, modalRemindMeLater, toggleSidebar as toggleSidebarActionCreator } from '../action-creators' -import { MODAL_CLOSE_DURATION, RANKED_ROOT, ROOT_TOKEN, TUTORIAL2_STEP_SUCCESS } from '../constants' -import { attribute, getSetting, getAllChildren, isChildVisibleWithCursorCheck, isTutorial } from '../selectors' -import { publishMode } from '../util' +import { MODAL_CLOSE_DURATION, ABSOLUTE_PATH, HOME_PATH, TUTORIAL2_STEP_SUCCESS } from '../constants' +import { attribute, getSetting, getAllChildren, isTutorial } from '../selectors' +import { isAbsolute, publishMode } from '../util' import { State } from '../util/initialState' // components import NewThoughtInstructions from './NewThoughtInstructions' import Search from './Search' import Subthoughts from './Subthoughts' +import { childrenFilterPredicate } from '../selectors/getChildren' +import Editable from './Editable' +import { SimplePath } from '../types' const tutorialLocal = localStorage['Settings/Tutorial'] === 'On' const tutorialStepLocal = +(localStorage['Settings/Tutorial Step'] || 1) +const TransientChildPath = [{ + value: '', + rank: 0, +}] as SimplePath + +/* + Transient Editable represents a child that is yet not in the state. + But as soon as user types it adds the child to the state with the new value and vanishes. + However removing the transient editable should be handled by some business logic by parent components. +*/ +const TransientEditable = + // eslint-disable-next-line jsdoc/require-jsdoc const mapStateToProps = (state: State) => { - const { isLoading, noteFocus, search, showModal } = state + const { isLoading, noteFocus, search, showModal, rootContext } = state const isTutorialLocal = isLoading ? tutorialLocal : isTutorial(state) @@ -28,11 +48,14 @@ const mapStateToProps = (state: State) => { // eslint-disable-next-line @typescript-eslint/no-extra-parens const tutorialStep = isLoading ? tutorialStepLocal : +(getSetting(state, 'Tutorial Step') ?? 1) - const children = getAllChildren(state, [ROOT_TOKEN]) - const rootThoughtsLength = children.filter(isChildVisibleWithCursorCheck(state, RANKED_ROOT, RANKED_ROOT)).length + const isAbsoluteContext = isAbsolute(rootContext) + const children = getAllChildren(state, rootContext) + + const rankedRoot = isAbsoluteContext ? ABSOLUTE_PATH : HOME_PATH + const rootThoughtsLength = children.filter(childrenFilterPredicate(state, rankedRoot, rankedRoot)).length // pass rootSort to allow root Subthoughts ro render on toggleSort - const rootSort = attribute(state, [ROOT_TOKEN], '=sort') || 'None' + const rootSort = attribute(state, rootContext, '=sort') || 'None' return { search, @@ -41,7 +64,9 @@ const mapStateToProps = (state: State) => { tutorialStep, rootThoughtsLength, noteFocus, - rootSort + rootSort, + isAbsoluteContext, + rootContext, } } @@ -72,7 +97,7 @@ const isLeftSpaceClick = (e: MouseEvent, content?: HTMLElement) => { /** The main content section of em. */ const Content: ContentComponent = props => { - const { search, isTutorialLocal, tutorialStep, showModal, showRemindMeLaterModal, cursorBack: moveCursorBack, toggleSidebar, rootThoughtsLength, noteFocus, rootSort } = props + const { search, isTutorialLocal, tutorialStep, showModal, showRemindMeLaterModal, cursorBack: moveCursorBack, toggleSidebar, rootThoughtsLength, noteFocus, rootSort, isAbsoluteContext } = props const contentRef = useRef(null) /** Removes the cursor if the click goes all the way through to the content. Extends cursorBack with logic for closing modals. */ @@ -114,11 +139,12 @@ const Content: ContentComponent = props => { {search != null ? : - {rootThoughtsLength === 0 ? : } + {rootThoughtsLength === 0 ? + isAbsoluteContext ? TransientEditable : : } } diff --git a/src/components/Editable.tsx b/src/components/Editable.tsx index 903e0f191da..406f6935a09 100644 --- a/src/components/Editable.tsx +++ b/src/components/Editable.tsx @@ -4,7 +4,7 @@ import { Dispatch } from 'redux' import { connect } from 'react-redux' import { unescape } from 'html-escaper' import classNames from 'classnames' -import { alert, cursorBack, editing, error, existingThoughtChange, importText, render, setCursor, setEditingValue, setInvalidState, tutorialNext } from '../action-creators' +import { alert, cursorBack, editing, error, existingThoughtChange, importText, render, setCursor, setEditingValue, setInvalidState, tutorialNext, newThought } from '../action-creators' import { isTouch, isSafari } from '../browser' import globals from '../globals' import { store } from '../store' @@ -17,7 +17,6 @@ import { EDIT_THROTTLE, EM_TOKEN, MODIFIER_KEYS, - ROOT_TOKEN, TUTORIAL2_STEP_CONTEXT1, TUTORIAL2_STEP_CONTEXT1_PARENT, TUTORIAL2_STEP_CONTEXT2, @@ -58,6 +57,7 @@ import { getAllChildren, hasChild, isContextViewActive, + rootedParentOf, } from '../selectors' // the amount of time in milliseconds since lastUpdated before the thought placeholder changes to something more facetious @@ -115,6 +115,11 @@ interface EditableProps { showContexts?: boolean, style?: React.CSSProperties, simplePath: SimplePath, + /* If transient is true: + 1. Instead of calling exisitingThoughtChange, it calls newThought to add the given child to the state. + 2. It also sets focus to itself on render. + */ + transient?: boolean, onKeyDownAction?: () => void, } @@ -154,7 +159,7 @@ const showDuplicationAlert = duplicateAlertToggler() * An editable thought with throttled editing. * Use rank instead of headRank(simplePath) as it will be different for context view. */ -const Editable = ({ disabled, isEditing, simplePath, path, cursorOffset, showContexts, rank, style, onKeyDownAction, dispatch }: Connected) => { +const Editable = ({ disabled, isEditing, simplePath, path, cursorOffset, showContexts, rank, style, onKeyDownAction, dispatch, transient }: Connected) => { const state = store.getState() const thoughts = pathToContext(simplePath) const value = head(showContexts ? parentOf(thoughts) : thoughts) || '' @@ -162,7 +167,7 @@ const Editable = ({ disabled, isEditing, simplePath, path, cursorOffset, showCon const uneditable = hasChild(state, thoughts, '=uneditable') const context = showContexts && thoughts.length > 2 ? parentOf(parentOf(thoughts)) : !showContexts && thoughts.length > 1 ? parentOf(thoughts) - : [ROOT_TOKEN] + : state.rootContext const childrenOptions = getAllChildren(state, [...context, '=options']) const options = childrenOptions.length > 0 ? childrenOptions.map(child => child.value.toLowerCase()) @@ -253,7 +258,17 @@ const Editable = ({ disabled, isEditing, simplePath, path, cursorOffset, showCon const oldValue = oldValueRef.current const thought = getThought(state, oldValue) + + if (transient) { + dispatch(newThought({ + at: rootedParentOf(state, path), + value: newValue, + })) + return + } + if (thought) { + dispatch(existingThoughtChange({ context, showContexts, @@ -321,12 +336,12 @@ const Editable = ({ disabled, isEditing, simplePath, path, cursorOffset, showCon // noFocusNode: !noteFocus && (cursorOffset !== null || !window.getSelection()?.focusNode) && !dragHold, // }) - if (isEditing && - contentRef.current && - editMode && - !noteFocus && - (state.cursorOffset !== null || !window.getSelection()?.focusNode) && - !dragHold + // Note: Allow tranisent editable to have focus on render + if (transient || + ( + isEditing && editMode && !noteFocus && contentRef.current + && (state.cursorOffset !== null || !window.getSelection()?.focusNode) + && !dragHold) ) { /* When a new thought is created, the Shift key should be on when Auto-Capitalization is enabled. @@ -437,7 +452,7 @@ const Editable = ({ disabled, isEditing, simplePath, path, cursorOffset, showCon } // run the thoughtChangeHandler immediately if superscript changes or it's a url (also when it changes true to false) - if (contextLengthChange || urlChange || isEmpty || isDivider(newValue)) { + if (transient || contextLengthChange || urlChange || isEmpty || isDivider(newValue)) { // update new supercript value and url boolean throttledChangeRef.current.flush() thoughtChangeHandler(newValue, { context, showContexts, rank, simplePath }) @@ -469,6 +484,15 @@ const Editable = ({ disabled, isEditing, simplePath, path, cursorOffset, showCon // text/plain may contain text that ultimately looks like html (contains
  • ) and should be parsed as html // pass the untrimmed old value to importText so that the whitespace is not loss when combining the existing value with the pasted value const rawDestValue = strip(contentRef.current!.innerHTML, { preventTrim: true }) + + // If transient first add new thought and then import the text + if (transient) { + dispatch(newThought({ + at: rootedParentOf(state, path), + value: '', + })) + } + dispatch(importText({ path, text: isHTML(plainText) diff --git a/src/components/ModalExport.tsx b/src/components/ModalExport.tsx index e7ec513d89c..725bc6a7a14 100644 --- a/src/components/ModalExport.tsx +++ b/src/components/ModalExport.tsx @@ -4,8 +4,8 @@ import ArrowDownWhite from '../images/keyboard_arrow_down_352466.svg' import ArrowDownBlack from '../images/iconfinder_ic_keyboard_arrow_down_black_352466.svg' import ClipboardJS from 'clipboard' import globals from '../globals' -import { RANKED_ROOT } from '../constants' -import { download, ellipsize, getPublishUrl, hashContext, headValue, isDocumentEditable, isRoot, pathToContext, removeRoot, timestamp, unroot } from '../util' +import { HOME_PATH } from '../constants' +import { download, ellipsize, getPublishUrl, hashContext, headValue, isDocumentEditable, isRoot, pathToContext, removeHome, timestamp, unroot } from '../util' import { alert, error, modalRemindMeLater, pull } from '../action-creators' import { exportContext, getDescendants, getAllChildren, simplifyPath, theme } from '../selectors' import Modal from './Modal' @@ -26,7 +26,7 @@ const ModalExport = () => { const dispatch = useDispatch() const isMounted = useRef(false) const state = store.getState() - const cursor = useSelector((state: State) => state.cursor || RANKED_ROOT) + const cursor = useSelector((state: State) => state.cursor || HOME_PATH) const simplePath = simplifyPath(state, cursor) const context = pathToContext(simplePath) const contextTitle = unroot(context.concat(['=publish', 'Title'])) @@ -67,7 +67,7 @@ const ModalExport = () => { const exported = exportContext(store.getState(), context, selected.type, { title: titleChild ? titleChild.value : undefined }) - setExportContent(titleChild ? exported : removeRoot(exported).trimStart()) + setExportContent(titleChild ? exported : removeHome(exported).trimStart()) } // fetch all pending descendants of the cursor once before they are exported diff --git a/src/components/QuickAddButton.tsx b/src/components/QuickAddButton.tsx new file mode 100644 index 00000000000..7da73a7f5d8 --- /dev/null +++ b/src/components/QuickAddButton.tsx @@ -0,0 +1,26 @@ +import React from 'react' +import AddIcon from './AddIcon' +import { useDispatch, useSelector } from 'react-redux' +import { State } from '../util/initialState' +import classNames from 'classnames' +import { toggleAbsoluteContext } from '../action-creators' +import { isAbsolute } from '../util' + +/** + * Quick Add Button. + */ +const QuickAddButton: React.FC = () => { + const rootContext = useSelector((state: State) => state.rootContext) + const dispatch = useDispatch() + + return ( +
    dispatch(toggleAbsoluteContext())}> + +
    + ) +} + +export default QuickAddButton diff --git a/src/components/SearchSubthoughts.tsx b/src/components/SearchSubthoughts.tsx index b42cb232c8c..965ae49ee9c 100644 --- a/src/components/SearchSubthoughts.tsx +++ b/src/components/SearchSubthoughts.tsx @@ -1,7 +1,7 @@ import React, { FC } from 'react' import { connect } from 'react-redux' import { store } from '../store' -import { EM_TOKEN, RANKED_ROOT, ROOT_TOKEN } from '../constants' +import { EM_TOKEN, HOME_PATH, HOME_TOKEN } from '../constants' import { exists } from '../selectors' import { searchLimit as setSearchLimit } from '../action-creators' import { escapeRegExp, formatNumber, isArchived, isDocumentEditable, rankThoughtsSequential, sort } from '../util' @@ -56,7 +56,7 @@ const SearchSubthoughts: FC> = ({ search, arch sort(Object.values(thoughtIndex) .filter(thought => (archived || !isArchived(thought)) && - thought.value !== ROOT_TOKEN && + thought.value !== HOME_TOKEN && thought.value !== EM_TOKEN && searchRegexp.test(thought.value) ) @@ -81,7 +81,7 @@ const SearchSubthoughts: FC> = ({ search, arch {formatNumber(children.length)} match{children.length === 1 ? '' : 'es'} for "{search}" diff --git a/src/components/Subthoughts.tsx b/src/components/Subthoughts.tsx index 666a1926a3b..eaadeb4b645 100644 --- a/src/components/Subthoughts.tsx +++ b/src/components/Subthoughts.tsx @@ -27,19 +27,20 @@ import { isDivider, isEM, isFunction, - isRoot, parseJsonSafe, pathToContext, - rootedParentOf, isDescendantPath, sumSubthoughtsLength, + isRoot, unroot, + isAbsolute, } from '../util' // selectors import { attribute, attributeEquals, + childrenFilterPredicate, getChildPath, appendChildPath, getContextsSortedAndRanked, @@ -50,8 +51,8 @@ import { getAllChildren, getChildrenRanked, getAllChildrenSorted, - isChildVisibleWithCursorCheck, isContextViewActive, + rootedParentOf, } from '../selectors' /** The type of the exported Subthoughts. */ @@ -88,8 +89,11 @@ const mapStateToProps = (state: State, props: SubthoughtsProps) => { cursor, dataNonce, showHiddenThoughts, + rootContext } = state + const isAbsoluteContext = isAbsolute(rootContext) + const resolvedPath = props.path ?? props.simplePath // check if the cursor path includes the current thought @@ -138,7 +142,8 @@ const mapStateToProps = (state: State, props: SubthoughtsProps) => { simplePath: simplePathLive, // re-render if children change __render: getAllChildren(state, pathToContext(simplePathLive)), - isExpanded: store.getState().expanded[hashContext(pathToContext(resolvedPath))] + isExpanded: store.getState().expanded[hashContext(pathToContext(resolvedPath))], + isAbsoluteContext, } } @@ -183,8 +188,8 @@ const drop = (props: SubthoughtsProps, monitor: DropTargetMonitor) => { }]) const isRootOrEM = isRoot(thoughtsFrom) || isEM(thoughtsFrom) - const oldContext = rootedParentOf(pathToContext(thoughtsFrom)) - const newContext = rootedParentOf(pathToContext(newPath)) + const oldContext = rootedParentOf(state, pathToContext(thoughtsFrom)) + const newContext = rootedParentOf(state, pathToContext(newPath)) const sameContext = equalArrays(oldContext, newContext) // cannot drop on itself @@ -312,7 +317,8 @@ export const SubthoughtsComponent = ({ sort: contextSort, simplePath, showHiddenThoughts, - isExpanded + isExpanded, + isAbsoluteContext }: SubthoughtsProps & ReturnType & ReturnType) => { // render @@ -355,7 +361,8 @@ export const SubthoughtsComponent = ({ const editIndex = cursor && children && show ? children.findIndex(child => { return cursor[depth] && cursor[depth].rank === child.rank }) : 0 - const filteredChildren = children.filter(isChildVisibleWithCursorCheck(state, resolvedPath, simplePath)) + + const filteredChildren = children.filter(childrenFilterPredicate(state, resolvedPath, simplePath)) const proposedPageSize = isRoot(simplePath) ? Infinity @@ -440,6 +447,7 @@ export const SubthoughtsComponent = ({ > {filteredChildren .map((child, i) => { + if (i >= proposedPageSize) { return null } diff --git a/src/components/Superscript.tsx b/src/components/Superscript.tsx index eb3395a037f..ae85a6e4a3a 100644 --- a/src/components/Superscript.tsx +++ b/src/components/Superscript.tsx @@ -1,8 +1,8 @@ import React, { FC } from 'react' import { connect } from 'react-redux' -import { exists, getContexts } from '../selectors' -import { ROOT_TOKEN } from '../constants' -import { parentOf, equalArrays, head, headValue, pathToContext, rootedParentOf } from '../util' +import { exists, getContexts, rootedParentOf } from '../selectors' +import { HOME_TOKEN } from '../constants' +import { parentOf, equalArrays, head, headValue, pathToContext } from '../util' import { State } from '../util/initialState' import { Child, Context, Index, SimplePath } from '../types' @@ -24,12 +24,12 @@ interface SuperscriptProps { const mapStateToProps = (state: State, props: SuperscriptProps) => { const { contextViews, cursor, showHiddenThoughts, showModal } = state - const cursorContext = cursor ? pathToContext(cursor) : [ROOT_TOKEN] + const cursorContext = cursor ? pathToContext(cursor) : [HOME_TOKEN] const editing = equalArrays(cursorContext, pathToContext(props.simplePath || [])) && exists(state, headValue(cursor || [])) const simplePath = props.showContexts && props.simplePath - ? rootedParentOf(props.simplePath) + ? rootedParentOf(state, props.simplePath) : props.simplePath const thoughts = props.thoughts || pathToContext(simplePath) diff --git a/src/components/Thought.tsx b/src/components/Thought.tsx index 792f2d8de8c..49c37476c7e 100644 --- a/src/components/Thought.tsx +++ b/src/components/Thought.tsx @@ -39,13 +39,12 @@ import { isDocumentEditable, isEM, isFunction, - isRoot, isURL, parseJsonSafe, pathToContext, publishMode, - rootedParentOf, isDescendantPath, + isRoot, unroot, } from '../util' @@ -64,6 +63,7 @@ import { hasChildren, isBefore, isContextViewActive, + rootedParentOf, } from '../selectors' /********************************************************************** @@ -157,14 +157,14 @@ const mapStateToProps = (state: State, props: ThoughtContainerProps) => { const isCursorParent = distance === 2 // grandparent - ? equalPath(rootedParentOf(parentOf(cursor || [])), path) && getChildrenRanked(state, pathToContext(cursor || [])).length === 0 + ? equalPath(rootedParentOf(state, parentOf(cursor || [])), path) && getChildrenRanked(state, pathToContext(cursor || [])).length === 0 // parent : equalPath(parentOf(cursor || []), path) const contextBinding = parseJsonSafe(attribute(state, pathToContext(simplePathLive), '=bindContext') ?? '') as SimplePath | undefined const isCursorGrandparent = - equalPath(rootedParentOf(parentOf(cursor || [])), path) + equalPath(rootedParentOf(state, parentOf(cursor || [])), path) const children = childrenForced || getChildrenRanked(state, pathToContext(contextBinding || simplePathLive)) const value = headValue(simplePathLive) @@ -268,8 +268,8 @@ const canDrop = (props: ConnectedThoughtContainerProps, monitor: DropTargetMonit const isHidden = distance >= 2 const isSelf = equalPath(thoughtsTo, thoughtsFrom) const isDescendant = isDescendantPath(thoughtsTo, thoughtsFrom) && !isSelf - const oldContext = rootedParentOf(thoughtsFrom) - const newContext = rootedParentOf(thoughtsTo) + const oldContext = rootedParentOf(state, thoughtsFrom) + const newContext = rootedParentOf(state, thoughtsTo) const sameContext = equalArrays(oldContext, newContext) // do not drop on descendants (exclusive) or thoughts hidden by autofocus @@ -288,8 +288,8 @@ const drop = (props: ThoughtContainerProps, monitor: DropTargetMonitor) => { const { simplePath: thoughtsFrom } = monitor.getItem() const thoughtsTo = props.simplePathLive! const isRootOrEM = isRoot(thoughtsFrom) || isEM(thoughtsFrom) - const oldContext = rootedParentOf(thoughtsFrom) - const newContext = rootedParentOf(thoughtsTo) + const oldContext = rootedParentOf(state, thoughtsFrom) + const newContext = rootedParentOf(state, thoughtsTo) const sameContext = equalArrays(oldContext, newContext) // cannot move root or em context @@ -379,11 +379,13 @@ const Thought = ({ const isRoot = simplePath.length === 1 const isRootChildLeaf = simplePath.length === 2 && isLeaf + const state = store.getState() + return
    {!(publish && (isRoot || isRootChildLeaf)) && !hideBullet && } - {showContextBreadcrumbs && !isRoot ? + {showContextBreadcrumbs && !isRoot ? : showContexts && simplePath.length > 2 ? { store.dispatch(expandContextThought(path)) }}>... @@ -488,7 +490,7 @@ const ThoughtContainer = ({ const value = headValue(simplePathLive!) // if rendering as a context and the thought is the root, render home icon instead of Editable - const homeContext = showContexts && isRoot([head(rootedParentOf(simplePath))]) + const homeContext = showContexts && isRoot([head(rootedParentOf(state, simplePath))]) // prevent fading out cursor parent // there is a special case here for the cursor grandparent when the cursor is a leaf diff --git a/src/components/ThoughtAnnotation.tsx b/src/components/ThoughtAnnotation.tsx index 6f39141b2ae..7adf39e429b 100644 --- a/src/components/ThoughtAnnotation.tsx +++ b/src/components/ThoughtAnnotation.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames' import { store } from '../store' import { REGEXP_PUNCTUATIONS } from '../constants' import { setCursor } from '../action-creators' -import { decodeThoughtsUrl, getContexts, getAllChildren, theme } from '../selectors' +import { decodeThoughtsUrl, getContexts, getAllChildren, theme, rootedParentOf } from '../selectors' import { State } from '../util/initialState' import { Connected, Context, SimplePath, ThoughtContext, Path } from '../types' @@ -17,7 +17,6 @@ import { headValue, pathToContext, publishMode, - rootedParentOf, } from '../util' // components @@ -122,7 +121,7 @@ const ThoughtAnnotation = ({ simplePath, showContexts, showContextBreadcrumbs, h return
    - {showContextBreadcrumbs && simplePath.length > 1 && } + {showContextBreadcrumbs && simplePath.length > 1 && } {homeContext ? diff --git a/src/components/Tutorial/TutorialNavigationNext.js b/src/components/Tutorial/TutorialNavigationNext.js index 5bd70c9a56f..b136ee9a1a3 100644 --- a/src/components/Tutorial/TutorialNavigationNext.js +++ b/src/components/Tutorial/TutorialNavigationNext.js @@ -7,7 +7,7 @@ import { getSetting, getAllChildren } from '../../selectors' import { tutorialNext } from '../../action-creators' import { - ROOT_TOKEN, + HOME_TOKEN, TUTORIAL2_STEP_CONTEXT1_SUBTHOUGHT, TUTORIAL2_STEP_CONTEXT2_SUBTHOUGHT, TUTORIAL2_STEP_CONTEXT_VIEW_EXAMPLES, @@ -29,7 +29,7 @@ const mapStateToProps = state => { contextIndex, cursor, expanded, - rootSubthoughts: getAllChildren(state, [ROOT_TOKEN]), + rootSubthoughts: getAllChildren(state, [HOME_TOKEN]), tutorialChoice: +getSetting(state, 'Tutorial Choice') || 0, tutorialStep: +getSetting(state, 'Tutorial Step') || 1, } diff --git a/src/components/Tutorial/TutorialStepAutoExpand.js b/src/components/Tutorial/TutorialStepAutoExpand.js index 2628b2a0dab..2b67ebe3792 100644 --- a/src/components/Tutorial/TutorialStepAutoExpand.js +++ b/src/components/Tutorial/TutorialStepAutoExpand.js @@ -3,7 +3,7 @@ import { store } from '../../store' import { isTouch } from '../../browser' // constants -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' // util import { @@ -28,7 +28,7 @@ const TutorialStepAutoExpand = ({ cursor, rootSubthoughts = [] } = {}) => { const isCursorLeaf = cursorChildren.length === 0 const ancestorThought = isCursorLeaf ? parentOf(parentOf(cursorContext)) : parentOf(cursorContext) - const ancestorThoughtChildren = getAllChildren(state, ancestorThought.length === 0 ? [ROOT_TOKEN] : ancestorThought) + const ancestorThoughtChildren = getAllChildren(state, ancestorThought.length === 0 ? [HOME_TOKEN] : ancestorThought) const isCursorRootChildren = (cursor || []).length === 1 const isCursorCollapsePossible = ancestorThoughtChildren.length > 1 && !(isCursorRootChildren && isCursorLeaf) @@ -51,7 +51,7 @@ const TutorialStepAutoExpand = ({ cursor, rootSubthoughts = [] } = {}) => { : Add a subthought and I'll show you. - : getAllChildren(state, [ROOT_TOKEN]).length === 0 ? ' Oops! There are no thoughts in the tree. Please add some thoughts to continue with the tutorial.' : ' Oops! Please focus on one of the thoughts.' + : getAllChildren(state, [HOME_TOKEN]).length === 0 ? ' Oops! There are no thoughts in the tree. Please add some thoughts to continue with the tutorial.' : ' Oops! Please focus on one of the thoughts.' }

    diff --git a/src/components/Tutorial/index.js b/src/components/Tutorial/index.js index 5f91e202b83..d0915637659 100644 --- a/src/components/Tutorial/index.js +++ b/src/components/Tutorial/index.js @@ -11,7 +11,7 @@ import { tutorial } from '../../action-creators' // constants import { - ROOT_TOKEN, + HOME_TOKEN, TUTORIAL2_STEP_CONTEXT1_HINT, TUTORIAL2_STEP_CONTEXT1_PARENT_HINT, TUTORIAL2_STEP_CONTEXT1_SUBTHOUGHT_HINT, @@ -44,7 +44,7 @@ const mapStateToProps = state => { return { contextViews, cursor, - rootChildren: getParent(state, [ROOT_TOKEN])?.children, + rootChildren: getParent(state, [HOME_TOKEN])?.children, tutorialChoice: +getSetting(state, 'Tutorial Choice') || 0, tutorialStep: +getSetting(state, 'Tutorial Step') || 1 } diff --git a/src/components/__tests__/Content.ts b/src/components/__tests__/Content.ts index 257890ad648..84075182105 100644 --- a/src/components/__tests__/Content.ts +++ b/src/components/__tests__/Content.ts @@ -3,7 +3,7 @@ import { store } from '../../store' import { deleteThought, importText } from '../../action-creators' import createTestApp, { cleanupTestApp } from '../../test-helpers/createTestApp' import NewThoughtInstructions from '../NewThoughtInstructions' -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' let wrapper: ReactWrapper // eslint-disable-line fp/no-let @@ -19,7 +19,7 @@ it('show NewThoughtInstructions when there are no visible thoughts in the root c expect(wrapper.find(NewThoughtInstructions)).toHaveLength(1) store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b diff --git a/src/components/__tests__/Divider.ts b/src/components/__tests__/Divider.ts index e6b6e9262b4..325a708c7c5 100644 --- a/src/components/__tests__/Divider.ts +++ b/src/components/__tests__/Divider.ts @@ -1,6 +1,6 @@ import { ReactWrapper } from 'enzyme' import { store } from '../../store' -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { getChildrenRanked } from '../../selectors' import windowEvent from '../../test-helpers/windowEvent' import createTestApp, { cleanupTestApp } from '../../test-helpers/createTestApp' @@ -65,7 +65,7 @@ it('do not convert "-" to divider', async () => { jest.runOnlyPendingTimers() // state - const rootSubthoughts = getChildrenRanked(store.getState(), [ROOT_TOKEN]) + const rootSubthoughts = getChildrenRanked(store.getState(), [HOME_TOKEN]) expect(rootSubthoughts).toHaveLength(1) expect(rootSubthoughts[0]).toMatchObject({ value: '-', rank: 0 }) @@ -90,7 +90,7 @@ it('do not convert "—" (emdash) to divider', async () => { jest.runOnlyPendingTimers() // state - const rootSubthoughts = getChildrenRanked(store.getState(), [ROOT_TOKEN]) + const rootSubthoughts = getChildrenRanked(store.getState(), [HOME_TOKEN]) expect(rootSubthoughts).toHaveLength(1) expect(rootSubthoughts[0]).toMatchObject({ value: '—', rank: 0 }) diff --git a/src/components/__tests__/Editable.ts b/src/components/__tests__/Editable.ts index 2638f0a0729..e9146bdb3df 100644 --- a/src/components/__tests__/Editable.ts +++ b/src/components/__tests__/Editable.ts @@ -1,6 +1,6 @@ import { ReactWrapper } from 'enzyme' import { store } from '../../store' -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' import createTestApp, { cleanupTestApp } from '../../test-helpers/createTestApp' import { importText } from '../../action-creators' import { setCursorFirstMatchActionCreator } from '../../test-helpers/setCursorFirstMatch' @@ -21,7 +21,7 @@ it('reset content editable inner html on thought bump', async () => { // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b` diff --git a/src/components/__tests__/Subthoughts.ts b/src/components/__tests__/Subthoughts.ts index 82d3cd48850..943d925a80f 100644 --- a/src/components/__tests__/Subthoughts.ts +++ b/src/components/__tests__/Subthoughts.ts @@ -1,7 +1,7 @@ import { ReactWrapper } from 'enzyme' import { store } from '../../store' import createTestApp, { cleanupTestApp } from '../../test-helpers/createTestApp' -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' import { equalArrays, pathToContext, timestamp } from '../../util' import { importText, setCursor, toggleAttribute } from '../../action-creators' import Editable from '../Editable' @@ -43,7 +43,7 @@ it('normal view', () => { // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: `- a - b - c` @@ -77,7 +77,7 @@ describe('context view', () => { // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - m @@ -134,7 +134,7 @@ describe('context view', () => { // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - one @@ -210,7 +210,7 @@ describe('context view', () => { // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -256,7 +256,7 @@ describe('hidden thoughts', () => { // import thoughts store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, preventSetCursor: true, text: ` - a @@ -327,7 +327,7 @@ describe('hidden thoughts', () => { // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - d @@ -408,7 +408,7 @@ describe('expand thoughts', () => { // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, preventSetCursor: true, text: ` - a diff --git a/src/components/__tests__/Thought.ts b/src/components/__tests__/Thought.ts index 94d684612a3..4e1dd9e71a8 100644 --- a/src/components/__tests__/Thought.ts +++ b/src/components/__tests__/Thought.ts @@ -1,6 +1,6 @@ import { ReactWrapper } from 'enzyme' import { store } from '../../store' -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { HOME_PATH, HOME_TOKEN } from '../../constants' import { getChildrenRanked } from '../../selectors' import { importText, render } from '../../action-creators' import windowEvent from '../../test-helpers/windowEvent' @@ -37,7 +37,7 @@ it('create, navigate, and edit thoughts', async () => { jest.runOnlyPendingTimers() // state - const rootSubthoughts = getChildrenRanked(store.getState(), [ROOT_TOKEN]) + const rootSubthoughts = getChildrenRanked(store.getState(), [HOME_TOKEN]) expect(rootSubthoughts).toHaveLength(1) expect(rootSubthoughts[0]).toMatchObject({ value: 'a', rank: 0 }) @@ -101,7 +101,7 @@ it.skip('caret is set on new subthought', async () => { it('do not allow edit to duplicate thought', async () => { store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -119,7 +119,7 @@ it('do not allow edit to duplicate thought', async () => { windowEvent('keydown', { key: 'Escape' }) // state - const rootSubthoughts = getChildrenRanked(store.getState(), [ROOT_TOKEN]) + const rootSubthoughts = getChildrenRanked(store.getState(), [HOME_TOKEN]) expect(rootSubthoughts).toMatchObject([ { value: 'a', rank: 0 }, { value: 'b', rank: 1 }, @@ -151,7 +151,7 @@ it('allow duplicate empty thoughts', async () => { windowEvent('keydown', { key: 'Escape' }) // state - const rootSubthoughts = getChildrenRanked(store.getState(), [ROOT_TOKEN]) + const rootSubthoughts = getChildrenRanked(store.getState(), [HOME_TOKEN]) expect(rootSubthoughts).toMatchObject([ { value: '', rank: 0 }, { value: 'a', rank: 1 }, diff --git a/src/components/__tests__/drag-and-drop.ts b/src/components/__tests__/drag-and-drop.ts index e50b8af8eff..84d6d32f3b1 100644 --- a/src/components/__tests__/drag-and-drop.ts +++ b/src/components/__tests__/drag-and-drop.ts @@ -5,7 +5,7 @@ import createTestApp, { cleanupTestApp } from '../../test-helpers/createTestApp' import Thought from '../Thought' import Subthoughts from '../Subthoughts' -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { HOME_PATH, HOME_TOKEN } from '../../constants' import { equalArrays, pathToContext } from '../../util' import { exportContext } from '../../selectors' import { importText } from '../../action-creators' @@ -76,7 +76,7 @@ const simulateDragAndDrop = ({ source, drop, type }: { source: Context, drop: Co it('drop as sibling', () => { store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -92,9 +92,9 @@ it('drop as sibling', () => { type: 'sibling' }) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedExport = `- ${ROOT_TOKEN} + const expectedExport = `- ${HOME_TOKEN} - b - a - c @@ -105,7 +105,7 @@ it('drop as sibling', () => { it('drop as child (Drop end)', () => { store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -121,9 +121,9 @@ it('drop as child (Drop end)', () => { type: 'child' }) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedExport = `- ${ROOT_TOKEN} + const expectedExport = `- ${HOME_TOKEN} - a - b - c @@ -136,7 +136,7 @@ it('prevent drop into descendants', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -153,9 +153,9 @@ it('prevent drop into descendants', () => { type: 'child' }) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedExport = `- ${ROOT_TOKEN} + const expectedExport = `- ${HOME_TOKEN} - a - b - c` diff --git a/src/constants.ts b/src/constants.ts index b775dd81dfa..b7b7e577e05 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -98,12 +98,17 @@ export const SCHEMA_LATEST = SCHEMA_META_SETTINGS export const EMPTY_TOKEN = '__EMPTY__' // store the root string as a token that is not likely to be written by the user (bad things will happen) -export const ROOT_TOKEN = '__ROOT__' +export const HOME_TOKEN = '__ROOT__' // token for hidden system context export const EM_TOKEN = '__EM__' -export const RANKED_ROOT = [{ value: ROOT_TOKEN, rank: 0 }] as SimplePath +export const ABSOLUTE_TOKEN = '__ABSOLUTE__' + +export const ROOT_CONTEXTS = [HOME_TOKEN, ABSOLUTE_TOKEN] + +export const HOME_PATH = [{ value: HOME_TOKEN, rank: 0 }] as SimplePath +export const ABSOLUTE_PATH = [{ value: ABSOLUTE_TOKEN, rank: 0 }] as SimplePath export const ALLOW_SINGLE_CONTEXT = false diff --git a/src/data-providers/__tests__/dexie.ts b/src/data-providers/__tests__/dexie.ts index c905ab3f403..7dd7137ceca 100644 --- a/src/data-providers/__tests__/dexie.ts +++ b/src/data-providers/__tests__/dexie.ts @@ -1,5 +1,5 @@ import { store } from '../../store' -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialize } from '../../initialize' import { getThought } from '../../selectors' import { clear, newThought } from '../../action-creators' @@ -62,7 +62,7 @@ describe('integration', () => { fakeTimer.useRealTimer() // Note: Always use real timer before awaiting db calls. https://github.com/cybersemics/em/issues/919#issuecomment-739135971 - const parentEntryRoot = await getContext(db, [ROOT_TOKEN]) + const parentEntryRoot = await getContext(db, [HOME_TOKEN]) expect(parentEntryRoot).toMatchObject({ children: [{ value: 'a', rank: 0 }] @@ -77,7 +77,7 @@ describe('integration', () => { { type: 'newThought', value: '' }, { type: 'existingThoughtChange', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], oldValue: '', newValue: 'a', path: [{ value: '', rank: 0 }], @@ -88,7 +88,7 @@ describe('integration', () => { fakeTimer.useRealTimer() - const parentEntryRoot = await getContext(db, [ROOT_TOKEN]) + const parentEntryRoot = await getContext(db, [HOME_TOKEN]) expect(parentEntryRoot).toMatchObject({ children: [{ value: 'a', rank: 0 }] diff --git a/src/reducers/__tests__/archiveThought.ts b/src/reducers/__tests__/archiveThought.ts index e213db62db9..60632062133 100644 --- a/src/reducers/__tests__/archiveThought.ts +++ b/src/reducers/__tests__/archiveThought.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' // TODO: Why does util have to be imported before selectors and reducers? import { initialState, reducerFlow } from '../../util' @@ -17,9 +17,9 @@ it('archive a thought', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - =archive - b - a`) @@ -38,9 +38,9 @@ it('deduplicate archived thoughts with the same value', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - =archive - b - a`) @@ -57,9 +57,9 @@ it('do nothing if there is no cursor', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) }) @@ -76,9 +76,9 @@ it('move to top of archive', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - =archive - b - c @@ -96,9 +96,9 @@ it('permanently delete empty thought', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) }) @@ -115,9 +115,9 @@ it('permanently delete thought from archive', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - =archive - a`) @@ -135,9 +135,9 @@ it('permanently delete archive', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) // ensure =archive is removed from thoughtIndex @@ -159,9 +159,9 @@ it('permanently delete archive with descendants', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN}`) + expect(exported).toBe(`- ${HOME_TOKEN}`) // ensure =archive is removed from thoughtIndex expect(getContexts(stateNew, '=archive')) @@ -256,9 +256,9 @@ it('empty thought should be archived if it has descendants', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - =archive - ${''/* prevent trim_trailing_whitespace */} - b @@ -284,8 +284,8 @@ describe('context view', () => { ] const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') - const expected = `- ${ROOT_TOKEN} + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') + const expected = `- ${HOME_TOKEN} - a - m - x @@ -309,8 +309,8 @@ describe('context view', () => { ] const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') - const expected = `- ${ROOT_TOKEN} + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') + const expected = `- ${HOME_TOKEN} - a - =archive - m diff --git a/src/reducers/__tests__/bumpThoughtDown.ts b/src/reducers/__tests__/bumpThoughtDown.ts index 060fce7f096..9c558d20d2c 100644 --- a/src/reducers/__tests__/bumpThoughtDown.ts +++ b/src/reducers/__tests__/bumpThoughtDown.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' import { bumpThoughtDown, cursorBack, newSubthought, newThought } from '../index' @@ -13,9 +13,9 @@ it('bump leaf', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - ${''} - b`) @@ -50,9 +50,9 @@ it('bump thought with children', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - ${''} - b @@ -73,9 +73,9 @@ it('bump thought with children multiple times', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - ${''} - ${''} @@ -93,9 +93,9 @@ it('bump root leaf', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - ${''} - a`) @@ -112,9 +112,9 @@ it('bump root thought with children', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - ${''} - a - b`) diff --git a/src/reducers/__tests__/collapseContext.ts b/src/reducers/__tests__/collapseContext.ts index d1656685c1a..a063918e07e 100644 --- a/src/reducers/__tests__/collapseContext.ts +++ b/src/reducers/__tests__/collapseContext.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { collapseContext, cursorBack, cursorUp, newSubthought, newThought } from '../../reducers' import { exportContext } from '../../selectors' @@ -14,9 +14,9 @@ it('do nothing on leaf', () => { // run steps through reducer flow and export as plaintext for readable test const state = initialState() const stateNew = reducerFlow(steps)(state) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -34,9 +34,9 @@ it('collapse context with single child', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =archive - b @@ -60,9 +60,9 @@ it('collapse context with multiple children', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =archive - b @@ -89,9 +89,9 @@ it('merge children', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =archive - b @@ -119,9 +119,9 @@ it('merge duplicate children', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =archive - b diff --git a/src/reducers/__tests__/cursorDown.ts b/src/reducers/__tests__/cursorDown.ts index cace11c269a..9ccf2c5ce70 100644 --- a/src/reducers/__tests__/cursorDown.ts +++ b/src/reducers/__tests__/cursorDown.ts @@ -1,5 +1,5 @@ import { initialState, pathToContext, reducerFlow } from '../../util' -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' import { rankThoughtsFirstMatch } from '../../selectors' import { State } from '../../util/initialState' @@ -147,7 +147,7 @@ describe('context view', () => { - y` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursor({ path: [{ value: 'a', rank: 0 }, { value: 'm', rank: 1 }] }), toggleContextView, cursorDown, @@ -167,7 +167,7 @@ describe('context view', () => { - n` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a', 'm']), toggleContextView, cursorDown @@ -189,7 +189,7 @@ describe('context view', () => { - y` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a', 'm']), toggleContextView, setCursorFirstMatch(['a', 'm', 'a']), @@ -212,7 +212,7 @@ describe('context view', () => { - m` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursor({ path: [{ value: 'a', rank: 0 }, { value: 'm', rank: 1 }] }), toggleContextView, setCursor({ path: [{ value: 'a', rank: 0 }, { value: 'm', rank: 1 }, { value: 'a', rank: 0 }] }), @@ -237,7 +237,7 @@ describe('context view', () => { - y` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a', 'm']), toggleContextView, setCursorFirstMatch(['a', 'm', 'a', 'x']), @@ -263,7 +263,7 @@ describe('context view', () => { - z` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), (state: State) => setCursor(state, { path: rankThoughtsFirstMatch(state, ['a', 'm']) }), toggleContextView, (state: State) => setCursor(state, { path: rankThoughtsFirstMatch(state, ['a', 'm', 'b', 'y']) }), @@ -288,7 +288,7 @@ describe('context view', () => { - y` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), (state: State) => setCursor(state, { path: rankThoughtsFirstMatch(state, ['a', 'm']) }), toggleContextView, (state: State) => setCursor(state, { path: rankThoughtsFirstMatch(state, ['a', 'm', 'b', 'y']) }), @@ -317,7 +317,7 @@ describe('context view', () => { ` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), (state: State) => setCursor(state, { path: rankThoughtsFirstMatch(state, ['a', 'm']) }), toggleContextView, (state: State) => setCursor(state, { path: rankThoughtsFirstMatch(state, ['a', 'm', 'a', 'x']) }), diff --git a/src/reducers/__tests__/cursorUp.ts b/src/reducers/__tests__/cursorUp.ts index ba3b9f27bc7..fa9420bd6d3 100644 --- a/src/reducers/__tests__/cursorUp.ts +++ b/src/reducers/__tests__/cursorUp.ts @@ -1,5 +1,5 @@ import { initialState, pathToContext, reducerFlow } from '../../util' -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' import { cursorUp, importText, newSubthought, newThought, setCursor, toggleContextView, toggleHiddenThoughts } from '../../reducers' import { rankThoughtsFirstMatch } from '../../selectors' import { State } from '../../util/initialState' @@ -92,7 +92,7 @@ describe('context view', () => { - y` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), (state: State) => setCursor(state, { path: rankThoughtsFirstMatch(state, ['a', 'm']) }), toggleContextView, (state: State) => setCursor(state, { path: rankThoughtsFirstMatch(state, ['a', 'm', 'a']) }), diff --git a/src/reducers/__tests__/deleteAttribute.ts b/src/reducers/__tests__/deleteAttribute.ts index 18fa9946058..b80468992fd 100644 --- a/src/reducers/__tests__/deleteAttribute.ts +++ b/src/reducers/__tests__/deleteAttribute.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' @@ -30,9 +30,9 @@ it('delete attribute', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) }) diff --git a/src/reducers/__tests__/deleteEmptyThought.ts b/src/reducers/__tests__/deleteEmptyThought.ts index 26c3ceec35d..d354fdefbeb 100644 --- a/src/reducers/__tests__/deleteEmptyThought.ts +++ b/src/reducers/__tests__/deleteEmptyThought.ts @@ -4,7 +4,7 @@ import { importText } from '../../action-creators' import { store } from '../../store' import createTestApp, { cleanupTestApp } from '../../test-helpers/createTestApp' import { setCursorFirstMatchActionCreator } from '../../test-helpers/setCursorFirstMatch' -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { HOME_PATH, HOME_TOKEN } from '../../constants' // reducers import cursorBack from '../cursorBack' @@ -24,9 +24,9 @@ it('delete empty thought', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) }) @@ -40,9 +40,9 @@ it('do not delete non-empty thought', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) }) @@ -58,9 +58,9 @@ it('do not delete thought with children', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - ${''/* prevent trim_trailing_whitespace */} - 1`) @@ -77,9 +77,9 @@ it('archive thought with hidden children', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - =archive - ${''/* prevent trim_trailing_whitespace */} - =one`) @@ -97,9 +97,9 @@ it('do nothing if there is no cursor', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -115,9 +115,9 @@ it('merge thoughts', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - ab`) }) @@ -135,9 +135,9 @@ it('insert second thought\'s children', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - ab - b1 - b2`) @@ -157,9 +157,9 @@ it('do not change first thought\'s children', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - ab - a1 - a2`) @@ -254,7 +254,7 @@ describe('mount', () => { it('after merging siblings, caret should be in between', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - apple - banana` diff --git a/src/reducers/__tests__/deleteThought.ts b/src/reducers/__tests__/deleteThought.ts index c077d87dd09..2bd6b8d8e64 100644 --- a/src/reducers/__tests__/deleteThought.ts +++ b/src/reducers/__tests__/deleteThought.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' import { store } from '../../store' @@ -22,9 +22,9 @@ it('delete thought within root', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) }) @@ -40,9 +40,9 @@ it('delete thought with no cursor should do nothing ', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -58,9 +58,9 @@ it('delete thought within context', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) }) @@ -77,9 +77,9 @@ it('delete descendants', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) }) diff --git a/src/reducers/__tests__/existingThoughtChange.ts b/src/reducers/__tests__/existingThoughtChange.ts index 5878fdc0d4f..4ea4ea0549d 100644 --- a/src/reducers/__tests__/existingThoughtChange.ts +++ b/src/reducers/__tests__/existingThoughtChange.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext, getContexts, getAllChildren } from '../../selectors' import { existingThoughtChange, newThought, setCursor } from '../../reducers' @@ -13,24 +13,24 @@ it('edit a thought', () => { existingThoughtChange({ newValue: 'aa', oldValue: 'a', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'a', rank: 0 }] as SimplePath }) ] // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - aa - b`) // aa should exist in ROOT context expect(getContexts(stateNew, 'aa')) .toMatchObject([{ - context: [ROOT_TOKEN] + context: [HOME_TOKEN] }]) - expect(getAllChildren(stateNew, [ROOT_TOKEN])) + expect(getAllChildren(stateNew, [HOME_TOKEN])) .toMatchObject([{ value: 'b', rank: 1 }, { value: 'aa', rank: 0 }]) // cursor should be at /aa @@ -54,9 +54,9 @@ it('edit a descendant', () => { ] // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - aa1 - b`) @@ -81,16 +81,16 @@ it('edit a thought with descendants', () => { existingThoughtChange({ newValue: 'aa', oldValue: 'a', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'a', rank: 0 }] as SimplePath }) ] // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - aa - a1 - a2`) @@ -98,7 +98,7 @@ it('edit a thought with descendants', () => { // aa should exist in ROOT context expect(getContexts(stateNew, 'aa')) .toMatchObject([{ - context: [ROOT_TOKEN] + context: [HOME_TOKEN] }]) expect(getAllChildren(stateNew, ['aa'])) .toMatchObject([{ value: 'a1', rank: 0 }, { value: 'a2', rank: 1 }]) @@ -122,9 +122,9 @@ it('edit a thought existing in mutliple contexts', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - abc - b @@ -157,9 +157,9 @@ it('edit a thought that exists in another context', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - ab - b @@ -201,9 +201,9 @@ it('edit a child with the same value as its parent', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - ab`) @@ -230,22 +230,22 @@ it('do not duplicate children when new and old context are same', () => { existingThoughtChange({ newValue: 'as', oldValue: 'a', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'a', rank: 0 }] as SimplePath }), existingThoughtChange({ newValue: 'a', oldValue: 'as', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'as', rank: 0 }] as SimplePath }) ] // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) }) diff --git a/src/reducers/__tests__/existingThoughtDelete.ts b/src/reducers/__tests__/existingThoughtDelete.ts index 46b59fe5491..3223c22a7e3 100644 --- a/src/reducers/__tests__/existingThoughtDelete.ts +++ b/src/reducers/__tests__/existingThoughtDelete.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { getContexts, getAllChildren, getParent } from '../../selectors' import { existingThoughtDelete, newSubthought, newThought } from '../../reducers' @@ -9,7 +9,7 @@ it('delete from root', () => { newThought('a'), newThought('b'), existingThoughtDelete({ - context: [ROOT_TOKEN], + context: [HOME_TOKEN], thoughtRanked: { value: 'b', rank: 1 }, }), ] @@ -19,12 +19,12 @@ it('delete from root', () => { const stateNew = reducerFlow(steps)(state) /** Gets the root Parent from a state's contextIndex. */ - const rootParent = getParent(stateNew, [ROOT_TOKEN]) + const rootParent = getParent(stateNew, [HOME_TOKEN]) // contextIndex expect(rootParent) .toMatchObject({ - context: [ROOT_TOKEN], + context: [HOME_TOKEN], children: [{ value: 'a', rank: 0, @@ -44,7 +44,7 @@ it('delete descendants of root thought', () => { newSubthought('b'), newSubthought('c'), existingThoughtDelete({ - context: [ROOT_TOKEN], + context: [HOME_TOKEN], thoughtRanked: { value: 'a', rank: 0 }, }), ] @@ -53,7 +53,7 @@ it('delete descendants of root thought', () => { const stateNew = reducerFlow(steps)(initialState()) // cnntextIndex - expect(getAllChildren(stateNew, [ROOT_TOKEN])).toEqual([]) + expect(getAllChildren(stateNew, [HOME_TOKEN])).toEqual([]) expect(getAllChildren(stateNew, ['a'])).toEqual([]) expect(getAllChildren(stateNew, ['b', 'c'])).toEqual([]) @@ -70,7 +70,7 @@ it('delete thought with duplicate child', () => { newThought('a'), newSubthought('a'), existingThoughtDelete({ - context: [ROOT_TOKEN], + context: [HOME_TOKEN], thoughtRanked: { value: 'a', rank: 0 }, }), ] @@ -79,7 +79,7 @@ it('delete thought with duplicate child', () => { const stateNew = reducerFlow(steps)(initialState()) // cnntextIndex - expect(getAllChildren(stateNew, [ROOT_TOKEN])).toEqual([]) + expect(getAllChildren(stateNew, [HOME_TOKEN])).toEqual([]) expect(getAllChildren(stateNew, ['a'])).toEqual([]) // thoughtIndex diff --git a/src/reducers/__tests__/existingThoughtMove.ts b/src/reducers/__tests__/existingThoughtMove.ts index f41e965e10a..e4142c9073a 100644 --- a/src/reducers/__tests__/existingThoughtMove.ts +++ b/src/reducers/__tests__/existingThoughtMove.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { HOME_PATH, HOME_TOKEN } from '../../constants' import { equalArrays, initialState, reducerFlow } from '../../util' import { exportContext, getContexts, getThought, getAllChildren, getChildrenRanked } from '../../selectors' import { existingThoughtMove, importText, newSubthought, newThought, setCursor } from '../../reducers' @@ -16,16 +16,16 @@ it('move within root', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - b - a`) // b should exist in the ROOT context expect(getContexts(stateNew, 'b')) .toMatchObject([{ - context: [ROOT_TOKEN], + context: [HOME_TOKEN], rank: -1, }]) @@ -71,9 +71,9 @@ it('move within context', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a2 - a1`) @@ -102,9 +102,9 @@ it('move across contexts', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a1 - b1 @@ -136,9 +136,9 @@ it('move descendants', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - b - b1 - b1.1 @@ -149,7 +149,7 @@ it('move descendants', () => { // context of b should remain to be ROOT expect(getContexts(stateNew, 'b')) .toMatchObject([{ - context: [ROOT_TOKEN], + context: [HOME_TOKEN], rank: -1, }]) @@ -241,7 +241,7 @@ it('move root thought into another root thought', () => { - c` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), existingThoughtMove({ oldPath: [{ value: 'a', rank: 1 }], newPath: [{ value: 'x', rank: 0 }, { value: 'a', rank: 0 }], @@ -250,8 +250,8 @@ it('move root thought into another root thought', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') + expect(exported).toBe(`- ${HOME_TOKEN} - x - a - b @@ -286,7 +286,7 @@ it('move descendants with siblings', () => { - d` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), existingThoughtMove({ oldPath: [{ value: 'a', rank: 0 }, { value: 'b', rank: 0 }], newPath: [{ value: 'b', rank: 1 }], @@ -295,8 +295,8 @@ it('move descendants with siblings', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') + expect(exported).toBe(`- ${HOME_TOKEN} - a - b - c @@ -305,7 +305,7 @@ it('move descendants with siblings', () => { // b should exist in the ROOT context expect(getContexts(stateNew, 'b')) .toMatchObject([{ - context: [ROOT_TOKEN], + context: [HOME_TOKEN], rank: 1, }]) @@ -333,7 +333,7 @@ it('merge duplicate with new rank', () => { - y` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), existingThoughtMove({ oldPath: [{ value: 'm', rank: 1 }], newPath: [{ value: 'a', rank: 0 }, { value: 'm', rank: 0 }], @@ -342,9 +342,9 @@ it('merge duplicate with new rank', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - m - x @@ -373,7 +373,7 @@ it('merge with duplicate with duplicate rank', () => { - y` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), existingThoughtMove({ oldPath: [{ value: 'm', rank: 1 }], newPath: [{ value: 'a', rank: 0 }, { value: 'm', rank: 0 }], @@ -382,9 +382,9 @@ it('merge with duplicate with duplicate rank', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - m - x @@ -413,7 +413,7 @@ it('move with duplicate descendant', () => { - x` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), existingThoughtMove({ oldPath: [{ value: 'b', rank: 1 }], newPath: [{ value: 'a', rank: 0 }, { value: 'b', rank: 0 }], @@ -422,10 +422,10 @@ it('move with duplicate descendant', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') // contextIndex - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b - x @@ -450,7 +450,7 @@ it('move with hash matched descendant', () => { - note` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), existingThoughtMove({ oldPath: [{ value: 'b', rank: 1 }], newPath: [{ value: 'a', rank: 0 }, { value: 'b', rank: 0 }], @@ -458,9 +458,9 @@ it('move with hash matched descendant', () => { ] const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b - =note @@ -485,7 +485,7 @@ it('move with nested duplicate thoughts', () => { - b` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), existingThoughtMove({ oldPath: [{ value: 'c', rank: 1 }, { value: 'a', rank: 0 }], newPath: [{ value: 'a', rank: 0 }], @@ -493,9 +493,9 @@ it('move with nested duplicate thoughts', () => { ] const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b - c`) @@ -523,7 +523,7 @@ it('move with nested duplicate thoughts and merge their children', () => { -d` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), existingThoughtMove({ oldPath: [{ value: 'p', rank: 1 }, { value: 'a', rank: 0 }], newPath: [{ value: 'a', rank: 0 }], @@ -531,9 +531,9 @@ it('move with nested duplicate thoughts and merge their children', () => { ] const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b - c diff --git a/src/reducers/__tests__/indent.ts b/src/reducers/__tests__/indent.ts index 2993c8182f7..f5e13f3c804 100644 --- a/src/reducers/__tests__/indent.ts +++ b/src/reducers/__tests__/indent.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' import { indent, newSubthought, newThought, setCursor } from '../../reducers' @@ -13,9 +13,9 @@ it('indent within root', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -32,9 +32,9 @@ it('indent with no cursor should do nothing ', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -50,9 +50,9 @@ it('indent fully indented thought should do nothing ', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -69,9 +69,9 @@ it('indent within context', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a1 - a2`) diff --git a/src/reducers/__tests__/moveThoughtDown.ts b/src/reducers/__tests__/moveThoughtDown.ts index e12a72c19f9..c861fbcedfc 100644 --- a/src/reducers/__tests__/moveThoughtDown.ts +++ b/src/reducers/__tests__/moveThoughtDown.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' import setCursorFirstMatch from '../../test-helpers/setCursorFirstMatch' @@ -22,9 +22,9 @@ it('move within root', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - b - a`) @@ -42,9 +42,9 @@ it('move within context', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a2 - a1`) @@ -64,9 +64,9 @@ it('move to next uncle', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b - a1 @@ -88,9 +88,9 @@ it('move to next uncle in sorted list', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =sort - Alphabetical @@ -113,9 +113,9 @@ it('prevent move in sorted list when there is no next uncle', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =sort - Alphabetical @@ -138,9 +138,9 @@ it('move descendants', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - b - b1 - b1.1 @@ -161,9 +161,9 @@ it('trying to move last thought of root should do nothing', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -183,9 +183,9 @@ it('trying to move last thought of context with no next uncle should do nothing' // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a1 - a1.1 @@ -204,9 +204,9 @@ it('do nothing when there is no cursor', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) diff --git a/src/reducers/__tests__/moveThoughtUp.ts b/src/reducers/__tests__/moveThoughtUp.ts index 7c81e1091cb..de7b3e56084 100644 --- a/src/reducers/__tests__/moveThoughtUp.ts +++ b/src/reducers/__tests__/moveThoughtUp.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' @@ -20,9 +20,9 @@ it('move within root', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - b - a`) @@ -39,9 +39,9 @@ it('move within context', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a2 - a1`) @@ -60,9 +60,9 @@ it('move to prev uncle', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a1 - b1 @@ -83,9 +83,9 @@ it('move to prev uncle in sorted list', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a1 - b1 @@ -107,9 +107,9 @@ it('prevent move in sorted list when there is no previous uncle', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =sort - Alphabetical @@ -133,9 +133,9 @@ it('move descendants', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - b - b1 - b1.1 @@ -157,9 +157,9 @@ it('trying to move last thought of root should do nothing', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -178,9 +178,9 @@ it('trying to move first thought of context with no prev uncle should do nothing // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b - b1 @@ -200,9 +200,9 @@ it('do nothing when there is no cursor', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) diff --git a/src/reducers/__tests__/newThought.ts b/src/reducers/__tests__/newThought.ts index 9a1605dcf57..1a6c8d52c96 100644 --- a/src/reducers/__tests__/newThought.ts +++ b/src/reducers/__tests__/newThought.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' import newThought from '../newThought' @@ -6,9 +6,9 @@ import newThought from '../newThought' it('new thought in root', () => { const stateNew = newThought(initialState(), { value: 'a' }) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) }) @@ -22,9 +22,9 @@ it('new thought after', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -39,9 +39,9 @@ it('new thought before', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - b - a`) @@ -56,9 +56,9 @@ it('new subthought', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) }) @@ -74,9 +74,9 @@ it('new subthought top', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - d - b diff --git a/src/reducers/__tests__/outdent.ts b/src/reducers/__tests__/outdent.ts index b4d8e26dd75..0e2bbffebac 100644 --- a/src/reducers/__tests__/outdent.ts +++ b/src/reducers/__tests__/outdent.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' @@ -18,9 +18,9 @@ it('outdent within root', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a1`) @@ -37,9 +37,9 @@ it('outdent with no cursor should do nothing ', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a1`) @@ -55,9 +55,9 @@ it('outdent root thought should do nothing ', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -74,9 +74,9 @@ it('outdent within context', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - a1 - a2`) diff --git a/src/reducers/__tests__/setAttribute.ts b/src/reducers/__tests__/setAttribute.ts index 172db225333..488bbb20ae9 100644 --- a/src/reducers/__tests__/setAttribute.ts +++ b/src/reducers/__tests__/setAttribute.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' @@ -21,9 +21,9 @@ it('set', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =test - hello`) @@ -48,9 +48,9 @@ it('different value should override existing value', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =test - goodbye`) @@ -77,9 +77,9 @@ it('add attribute if key has already been created', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =test - goodbye`) diff --git a/src/reducers/__tests__/setCursor.ts b/src/reducers/__tests__/setCursor.ts index 183838b3ee4..1ee134c026a 100644 --- a/src/reducers/__tests__/setCursor.ts +++ b/src/reducers/__tests__/setCursor.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' // TODO: Why does util have to be imported before selectors and reducers? import { initialState, reducerFlow } from '../../util' @@ -15,7 +15,7 @@ it('set the cursor to a SimplePath', () => { const cursor = [{ value: 'a', rank: 0 }, { value: 'b', rank: 0 }, { value: 'c', rank: 0 }] const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursor({ path: cursor }), toggleContextView, ] @@ -45,7 +45,7 @@ it('set the cursor to a Path across a context view', () => { ] const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursor({ path: cursor }), toggleContextView, ] diff --git a/src/reducers/__tests__/splitSentences.ts b/src/reducers/__tests__/splitSentences.ts index dc881c7ab14..e7c9e3bfd18 100644 --- a/src/reducers/__tests__/splitSentences.ts +++ b/src/reducers/__tests__/splitSentences.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' @@ -12,9 +12,9 @@ it('split single thought by sentences', () => { ] const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - One. - Two. - Three.`) @@ -30,9 +30,9 @@ it('split thought by sentences surrounded by siblings', () => { ] const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - One. - Two. diff --git a/src/reducers/__tests__/splitThought.ts b/src/reducers/__tests__/splitThought.ts index 2b34bdb5285..0f0ecc4b28b 100644 --- a/src/reducers/__tests__/splitThought.ts +++ b/src/reducers/__tests__/splitThought.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' @@ -15,9 +15,9 @@ it('split thought', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - ap - ple`) diff --git a/src/reducers/__tests__/subCategorizeAll.ts b/src/reducers/__tests__/subCategorizeAll.ts index 5da75d70e3a..c9dfd87bc49 100644 --- a/src/reducers/__tests__/subCategorizeAll.ts +++ b/src/reducers/__tests__/subCategorizeAll.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' @@ -21,9 +21,9 @@ it('subcategorize multiple thoughts', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - ${''/* prevent trim_trailing_whitespace */} - b @@ -42,9 +42,9 @@ it('subcategorize multiple thoughts in the root', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - ${''/* prevent trim_trailing_whitespace */} - a - b`) @@ -63,9 +63,9 @@ it('should do nothing with no cursor', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -103,9 +103,9 @@ it('move all visible and hidden thoughts into a new empty thought after subcateg // run steps through reducer flow const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - b -${' '} - =archive diff --git a/src/reducers/__tests__/subCategorizeOne.ts b/src/reducers/__tests__/subCategorizeOne.ts index e3c3bf3ff78..23f80debfba 100644 --- a/src/reducers/__tests__/subCategorizeOne.ts +++ b/src/reducers/__tests__/subCategorizeOne.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' @@ -18,9 +18,9 @@ it('subcategorize a thought', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - ${''/* prevent trim_trailing_whitespace */} - b`) @@ -36,9 +36,9 @@ it('subcategorize a thought in the root', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - ${''/* prevent trim_trailing_whitespace */} - a`) @@ -55,9 +55,9 @@ it('subcategorize with no cursor should do nothing', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b`) @@ -88,16 +88,16 @@ it('set cursor on new empty thought', () => { // - b` // const steps = [ -// importText({ path: RANKED_ROOT, text }), +// importText({ path: HOME_PATH, text }), // setCursorFirstMatch(['a']), // subCategorizeOne, // ] // // run steps through reducer flow and export as plaintext for readable test // const stateNew = reducerFlow(steps)(initialState()) -// const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') +// const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') -// expect(exported).toBe(`- ${ROOT_TOKEN} +// expect(exported).toBe(`- ${HOME_TOKEN} // - ${''/* prevent trim_trailing_whitespace */} // - a // - b @@ -114,16 +114,16 @@ it('set cursor on new empty thought', () => { // - note` // const steps = [ -// importText({ path: RANKED_ROOT, text }), +// importText({ path: HOME_PATH, text }), // setCursorFirstMatch(['a']), // subCategorizeOne, // ] // // run steps through reducer flow and export as plaintext for readable test // const stateNew = reducerFlow(steps)(initialState()) -// const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') +// const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') -// expect(exported).toBe(`- ${ROOT_TOKEN} +// expect(exported).toBe(`- ${HOME_TOKEN} // - ${''/* prevent trim_trailing_whitespace */} // - a // - =note diff --git a/src/reducers/__tests__/toggleAttribute.ts b/src/reducers/__tests__/toggleAttribute.ts index c7dd8bb15c8..a9e0c910377 100644 --- a/src/reducers/__tests__/toggleAttribute.ts +++ b/src/reducers/__tests__/toggleAttribute.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { exportContext } from '../../selectors' @@ -21,9 +21,9 @@ it('toggle on', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =test - hello`) @@ -48,9 +48,9 @@ it('toggle off', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a`) }) @@ -73,9 +73,9 @@ it('different value should override existing value', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =test - goodbye`) @@ -102,9 +102,9 @@ it('add attribute if key has already been created', () => { // run steps through reducer flow and export as plaintext for readable test const stateNew = reducerFlow(steps)(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - =test - goodbye`) diff --git a/src/reducers/archiveThought.ts b/src/reducers/archiveThought.ts index 77e7df0e7c5..b0999094a23 100644 --- a/src/reducers/archiveThought.ts +++ b/src/reducers/archiveThought.ts @@ -1,5 +1,5 @@ import _ from 'lodash' -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' import { State } from '../util/initialState' import { Child, Context, Path, SimplePath, ThoughtContext } from '../types' @@ -14,7 +14,6 @@ import { parentOf, pathToContext, reducerFlow, - rootedParentOf, unroot, } from '../util' @@ -27,6 +26,7 @@ import { isContextViewActive, lastThoughtsFromContextChain, nextSibling, + rootedParentOf, prevSibling, splitChain, thoughtsEditingFromChain, @@ -71,7 +71,7 @@ const archiveThought = (state: State, options: { path?: Path }): State => { : path as SimplePath const pathParent = showContexts && contextChain.length > 1 ? contextChain[contextChain.length - 1] : !showContexts && simplePath.length > 1 ? parentOf(simplePath) : - RANKED_ROOT + HOME_PATH const context = pathToContext(pathParent) const { value, rank } = head(simplePath) const thoughts = pathToContext(simplePath) @@ -125,7 +125,7 @@ const archiveThought = (state: State, options: { path?: Path }): State => { ? parentOf(path).concat({ value: head((next as ThoughtContext).context), rank: next.rank }) : parentOf(path).concat(next as Child)), 0] : // Case III: delete last thought in context; set cursor on context - thoughts.length > 1 ? [rootedParentOf(path), head(context).length] + thoughts.length > 1 ? [rootedParentOf(state, path), head(context).length] // Case IV: delete very last thought; remove cursor : [null, undefined] diff --git a/src/reducers/bumpThoughtDown.ts b/src/reducers/bumpThoughtDown.ts index 040d299cb25..c72d4b76682 100644 --- a/src/reducers/bumpThoughtDown.ts +++ b/src/reducers/bumpThoughtDown.ts @@ -1,7 +1,7 @@ import _ from 'lodash' import { existingThoughtChange, existingThoughtMove, newThoughtSubmit, setCursor, subCategorizeOne, editableRender } from '../reducers' -import { getPrevRank, getRankBefore, getAllChildren, simplifyPath } from '../selectors' -import { parentOf, headValue, pathToContext, reducerFlow, rootedParentOf, unroot } from '../util' +import { getPrevRank, getRankBefore, getAllChildren, simplifyPath, rootedParentOf } from '../selectors' +import { parentOf, headValue, pathToContext, reducerFlow, unroot } from '../util' import { State } from '../util/initialState' import { Path, SimplePath } from '../types' @@ -42,7 +42,7 @@ const bumpThoughtDown = (state: State, { simplePath }: { simplePath?: SimplePath existingThoughtChange({ oldValue: value, newValue: '', - context: rootedParentOf(context), + context: rootedParentOf(state, context), path: simplePathWithNewRank }), diff --git a/src/reducers/cursorBack.ts b/src/reducers/cursorBack.ts index ee98ad87a28..74b7cbf61ac 100644 --- a/src/reducers/cursorBack.ts +++ b/src/reducers/cursorBack.ts @@ -1,10 +1,14 @@ import { cursorHistory, search as searchReducer, setCursor } from '../reducers' -import { parentOf, reducerFlow } from '../util' +import { isAbsolute, parentOf, reducerFlow } from '../util' import { State } from '../util/initialState' +import toggleAbsoluteContext from './toggleAbsoluteContext' /** Moves the cursor up one level. */ const cursorBack = (state: State) => { - const { cursor: cursorOld, editing, search } = state + const { cursor: cursorOld, editing, search, rootContext } = state + + const isAbsoluteRoot = isAbsolute(rootContext) + const cursorNew = cursorOld && parentOf(cursorOld) return reducerFlow( @@ -19,9 +23,10 @@ const cursorBack = (state: State) => { cursorHistory({ cursor: cursorOld }), ] - // if there is no cursor and search is active, close the search - : search === '' ? [ - + // if there is no cursor and isAbsoluteRoot is active, toggle the context + // else of search is active, close the search + : + isAbsoluteRoot ? [toggleAbsoluteContext] : search === '' ? [ // close the search searchReducer({ value: null }), @@ -31,6 +36,7 @@ const cursorBack = (state: State) => { : null, ] : [] + )(state) } diff --git a/src/reducers/cursorDown.ts b/src/reducers/cursorDown.ts index b9fded9beaa..d0ebaa606bd 100644 --- a/src/reducers/cursorDown.ts +++ b/src/reducers/cursorDown.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { setCursor } from '../reducers' import { getChildrenSorted } from '../selectors' import { nextThought } from '../util' @@ -23,7 +23,7 @@ const cursorDown = (state: State) => { } // if no cursor, move cursor to first thought in root else { - const children = getChildrenSorted(state, [ROOT_TOKEN]) + const children = getChildrenSorted(state, [HOME_TOKEN]) return children.length > 0 ? setCursor(state, { path: [children[0]] }) : state diff --git a/src/reducers/cursorForward.ts b/src/reducers/cursorForward.ts index 06f15e9ceb1..f248e172eb8 100644 --- a/src/reducers/cursorForward.ts +++ b/src/reducers/cursorForward.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' import { setCursor } from '../reducers' import { firstVisibleChild } from '../selectors' import { pathToContext, unroot } from '../util' @@ -14,7 +14,7 @@ const cursorForward = (state: State) => { } // otherwise move cursor to first child else { - const cursor = state.cursor || RANKED_ROOT + const cursor = state.cursor || HOME_PATH const firstChild = firstVisibleChild(state, pathToContext(cursor)) if (!firstChild) return state diff --git a/src/reducers/cursorUp.ts b/src/reducers/cursorUp.ts index a6948915fb8..3299ae518a7 100644 --- a/src/reducers/cursorUp.ts +++ b/src/reducers/cursorUp.ts @@ -1,15 +1,15 @@ -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' import { setCursor } from '../reducers' -import { prevSibling } from '../selectors' -import { parentOf, head, isRoot, pathToContext, rootedParentOf, unroot } from '../util' +import { rootedParentOf, prevSibling } from '../selectors' +import { parentOf, head, pathToContext, unroot, isRoot } from '../util' import { State } from '../util/initialState' /** Moves the cursor to the previous sibling. */ const cursorUp = (state: State) => { const { cursor } = state - const path = cursor || RANKED_ROOT + const path = cursor || HOME_PATH const { value, rank } = head(path) - const contextRanked = rootedParentOf(path) + const contextRanked = rootedParentOf(state, path) const context = pathToContext(contextRanked) const thoughtBefore = prevSibling(state, value, context, rank) diff --git a/src/reducers/deleteEmptyThought.ts b/src/reducers/deleteEmptyThought.ts index 4cbedb15f3a..045390f5162 100644 --- a/src/reducers/deleteEmptyThought.ts +++ b/src/reducers/deleteEmptyThought.ts @@ -1,6 +1,6 @@ -import { ROOT_TOKEN } from '../constants' -import { head, headRank, headValue, isDivider, parentOf, pathToContext, reducerFlow, rootedParentOf } from '../util' -import { getNextRank, getChildren, getChildrenRanked, isContextViewActive, prevSibling, simplifyPath } from '../selectors' +import { HOME_TOKEN } from '../constants' +import { head, headRank, headValue, isDivider, parentOf, pathToContext, reducerFlow } from '../util' +import { getNextRank, getChildren, getChildrenRanked, isContextViewActive, prevSibling, simplifyPath, rootedParentOf } from '../selectors' import { archiveThought, deleteThought, existingThoughtChange, existingThoughtDelete, existingThoughtMove, setCursor } from '../reducers' import { State } from '../util/initialState' import { SimplePath } from '../types' @@ -34,8 +34,8 @@ const deleteEmptyThought = (state: State): State => { else if (offset === 0 && sel?.isCollapsed && !showContexts) { const value = headValue(cursor) const rank = headRank(cursor) - const parentContext = context.length > 1 ? parentOf(context) : [ROOT_TOKEN] - const prev = prevSibling(state, value, pathToContext(rootedParentOf(cursor)), rank) + const parentContext = context.length > 1 ? parentOf(context) : [HOME_TOKEN] + const prev = prevSibling(state, value, pathToContext(rootedParentOf(state, cursor)), rank) // only if there is a previous sibling if (prev) { diff --git a/src/reducers/deleteThought.ts b/src/reducers/deleteThought.ts index 13c7ff0c7ae..3d38012f90c 100644 --- a/src/reducers/deleteThought.ts +++ b/src/reducers/deleteThought.ts @@ -11,7 +11,6 @@ import { pathToContext, once, reducerFlow, - rootedParentOf, unroot, } from '../util' @@ -21,6 +20,7 @@ import { getContexts, getContextsSortedAndRanked, isContextViewActive, + rootedParentOf, prevSibling, rankThoughtsFirstMatch, simplifyPath, @@ -49,7 +49,7 @@ const deleteThought = (state: State, payload: { path?: Path }) => { } const simplePath = simplifyPath(state, path) const thoughts = pathToContext(simplePath) - const context = rootedParentOf(thoughts) + const context = rootedParentOf(state, thoughts) const { value, rank } = head(simplePath) /** Calculates the previous context within a context view. */ @@ -107,7 +107,7 @@ const deleteThought = (state: State, payload: { path?: Path }) => { : parentOf(path).concat(next() as Child) ), { offset: 0 }] : // Case III: delete last thought in context; set cursor on context - thoughts.length > 1 ? [rootedParentOf(path), { offset: head(context).length }] + thoughts.length > 1 ? [rootedParentOf(state, path), { offset: head(context).length }] // Case IV: delete very last thought; remove cursor : [null] )(state) diff --git a/src/reducers/existingThoughtChange.ts b/src/reducers/existingThoughtChange.ts index 5876f84f5e7..48a41cc58d8 100644 --- a/src/reducers/existingThoughtChange.ts +++ b/src/reducers/existingThoughtChange.ts @@ -1,6 +1,6 @@ import _ from 'lodash' import { treeChange } from '../util/recentlyEditedTree' -import { getThought, getAllChildren, getChildrenRanked, isPending } from '../selectors' +import { getThought, getAllChildren, getChildrenRanked, isPending, rootedParentOf } from '../selectors' import updateThoughts from './updateThoughts' import { State } from '../util/initialState' import { Context, Index, Lexeme, Parent, Path, SimplePath, Timestamp } from '../types' @@ -21,7 +21,6 @@ import { keyValueBy, pathToContext, removeContext, - rootedParentOf, timestamp, unroot, } from '../util' @@ -116,7 +115,7 @@ const existingThoughtChange = (state: State, { oldValue, newValue, context, show lastUpdated: timestamp(), } const thoughtNew = thoughtOld.contexts.length > 0 - ? addContext(newThoughtWithoutContext, context, showContexts ? headRank(rootedParentOf(pathLiveOld)) : rank, id, archived as Timestamp) + ? addContext(newThoughtWithoutContext, context, showContexts ? headRank(rootedParentOf(state, pathLiveOld)) : rank, id, archived as Timestamp) : newThoughtWithoutContext // update local thoughtIndex so that we do not have to wait for firebase @@ -174,7 +173,7 @@ const existingThoughtChange = (state: State, { oldValue, newValue, context, show const thoughtOldSubthoughts = getAllChildren(state, contextOld) .filter(child => !equalThoughtRanked(child, head(pathLiveOld))) - const contextParent = rootedParentOf(showContexts + const contextParent = rootedParentOf(state, showContexts ? context : pathToContext(pathLiveOld) ) @@ -182,14 +181,14 @@ const existingThoughtChange = (state: State, { oldValue, newValue, context, show const thoughtParentSubthoughts = showContexts ? getAllChildren(state, contextParent) .filter(child => - (newOldThought || !equalThoughtRanked(child, { value: oldValue, rank: headRank(rootedParentOf(pathLiveOld)) })) && - !equalThoughtRanked(child, { value: newValue, rank: headRank(rootedParentOf(pathLiveOld)) }) + (newOldThought || !equalThoughtRanked(child, { value: oldValue, rank: headRank(rootedParentOf(state, pathLiveOld)) })) && + !equalThoughtRanked(child, { value: newValue, rank: headRank(rootedParentOf(state, pathLiveOld)) }) ) // do not add floating thought to context .concat(thoughtOld.contexts.length > 0 ? { value: newValue, id, - rank: headRank(rootedParentOf(pathLiveOld)), + rank: headRank(rootedParentOf(state, pathLiveOld)), lastUpdated: timestamp(), ...archived ? { archived } : {} } : []) diff --git a/src/reducers/existingThoughtDelete.ts b/src/reducers/existingThoughtDelete.ts index b4d7c861fcc..b39b44a9a74 100644 --- a/src/reducers/existingThoughtDelete.ts +++ b/src/reducers/existingThoughtDelete.ts @@ -1,7 +1,7 @@ import _ from 'lodash' import { render, updateThoughts } from '../reducers' import { treeDelete } from '../util/recentlyEditedTree' -import { exists, getThought, getAllChildren, getChildrenRanked, isPending, rankThoughtsFirstMatch } from '../selectors' +import { exists, getThought, getAllChildren, getChildrenRanked, isPending, rankThoughtsFirstMatch, rootedParentOf } from '../selectors' import { State } from '../util/initialState' import { Child, Context, Index, Lexeme, Parent } from '../types' @@ -13,7 +13,6 @@ import { hashThought, reducerFlow, removeContext, - rootedParentOf, timestamp, unroot, } from '../util' @@ -38,7 +37,7 @@ const existingThoughtDelete = (state: State, { context, thoughtRanked, showConte if (!exists(state, value)) return state const thoughts = unroot(context.concat(value)) - context = rootedParentOf(thoughts) + context = rootedParentOf(state, thoughts) const key = hashThought(value) const thought = getThought(state, value) diff --git a/src/reducers/existingThoughtMove.ts b/src/reducers/existingThoughtMove.ts index 50e744b4443..24d908ef384 100644 --- a/src/reducers/existingThoughtMove.ts +++ b/src/reducers/existingThoughtMove.ts @@ -1,7 +1,7 @@ import _ from 'lodash' import { treeMove } from '../util/recentlyEditedTree' import { render, updateThoughts } from '../reducers' -import { getNextRank, getThought, getAllChildren, getChildrenRanked, isPending, simplifyPath } from '../selectors' +import { getNextRank, getThought, getAllChildren, getChildrenRanked, isPending, simplifyPath, rootedParentOf } from '../selectors' import { State } from '../util/initialState' import { Child, Context, Index, Lexeme, Parent, Path, SimplePath, Timestamp } from '../types' @@ -22,7 +22,6 @@ import { reducerFlow, removeContext, removeDuplicatedContext, - rootedParentOf, isDescendantPath, timestamp, } from '../util' @@ -57,8 +56,8 @@ const existingThoughtMove = (state: State, { oldPath, newPath, offset }: { const key = hashThought(value) const oldRank = headRank(oldSimplePath) const newRank = headRank(newSimplePath) - const oldContext = rootedParentOf(oldThoughts) - const newContext = rootedParentOf(newThoughts) + const oldContext = rootedParentOf(state, oldThoughts) + const newContext = rootedParentOf(state, newThoughts) const sameContext = equalArrays(oldContext, newContext) const oldThought = getThought(state, value) diff --git a/src/reducers/extractThought.ts b/src/reducers/extractThought.ts index b29ad2fafca..aff7298ac2e 100644 --- a/src/reducers/extractThought.ts +++ b/src/reducers/extractThought.ts @@ -1,9 +1,9 @@ import _ from 'lodash' import { State } from '../util/initialState' -import { headRank, headValue, pathToContext, reducerFlow, rootedParentOf } from '../util' +import { headRank, headValue, pathToContext, reducerFlow } from '../util' import existingThoughtChange from './existingThoughtChange' import newThought from './newThought' -import { simplifyPath } from '../selectors' +import { rootedParentOf, simplifyPath } from '../selectors' import alert from './alert' /** Extract the selection as child thought. */ @@ -27,7 +27,7 @@ const extractThought = (state: State) => { const childValue = value.slice(selectionStart, selectionEnd) const thoughts = pathToContext(cursor) - const cursorContext = rootedParentOf(thoughts) + const cursorContext = rootedParentOf(state, thoughts) const rank = headRank(cursor) const reducers = [ diff --git a/src/reducers/importText.ts b/src/reducers/importText.ts index c783f715d92..2a5ae44a91d 100644 --- a/src/reducers/importText.ts +++ b/src/reducers/importText.ts @@ -1,14 +1,14 @@ import { parse } from 'jex-block-parser' import _ from 'lodash' import { unescape } from 'html-escaper' -import { parentOf, convertHTMLtoJSON, head, importJSON, pathToContext, reducerFlow, roamJsonToBlocks, rootedParentOf, strip, validateRoam, createId, unroot } from '../util' +import { parentOf, convertHTMLtoJSON, head, importJSON, pathToContext, reducerFlow, roamJsonToBlocks, strip, validateRoam, createId, unroot } from '../util' import { existingThoughtChange, setCursor, updateThoughts } from '../reducers' -import { getAllChildren, rankThoughtsFirstMatch, simplifyPath } from '../selectors' +import { getAllChildren, rankThoughtsFirstMatch, simplifyPath, rootedParentOf } from '../selectors' import { Block, Path, SimplePath, Timestamp } from '../types' import { State } from '../util/initialState' import newThought from './newThought' import collapseContext from './collapseContext' -// import { ROOT_TOKEN } from '../constants' +// import { HOME_TOKEN } from '../constants' // a list item tag const regexpListItem = /)/gmi @@ -148,7 +148,7 @@ const importText = (state: State, { path, text, lastUpdated, preventSetCursor, r existingThoughtChange({ oldValue: destValue, newValue, - context: rootedParentOf(context), + context: rootedParentOf(state, pathToContext(path)), path: simplePath, }), diff --git a/src/reducers/indent.ts b/src/reducers/indent.ts index 6feba8a1017..da1ce647d62 100644 --- a/src/reducers/indent.ts +++ b/src/reducers/indent.ts @@ -1,5 +1,5 @@ import { alert, existingThoughtMove } from '../reducers' -import { getNextRank, hasChild, prevSibling } from '../selectors' +import { getNextRank, hasChild, rootedParentOf, prevSibling } from '../selectors' import { State } from '../util/initialState' // util @@ -12,7 +12,6 @@ import { isRoot, parentOf, pathToContext, - rootedParentOf, } from '../util' /** Increases the indentation level of the thought, i.e. Moves it to the end of its previous sibling. */ @@ -21,11 +20,11 @@ const indent = (state: State) => { if (!cursor) return state - const prev = prevSibling(state, headValue(cursor), pathToContext(rootedParentOf(cursor)), headRank(cursor)) + const prev = prevSibling(state, headValue(cursor), pathToContext(rootedParentOf(state, cursor)), headRank(cursor)) if (!prev) return state - // cancel if cursor is EM_TOKEN or ROOT_TOKEN + // cancel if cursor is EM_TOKEN or HOME_TOKEN if (isEM(cursor) || isRoot(cursor)) { return alert(state, { value: `The "${isEM(cursor) ? 'em' : 'home'} context" may not be indented.` }) } diff --git a/src/reducers/index.ts b/src/reducers/index.ts index e8f714fdc77..d5c890ec146 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -60,6 +60,7 @@ export { default as splitThought } from './splitThought' export { default as status } from './status' export { default as subCategorizeAll } from './subCategorizeAll' export { default as subCategorizeOne } from './subCategorizeOne' +export { default as toggleAbsoluteContext } from './toggleAbsoluteContext' export { default as toggleAttribute } from './toggleAttribute' export { default as toggleContextView } from './toggleContextView' export { default as toggleHiddenThoughts } from './toggleHiddenThoughts' diff --git a/src/reducers/moveThoughtDown.ts b/src/reducers/moveThoughtDown.ts index 6b61d9c7703..7db044e26c9 100644 --- a/src/reducers/moveThoughtDown.ts +++ b/src/reducers/moveThoughtDown.ts @@ -1,8 +1,8 @@ import { alert, existingThoughtMove } from '../reducers' import { State } from '../util/initialState' import { SimplePath } from '../types' -import { parentOf, ellipsize, head, headRank, headValue, pathToContext, rootedParentOf } from '../util' -import { getPrevRank, getRankAfter, getSortPreference, getThoughtAfter, hasChild, nextSibling, simplifyPath } from '../selectors' +import { parentOf, ellipsize, head, headRank, headValue, pathToContext } from '../util' +import { getPrevRank, getRankAfter, getSortPreference, getThoughtAfter, hasChild, nextSibling, rootedParentOf, simplifyPath } from '../selectors' /** Swaps the thought with its next siblings. */ const moveThoughtDown = (state: State) => { @@ -17,7 +17,7 @@ const moveThoughtDown = (state: State) => { const value = headValue(cursor) const rank = headRank(cursor) - const nextThought = nextSibling(state, value, rootedParentOf(pathToContext(cursor)), rank) + const nextThought = nextSibling(state, value, rootedParentOf(state, pathToContext(cursor)), rank) // if the cursor is the last child or the context is sorted, move the thought to the beginning of its next uncle const nextUncleThought = pathParent.length > 0 ? getThoughtAfter(state, simplifyPath(state, pathParent)) : null diff --git a/src/reducers/moveThoughtUp.ts b/src/reducers/moveThoughtUp.ts index 9b50e09503f..ba7e85f7dd1 100644 --- a/src/reducers/moveThoughtUp.ts +++ b/src/reducers/moveThoughtUp.ts @@ -1,8 +1,8 @@ import { alert, existingThoughtMove } from '../reducers' import { State } from '../util/initialState' import { SimplePath } from '../types' -import { parentOf, ellipsize, head, headRank, headValue, pathToContext, rootedParentOf } from '../util' -import { getNextRank, getRankBefore, getSortPreference, getThoughtBefore, hasChild, prevSibling, simplifyPath } from '../selectors' +import { parentOf, ellipsize, head, headRank, headValue, pathToContext } from '../util' +import { getNextRank, getRankBefore, getSortPreference, getThoughtBefore, hasChild, rootedParentOf, prevSibling, simplifyPath } from '../selectors' /** Swaps the thought with its previous siblings. */ const moveThoughtUp = (state: State) => { @@ -17,7 +17,7 @@ const moveThoughtUp = (state: State) => { const value = headValue(cursor) const rank = headRank(cursor) - const prevThought = prevSibling(state, value, rootedParentOf(pathToContext(cursor)), rank) + const prevThought = prevSibling(state, value, rootedParentOf(state, pathToContext(cursor)), rank) // if the cursor is the first thought or the context is sorted, move the thought to the end of its prev uncle const prevUncleThought = pathParent.length > 0 ? getThoughtBefore(state, simplifyPath(state, pathParent)) : null diff --git a/src/reducers/newThought.ts b/src/reducers/newThought.ts index ee9f521bf16..ba02c7d8176 100644 --- a/src/reducers/newThought.ts +++ b/src/reducers/newThought.ts @@ -4,7 +4,6 @@ import { Path } from '../types' // constants import { - RANKED_ROOT, TUTORIAL2_STEP_CONTEXT1, TUTORIAL2_STEP_CONTEXT1_HINT, TUTORIAL2_STEP_CONTEXT1_PARENT, @@ -29,7 +28,6 @@ import { parentOf, pathToContext, reducerFlow, - rootedParentOf, unroot, } from '../util' @@ -39,9 +37,11 @@ import { getPrevRank, getRankAfter, getRankBefore, + getRootPath, getSetting, hasChild, isContextViewActive, + rootedParentOf, simplifyPath, } from '../selectors' @@ -91,10 +91,12 @@ const newThought = (state: State, payload: NewThoughtPayload | string) => { (tutorialStep === TUTORIAL_STEP_SECONDTHOUGHT_ENTER || tutorialStep === TUTORIAL_STEP_FIRSTTHOUGHT_ENTER)) - const path = at || state.cursor || RANKED_ROOT + const path = at || state.cursor || getRootPath(state) + const simplePath = simplifyPath(state, path) + const thoughts = pathToContext(simplePath) - const context = pathToContext(rootedParentOf(simplePath)) + const context = pathToContext(rootedParentOf(state, simplePath)) // prevent adding Subthought to readonly or unextendable Thought const sourceContext = insertNewSubthought ? thoughts : context diff --git a/src/reducers/outdent.ts b/src/reducers/outdent.ts index d7d67686ac8..123e8bddfa6 100644 --- a/src/reducers/outdent.ts +++ b/src/reducers/outdent.ts @@ -1,15 +1,15 @@ import { alert, existingThoughtMove } from '../reducers' -import { getRankAfter, hasChild, simplifyPath } from '../selectors' +import { getRankAfter, hasChild, rootedParentOf, simplifyPath } from '../selectors' import { State } from '../util/initialState' import { Path } from '../types' -import { ellipsize, head, headValue, isEM, isRoot, parentOf, pathToContext, rootedParentOf, unroot } from '../util' +import { ellipsize, head, headValue, isEM, isRoot, parentOf, pathToContext, unroot } from '../util' /** Decreases the indent level of the given thought, moving it to its parent. */ const outdent = (state: State) => { const { cursor } = state if (!cursor || cursor.length <= 1) return state - // Cancel if a direct child of EM_TOKEN or ROOT_TOKEN + // Cancel if a direct child of EM_TOKEN or HOME_TOKEN if (isEM(parentOf(cursor)) || isRoot(parentOf(cursor))) { return alert(state, { value: `Subthought of the "${isEM(parentOf(cursor)) ? 'em' : 'home'} context" may not be de-indented.` @@ -31,7 +31,7 @@ const outdent = (state: State) => { const offset = window.getSelection()?.focusOffset const cursorNew: Path = [ - ...unroot(rootedParentOf(parentOf(cursor))), + ...unroot(rootedParentOf(state, parentOf(cursor))), { ...head(cursor), rank: getRankAfter(state, parentOf(simplifyPath(state, cursor))) diff --git a/src/reducers/setCursor.ts b/src/reducers/setCursor.ts index da7739ff332..f52e6648890 100644 --- a/src/reducers/setCursor.ts +++ b/src/reducers/setCursor.ts @@ -1,5 +1,5 @@ import _ from 'lodash' -import { RANKED_ROOT, ROOT_TOKEN, TUTORIAL2_STEP_CONTEXT_VIEW_SELECT, TUTORIAL_CONTEXT, TUTORIAL_STEP_AUTOEXPAND, TUTORIAL_STEP_AUTOEXPAND_EXPAND } from '../constants' +import { HOME_PATH, HOME_TOKEN, TUTORIAL2_STEP_CONTEXT_VIEW_SELECT, TUTORIAL_CONTEXT, TUTORIAL_STEP_AUTOEXPAND, TUTORIAL_STEP_AUTOEXPAND_EXPAND } from '../constants' import { chain, expandThoughts, getSetting, getAllChildren, simplifyPath } from '../selectors' import { equalPath, equalThoughtRanked, hashContext, headValue, isDescendant, pathToContext } from '../util' import { render, settings } from '../reducers' @@ -29,13 +29,13 @@ const setCursor = (state: State, { path: Path | null, }): State => { - if (path && path.length > 1 && equalThoughtRanked(path[0], RANKED_ROOT[0])) { + if (path && path.length > 1 && equalThoughtRanked(path[0], HOME_PATH[0])) { // log error instead of throwing since it can cause the pullQueue to enter an infinite loop - console.error(new Error('setCursor: Invalid Path; Non-root Paths should omit ' + ROOT_TOKEN)) + console.error(new Error('setCursor: Invalid Path; Non-root Paths should omit ' + HOME_TOKEN)) return state } - const simplePath = path ? simplifyPath(state, path) : RANKED_ROOT + const simplePath = path ? simplifyPath(state, path) : HOME_PATH const context = pathToContext(simplePath) const thoughtsResolved = path && contextChain.length > 0 ? chain(state, contextChain, simplePath!) diff --git a/src/reducers/splitSentences.ts b/src/reducers/splitSentences.ts index 9854e6c4667..e973a4546ec 100644 --- a/src/reducers/splitSentences.ts +++ b/src/reducers/splitSentences.ts @@ -1,15 +1,15 @@ import _ from 'lodash' import { State } from '../util/initialState' -import { parentOf, head, headRank, headValue, pathToContext, reducerFlow, rootedParentOf } from '../util' +import { parentOf, head, headRank, headValue, pathToContext, reducerFlow } from '../util' import { editableRender, editingValue, existingThoughtChange, newThought, setCursor } from '../reducers' -import { simplifyPath } from '../selectors' +import { rootedParentOf, simplifyPath } from '../selectors' /** Split thought by sentences. Create new thought for each sentence. Thought value, on which cursor is on, replace with first sentence. */ const splitSentences = (state: State) => { const { cursor } = state if (!cursor) return state const thoughts = pathToContext(cursor) - const cursorContext = rootedParentOf(thoughts) + const cursorContext = rootedParentOf(state, thoughts) const rank = headRank(cursor) const value = headValue(cursor) diff --git a/src/reducers/splitThought.ts b/src/reducers/splitThought.ts index 9b40af3c923..a401bacf16d 100644 --- a/src/reducers/splitThought.ts +++ b/src/reducers/splitThought.ts @@ -1,6 +1,6 @@ import _ from 'lodash' import xhtmlPurifier from 'xhtml-purifier' -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { parentOf, headRank, headValue, pathToContext, reducerFlow, strip } from '../util' import { getThoughtAfter, getChildrenRanked, simplifyPath } from '../selectors' import { editableRender, existingThoughtChange, existingThoughtMove, newThought, render } from '../reducers' @@ -20,7 +20,7 @@ const splitThought = (state: State, { path, offset }: { path?: Path, offset?: nu const simplePath = simplifyPath(state, path) const thoughts = pathToContext(simplePath) - const context = thoughts.length > 1 ? parentOf(thoughts) : [ROOT_TOKEN] + const context = thoughts.length > 1 ? parentOf(thoughts) : [HOME_TOKEN] // split the value into left and right parts const value = headValue(path) diff --git a/src/reducers/subCategorizeAll.ts b/src/reducers/subCategorizeAll.ts index 15df1c0ee2a..a34a248cac2 100644 --- a/src/reducers/subCategorizeAll.ts +++ b/src/reducers/subCategorizeAll.ts @@ -1,7 +1,7 @@ -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' import { alert, existingThoughtMove, newThought } from '../reducers' import { State } from '../util/initialState' -import { parentOf, ellipsize, headValue, isEM, isRoot, pathToContext, once, reducerFlow } from '../util' +import { parentOf, ellipsize, headValue, isEM, pathToContext, once, reducerFlow, isRoot } from '../util' import { getChildrenRanked, hasChild, lastThoughtsFromContextChain, simplifyPath, splitChain } from '../selectors' /** Inserts a new thought as a parent of all thoughts in the given context. */ @@ -14,7 +14,7 @@ const subCategorizeAll = (state: State) => { const cursorParent = parentOf(cursor) const context = pathToContext(cursorParent) - // cancel if a direct child of EM_TOKEN or ROOT_TOKEN + // cancel if a direct child of EM_TOKEN or HOME_TOKEN if (isEM(cursorParent) || isRoot(cursorParent)) { return alert(state, { value: `Subthought of the "${isEM(cursorParent) ? 'em' : 'home'} context" may not be de-indented.` @@ -37,10 +37,10 @@ const subCategorizeAll = (state: State) => { ? parentOf(contextChain.length > 1 ? lastThoughtsFromContextChain(state, contextChain) : cursor) - : RANKED_ROOT + : HOME_PATH const children = getChildrenRanked(state, pathToContext(simplifyPath(state, path))) - const pathParent = cursor.length > 1 ? cursorParent : RANKED_ROOT + const pathParent = cursor.length > 1 ? cursorParent : HOME_PATH // get newly created thought // use fresh state diff --git a/src/reducers/subCategorizeOne.ts b/src/reducers/subCategorizeOne.ts index a79acec18ab..72c44adad5d 100644 --- a/src/reducers/subCategorizeOne.ts +++ b/src/reducers/subCategorizeOne.ts @@ -10,9 +10,9 @@ import { head, headValue, isEM, - isRoot, pathToContext, reducerFlow, + isRoot, } from '../util' /** Inserts a new thought and adds the given thought as a subthought. */ @@ -25,7 +25,7 @@ const subCategorizeOne = (state: State) => { const cursorParent = parentOf(cursor) const context = pathToContext(cursorParent) - // cancel if a direct child of EM_TOKEN or ROOT_TOKEN + // cancel if a direct child of EM_TOKEN or HOME_TOKEN if (isEM(cursorParent) || isRoot(cursorParent)) { return alert(state, { value: `Subthoughts of the "${isEM(cursorParent) ? 'em' : 'home'}" contex may not be de-indented.` diff --git a/src/reducers/toggleAbsoluteContext.ts b/src/reducers/toggleAbsoluteContext.ts new file mode 100644 index 00000000000..76db4909e9b --- /dev/null +++ b/src/reducers/toggleAbsoluteContext.ts @@ -0,0 +1,14 @@ +import { State } from '../util/initialState' +import { ABSOLUTE_TOKEN, HOME_TOKEN, TRANSIENT_ABSOLUTE_CHILD_PATH } from '../constants' +import { isHome, timestamp } from '../util' + +/** Toggles starting context. */ +const toggleAbsoluteContext = (state: State): State => ({ + ...state, + rootContext: isHome(state.rootContext) ? [ABSOLUTE_TOKEN] : [HOME_TOKEN], + cursorBeforeQuickAdd: state.cursor, + absoluteContextTime: timestamp(), + cursor: isHome(state.rootContext) ? TRANSIENT_ABSOLUTE_CHILD_PATH : state.cursorBeforeQuickAdd, +}) + +export default toggleAbsoluteContext diff --git a/src/reducers/undoArchive.ts b/src/reducers/undoArchive.ts index 937e731464c..8ead49ef262 100644 --- a/src/reducers/undoArchive.ts +++ b/src/reducers/undoArchive.ts @@ -1,6 +1,6 @@ import _ from 'lodash' -import { pathToContext, reducerFlow, rootedParentOf } from '../util' -import { getAllChildren, getChildrenRanked } from '../selectors' +import { pathToContext, reducerFlow } from '../util' +import { getAllChildren, getChildrenRanked, rootedParentOf } from '../selectors' import { alert, existingThoughtDelete, existingThoughtMove, setCursor } from '../reducers' import { State } from '../util/initialState' import { Path } from '../types' @@ -8,8 +8,8 @@ import { Path } from '../types' /** Moves the archived thought back to its original location. */ const undoArchive = (state: State, { originalPath, currPath, offset }: { originalPath: Path, currPath: Path, offset?: number }) => { - const context = rootedParentOf(pathToContext(currPath)) - const archiveContext = rootedParentOf(pathToContext(originalPath)) + const context = rootedParentOf(state, pathToContext(currPath)) + const archiveContext = rootedParentOf(state, pathToContext(originalPath)) return reducerFlow([ diff --git a/src/reducers/updateThoughts.ts b/src/reducers/updateThoughts.ts index 7767384209c..8a476282fa4 100644 --- a/src/reducers/updateThoughts.ts +++ b/src/reducers/updateThoughts.ts @@ -2,8 +2,8 @@ import _ from 'lodash' import { State, PushBatch } from '../util/initialState' import { decodeThoughtsUrl, expandThoughts } from '../selectors' import { ExistingThoughtChangePayload } from '../reducers/existingThoughtChange' -import { hashContext, isRoot, logWithTime, mergeUpdates, reducerFlow, getWhitelistedThoughts } from '../util' -import { CONTEXT_CACHE_SIZE, EM_TOKEN, ROOT_TOKEN, THOUGHT_CACHE_SIZE } from '../constants' +import { hashContext, logWithTime, mergeUpdates, reducerFlow, getWhitelistedThoughts, isRoot } from '../util' +import { CONTEXT_CACHE_SIZE, EM_TOKEN, HOME_TOKEN, THOUGHT_CACHE_SIZE } from '../constants' import { Child, Context, ContextHash, Index, Lexeme, Parent, Path, SimplePath, ThoughtHash, ThoughtsInterface } from '../types' export interface UpdateThoughtsOptions { @@ -20,7 +20,7 @@ export interface UpdateThoughtsOptions { isLoading?: boolean, } -const rootEncoded = hashContext([ROOT_TOKEN]) +const rootEncoded = hashContext([HOME_TOKEN]) /** * Updates thoughtIndex and contextIndex with any number of thoughts. diff --git a/src/redux-middleware/__tests__/pullQueue.ts b/src/redux-middleware/__tests__/pullQueue.ts index 845e6636b6e..40914e425b9 100644 --- a/src/redux-middleware/__tests__/pullQueue.ts +++ b/src/redux-middleware/__tests__/pullQueue.ts @@ -1,5 +1,5 @@ import { store } from '../../store' -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { HOME_PATH, HOME_TOKEN } from '../../constants' import { clear, existingThoughtChange, existingThoughtDelete, existingThoughtMove, importText, newThought, setCursor } from '../../action-creators' import { initialize } from '../../initialize' import { getAllChildren, getParent, rankThoughtsFirstMatch } from '../../selectors' @@ -40,7 +40,7 @@ it('disable isLoading after initialize', async () => { it('load thought', async () => { - const parentEntryRoot1 = await getContext(db, [ROOT_TOKEN]) + const parentEntryRoot1 = await getContext(db, [HOME_TOKEN]) expect(parentEntryRoot1).toBeUndefined() fakeTimer.useFakeTimer() @@ -51,7 +51,7 @@ it('load thought', async () => { await fakeTimer.runAllAsync() fakeTimer.useRealTimer() - const parentEntryRoot = await getContext(db, [ROOT_TOKEN]) + const parentEntryRoot = await getContext(db, [HOME_TOKEN]) expect(parentEntryRoot).toMatchObject({ children: [{ value: 'a', rank: 0 }] }) @@ -61,14 +61,14 @@ it('load thought', async () => { store.dispatch(clear()) await fakeTimer.runAllAsync() - const children = getAllChildren(store.getState(), [ROOT_TOKEN]) + const children = getAllChildren(store.getState(), [HOME_TOKEN]) expect(children).toHaveLength(0) // Note: Always use real timer before awaiting db calls. https://github.com/cybersemics/em/issues/919#issuecomment-739135971 fakeTimer.useRealTimer() // confirm thought is still in local db after state has been cleared - const parentEntryRootAfterReload = await getContext(db, [ROOT_TOKEN]) + const parentEntryRootAfterReload = await getContext(db, [HOME_TOKEN]) expect(parentEntryRootAfterReload).toMatchObject({ children: [{ value: 'a' }] }) @@ -79,7 +79,7 @@ it('load thought', async () => { initialize() await fakeTimer.runAllAsync() - const childrenAfterInitialize = getAllChildren(store.getState(), [ROOT_TOKEN]) + const childrenAfterInitialize = getAllChildren(store.getState(), [HOME_TOKEN]) expect(childrenAfterInitialize).toMatchObject([ { value: 'a' } ]) @@ -93,7 +93,7 @@ it('do not repopulate deleted thought', async () => { { type: 'newThought', value: '' }, { type: 'existingThoughtDelete', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], thoughtRanked: { value: '', rank: 0 }, }, // Need to setCursor to trigger the pullQueue @@ -104,7 +104,7 @@ it('do not repopulate deleted thought', async () => { await fakeTimer.runAllAsync() - const parentEntryRoot = getParent(store.getState(), [ROOT_TOKEN]) + const parentEntryRoot = getParent(store.getState(), [HOME_TOKEN]) expect(parentEntryRoot).toBe(undefined) const parentEntryChild = getParent(store.getState(), ['']) @@ -116,7 +116,7 @@ it('load buffered thoughts', async () => { fakeTimer.useFakeTimer() store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -129,7 +129,7 @@ it('load buffered thoughts', async () => { fakeTimer.useRealTimer() - expect(await getContext(db, [ROOT_TOKEN])).toMatchObject({ children: [{ value: 'a' }] }) + expect(await getContext(db, [HOME_TOKEN])).toMatchObject({ children: [{ value: 'a' }] }) expect(await getContext(db, ['a'])).toMatchObject({ children: [{ value: 'b' }] }) expect(await getContext(db, ['a', 'b'])).toMatchObject({ children: [{ value: 'c' }] }) expect(await getContext(db, ['a', 'b', 'c'])).toMatchObject({ children: [{ value: 'd' }] }) @@ -149,7 +149,7 @@ it('load buffered thoughts', async () => { fakeTimer.useRealTimer() const state = store.getState() - expect(getAllChildren(state, [ROOT_TOKEN])).toMatchObject([{ value: 'a' }]) + expect(getAllChildren(state, [HOME_TOKEN])).toMatchObject([{ value: 'a' }]) expect(getAllChildren(state, ['a'])).toMatchObject([{ value: 'b' }]) expect(getAllChildren(state, ['a', 'b'])).toMatchObject([{ value: 'c' }]) expect(getAllChildren(state, ['a', 'b', 'c'])).toMatchObject([{ value: 'd' }]) @@ -163,7 +163,7 @@ it('delete thought with buffered descendants', async () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - x - a @@ -179,7 +179,7 @@ it('delete thought with buffered descendants', async () => { fakeTimer.runAllAsync() - expect(await getContext(db, [ROOT_TOKEN])).toMatchObject({ children: [{ value: 'x' }, { value: 'a' }] }) + expect(await getContext(db, [HOME_TOKEN])).toMatchObject({ children: [{ value: 'x' }, { value: 'a' }] }) expect(await getContext(db, ['a'])).toMatchObject({ children: [{ value: 'b' }] }) expect(await getContext(db, ['a', 'b'])).toMatchObject({ children: [{ value: 'c' }] }) expect(await getContext(db, ['a', 'b', 'c'])).toMatchObject({ children: [{ value: 'd' }] }) @@ -195,16 +195,16 @@ it('delete thought with buffered descendants', async () => { // delete thought with buffered descendants store.dispatch(existingThoughtDelete({ - context: [ROOT_TOKEN], + context: [HOME_TOKEN], thoughtRanked: { value: 'a', rank: 1 } })) await fakeTimer.runAllAsync() fakeTimer.useRealTimer() - expect(getAllChildren(store.getState(), [ROOT_TOKEN])).toMatchObject([{ value: 'x' }]) + expect(getAllChildren(store.getState(), [HOME_TOKEN])).toMatchObject([{ value: 'x' }]) - expect(await getContext(db, [ROOT_TOKEN])).toMatchObject({ children: [{ value: 'x' }] }) + expect(await getContext(db, [HOME_TOKEN])).toMatchObject({ children: [{ value: 'x' }] }) expect(await getContext(db, ['a'])).toBeFalsy() expect(await getContext(db, ['a', 'b'])).toBeFalsy() expect(await getContext(db, ['a', 'b', 'c'])).toBeFalsy() @@ -218,7 +218,7 @@ it('move thought with buffered descendants', async () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - x - a @@ -235,7 +235,7 @@ it('move thought with buffered descendants', async () => { fakeTimer.useRealTimer() - expect(await getContext(db, [ROOT_TOKEN])).toMatchObject({ children: [{ value: 'x' }, { value: 'a' }] }) + expect(await getContext(db, [HOME_TOKEN])).toMatchObject({ children: [{ value: 'x' }, { value: 'a' }] }) expect(await getContext(db, ['a'])).toMatchObject({ children: [{ value: 'm' }, { value: 'b' }] }) expect(await getContext(db, ['a', 'b'])).toMatchObject({ children: [{ value: 'c' }] }) expect(await getContext(db, ['a', 'm'])).toBeUndefined() @@ -264,9 +264,9 @@ it('move thought with buffered descendants', async () => { fakeTimer.useRealTimer() - expect(getAllChildren(store.getState(), [ROOT_TOKEN])).toMatchObject([{ value: 'x' }]) + expect(getAllChildren(store.getState(), [HOME_TOKEN])).toMatchObject([{ value: 'x' }]) - expect(await getContext(db, [ROOT_TOKEN])).toMatchObject({ children: [{ value: 'x' }] }) + expect(await getContext(db, [HOME_TOKEN])).toMatchObject({ children: [{ value: 'x' }] }) expect(await getContext(db, ['a'])).toBeFalsy() expect(await getContext(db, ['a', 'b'])).toBeFalsy() expect(await getContext(db, ['a', 'b', 'c'])).toBeFalsy() @@ -288,7 +288,7 @@ it('edit thought with buffered descendants', async () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - x - a @@ -305,7 +305,7 @@ it('edit thought with buffered descendants', async () => { fakeTimer.useRealTimer() - expect(await getContext(db, [ROOT_TOKEN])).toMatchObject({ children: [{ value: 'x' }, { value: 'a' }] }) + expect(await getContext(db, [HOME_TOKEN])).toMatchObject({ children: [{ value: 'x' }, { value: 'a' }] }) expect(await getContext(db, ['a'])).toMatchObject({ children: [{ value: 'm' }, { value: 'b' }] }) expect(await getContext(db, ['a', 'b'])).toMatchObject({ children: [{ value: 'c' }] }) expect(await getContext(db, ['a', 'm'])).toBeUndefined() @@ -326,7 +326,7 @@ it('edit thought with buffered descendants', async () => { store.dispatch(existingThoughtChange({ oldValue: 'a', newValue: 'a!', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'a', rank: 1 }] as SimplePath, })) @@ -334,9 +334,9 @@ it('edit thought with buffered descendants', async () => { fakeTimer.useRealTimer() - expect(getAllChildren(store.getState(), [ROOT_TOKEN])).toMatchObject([{ value: 'x' }, { value: 'a!' }]) + expect(getAllChildren(store.getState(), [HOME_TOKEN])).toMatchObject([{ value: 'x' }, { value: 'a!' }]) - expect(await getContext(db, [ROOT_TOKEN])).toMatchObject({ children: [{ value: 'x' }, { value: 'a!' }] }) + expect(await getContext(db, [HOME_TOKEN])).toMatchObject({ children: [{ value: 'x' }, { value: 'a!' }] }) expect(await getContext(db, ['a'])).toBeFalsy() expect(await getContext(db, ['a', 'b'])).toBeFalsy() expect(await getContext(db, ['a', 'b', 'c'])).toBeFalsy() @@ -357,7 +357,7 @@ it.only('export thought with buffered descendants', async () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - x - a @@ -373,7 +373,7 @@ it.only('export thought with buffered descendants', async () => { fakeTimer.useRealTimer() - expect(await getContext(db, [ROOT_TOKEN])).toMatchObject({ children: [{ value: 'x' }, { value: 'a' }] }) + expect(await getContext(db, [HOME_TOKEN])).toMatchObject({ children: [{ value: 'x' }, { value: 'a' }] }) expect(await getContext(db, ['a'])).toMatchObject({ children: [{ value: 'b' }] }) expect(await getContext(db, ['a', 'b'])).toMatchObject({ children: [{ value: 'c' }] }) expect(await getContext(db, ['a', 'b', 'c'])).toMatchObject({ children: [{ value: 'd' }] }) @@ -392,7 +392,7 @@ it.only('export thought with buffered descendants', async () => { // delete thought with buffered descendants store.dispatch(existingThoughtDelete({ - context: [ROOT_TOKEN], + context: [HOME_TOKEN], thoughtRanked: { value: 'a', rank: 1 } })) @@ -403,9 +403,9 @@ it.only('export thought with buffered descendants', async () => { fakeTimer.useRealTimer() - expect(getAllChildren(store.getState(), [ROOT_TOKEN])).toMatchObject([{ value: 'x' }]) + expect(getAllChildren(store.getState(), [HOME_TOKEN])).toMatchObject([{ value: 'x' }]) - expect(await getContext(db, [ROOT_TOKEN])).toMatchObject({ children: [{ value: 'x' }] }) + expect(await getContext(db, [HOME_TOKEN])).toMatchObject({ children: [{ value: 'x' }] }) expect(await getContext(db, ['a'])).toBeFalsy() expect(await getContext(db, ['a', 'b'])).toBeFalsy() expect(await getContext(db, ['a', 'b', 'c'])).toBeFalsy() diff --git a/src/redux-middleware/pullQueue.ts b/src/redux-middleware/pullQueue.ts index 16ac9cad16b..efaaf616553 100644 --- a/src/redux-middleware/pullQueue.ts +++ b/src/redux-middleware/pullQueue.ts @@ -1,6 +1,6 @@ import _ from 'lodash' import { ThunkMiddleware } from 'redux-thunk' -import { EM_TOKEN, ROOT_TOKEN } from '../constants' +import { EM_TOKEN, HOME_TOKEN } from '../constants' import { decodeContextUrl, getAllChildrenByContextHash, hasPushes } from '../selectors' import { equalArrays, hashContext, keyValueBy, pathToContext, unroot } from '../util' import { pull } from '../action-creators' @@ -16,7 +16,7 @@ const flushPullQueueDelay = 500 /** Creates the initial pullQueue with only the em and root contexts. */ const initialPullQueue = (): Index => ({ [hashContext([EM_TOKEN])]: [EM_TOKEN], - [hashContext([ROOT_TOKEN])]: [ROOT_TOKEN], + [hashContext([HOME_TOKEN])]: [HOME_TOKEN], }) /** Generates a map of all visible contexts, including the cursor, all its ancestors, and the expanded contexts. */ diff --git a/src/redux-middleware/updateUrlHistory.ts b/src/redux-middleware/updateUrlHistory.ts index add963f8af0..48404973350 100644 --- a/src/redux-middleware/updateUrlHistory.ts +++ b/src/redux-middleware/updateUrlHistory.ts @@ -1,6 +1,6 @@ import _ from 'lodash' import { ThunkMiddleware } from 'redux-thunk' -import { RANKED_ROOT, ROOT_TOKEN } from '../constants' +import { HOME_PATH, HOME_TOKEN } from '../constants' import { equalArrays, hashContext, pathToContext } from '../util' import { decodeThoughtsUrl, hashContextUrl } from '../selectors' import { deleteCursor, updateCursor } from '../data-providers/dexie' @@ -20,14 +20,14 @@ interface Options { * * @param contextViews Optional argument can be used during toggleContextViews when the state has not yet been updated. Defaults to URL contextViews. */ -const updateUrlHistory = (state: State, path = RANKED_ROOT, { replace, contextViews }: Options = {}) => { +const updateUrlHistory = (state: State, path = HOME_PATH, { replace, contextViews }: Options = {}) => { const decoded = decodeThoughtsUrl(state, window.location.pathname) - const context = path ? pathToContext(path) : [ROOT_TOKEN] + const context = path ? pathToContext(path) : [HOME_TOKEN] const encoded = hashContext(context) // convert decoded root thought to null cursor - const contextDecoded = decoded.path ? pathToContext(decoded.path) : [ROOT_TOKEN] + const contextDecoded = decoded.path ? pathToContext(decoded.path) : [HOME_TOKEN] // if we are already on the page we are trying to navigate to (both in thoughts and contextViews), then NOOP if (equalArrays(contextDecoded, context) && decoded.contextViews[encoded] === (contextViews || state.contextViews)[encoded]) return @@ -55,7 +55,7 @@ const updateUrlHistory = (state: State, path = RANKED_ROOT, { replace, contextVi // an incrementing ID to track back or forward browser actions (window.history.state || 0) + 1, '', - hashContextUrl(stateWithNewContextViews, path ? context : [ROOT_TOKEN]) + hashContextUrl(stateWithNewContextViews, path ? context : [HOME_TOKEN]) ) } catch (e) { diff --git a/src/selectors/__tests__/expandThoughts.ts b/src/selectors/__tests__/expandThoughts.ts index ea0fb4e6048..e9909079dfd 100644 --- a/src/selectors/__tests__/expandThoughts.ts +++ b/src/selectors/__tests__/expandThoughts.ts @@ -1,5 +1,5 @@ import _ from 'lodash' -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { HOME_PATH, HOME_TOKEN } from '../../constants' import { hashContext, initialState, reducerFlow } from '../../util' import { expandThoughts, rankThoughtsFirstMatch } from '../../selectors' import { importText, newSubthought, newThought, setCursor } from '../../reducers' @@ -19,7 +19,7 @@ const isContextExpanded = (state: State, context: Context) => describe('normal view', () => { it('ROOT is always expanded', () => { - expect(isContextExpanded(initialState(), [ROOT_TOKEN])).toBeTruthy() + expect(isContextExpanded(initialState(), [HOME_TOKEN])).toBeTruthy() }) it('non-existent thoughts are not expanded', () => { @@ -63,7 +63,7 @@ describe('normal view', () => { - d` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a']) ] @@ -81,7 +81,7 @@ describe('normal view', () => { - d` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a', 'b']) ] @@ -102,7 +102,7 @@ describe('normal view', () => { - f` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a']) ] @@ -132,7 +132,7 @@ describe('table view', () => { - e` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a']) ] @@ -153,7 +153,7 @@ describe('table view', () => { - d - e` - const stateNew = importText(initialState(), { path: RANKED_ROOT, text }) + const stateNew = importText(initialState(), { path: HOME_PATH, text }) // cursor on row 1, column 2 const stateNew1 = setCursorFirstMatch(stateNew, ['a', 'b']) @@ -175,7 +175,7 @@ describe('table view', () => { - d - e` - const stateNew = importText(initialState(), { path: RANKED_ROOT, text }) + const stateNew = importText(initialState(), { path: HOME_PATH, text }) // cursor on row 1, column 2 const stateNew1 = setCursorFirstMatch(stateNew, ['a', 'b', 'c']) @@ -199,7 +199,7 @@ describe('table view', () => { - e` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a']) ] @@ -220,7 +220,7 @@ describe('table view', () => { - d - e` - const stateNew = importText(initialState(), { path: RANKED_ROOT, text }) + const stateNew = importText(initialState(), { path: HOME_PATH, text }) // cursor on row 1, column 2 const stateNew1 = setCursorFirstMatch(stateNew, ['a', 'b']) @@ -243,7 +243,7 @@ describe('table view', () => { - d - e` - const stateNew = importText(initialState(), { path: RANKED_ROOT, text }) + const stateNew = importText(initialState(), { path: HOME_PATH, text }) // cursor on row 1, column 2 (same row) const stateNew1 = setCursorFirstMatch(stateNew, ['a', 'b', 'c']) @@ -270,7 +270,7 @@ describe('=pin', () => { - e` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a']) ] @@ -294,7 +294,7 @@ describe('=pin', () => { - e` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a', 'd']) ] @@ -315,7 +315,7 @@ describe('=pin', () => { ` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a']) ] @@ -341,7 +341,7 @@ describe('=pinChildren', () => { - e` const steps = [ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), setCursorFirstMatch(['a']) ] @@ -362,7 +362,7 @@ describe('=pinChildren', () => { - d - e` - const stateNew = importText(initialState(), { path: RANKED_ROOT, text }) + const stateNew = importText(initialState(), { path: HOME_PATH, text }) const stateNew1 = setCursorFirstMatch(stateNew, ['a', 'b']) expect(isContextExpanded(stateNew1, ['a', 'd'])).toBeTruthy() @@ -382,7 +382,7 @@ describe('=pinChildren', () => { - d - e` - const stateNew = importText(initialState(), { path: RANKED_ROOT, text }) + const stateNew = importText(initialState(), { path: HOME_PATH, text }) const stateNew1 = setCursorFirstMatch(stateNew, ['a', 'b', 'c']) expect(isContextExpanded(stateNew1, ['a', 'd'])).toBeTruthy() diff --git a/src/selectors/__tests__/getAllChildren.ts b/src/selectors/__tests__/getAllChildren.ts index 64e7e0869e6..e00d1698aea 100644 --- a/src/selectors/__tests__/getAllChildren.ts +++ b/src/selectors/__tests__/getAllChildren.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../../constants' +import { HOME_TOKEN } from '../../constants' import { initialState, reducerFlow } from '../../util' import { getAllChildren } from '../../selectors' import { newSubthought, newThought } from '../../reducers' @@ -12,7 +12,7 @@ it('get root children', () => { const stateNew = reducerFlow(steps)(initialState()) - expect(getAllChildren(stateNew, [ROOT_TOKEN])) + expect(getAllChildren(stateNew, [HOME_TOKEN])) .toMatchObject([{ value: 'a', rank: 0 }, { value: 'b', rank: 1 }]) }) diff --git a/src/selectors/__tests__/getPrevRank.ts b/src/selectors/__tests__/getPrevRank.ts index 9aede0c8301..de70c04e1a7 100644 --- a/src/selectors/__tests__/getPrevRank.ts +++ b/src/selectors/__tests__/getPrevRank.ts @@ -1,5 +1,5 @@ import { initialState, isFunction, reducerFlow } from '../../util' -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' import { getAllChildrenSorted } from '../../selectors' import { importText, newSubthought, newThought } from '../../reducers' import getPrevRank from '../getPrevRank' @@ -28,7 +28,7 @@ it('get rank less than visible children but greater than hidden children', () => - b - c ` - const stateNew = importText({ path: RANKED_ROOT, text })(initialState()) + const stateNew = importText({ path: HOME_PATH, text })(initialState()) const children = getAllChildrenSorted(stateNew, ['a']) const firstVisibleIndex = children.findIndex(child => !isFunction(child.value)) const firstVisible = children[firstVisibleIndex] diff --git a/src/selectors/decodeContextUrl.ts b/src/selectors/decodeContextUrl.ts index 9ba3e46b67f..fd61fe19620 100644 --- a/src/selectors/decodeContextUrl.ts +++ b/src/selectors/decodeContextUrl.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { componentToThought, owner } from '../util' import { State } from '../util/initialState' @@ -12,7 +12,7 @@ const decodeContextUrl = (state: State, pathname: string) => { console.error(`decodeThoughtsUrl: owner does not match owner(). "${urlOwner}" !== "${owner()}". This is likely a regression, as they should always match.`) } - const urlPath = urlComponents.length > 1 ? urlComponents.slice(1) : [ROOT_TOKEN] + const urlPath = urlComponents.length > 1 ? urlComponents.slice(1) : [HOME_TOKEN] const pathUnranked = urlPath.map(componentToThought) return pathUnranked diff --git a/src/selectors/decodeThoughtsUrl.ts b/src/selectors/decodeThoughtsUrl.ts index dfc12718bb4..ca723791df4 100644 --- a/src/selectors/decodeThoughtsUrl.ts +++ b/src/selectors/decodeThoughtsUrl.ts @@ -1,4 +1,4 @@ -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { componentToThought, hashContext, keyValueBy, owner } from '../util' import { pathExists, rankThoughtsFirstMatch } from '../selectors' import { State } from '../util/initialState' @@ -18,7 +18,7 @@ const decodeThoughtsUrl = (state: State, pathname: string, { exists }: Options = console.error(`decodeThoughtsUrl: owner does not match owner(). "${urlOwner}" !== "${owner()}". This is likely a regression, as they should always match.`) } - const urlPath = urlComponents.length > 1 ? urlComponents.slice(1) : [ROOT_TOKEN] + const urlPath = urlComponents.length > 1 ? urlComponents.slice(1) : [HOME_TOKEN] const pathUnranked = urlPath.map(componentToThought) const contextViews = keyValueBy(urlPath, (cur, i) => /~$/.test(cur) ? { diff --git a/src/selectors/expandThoughts.ts b/src/selectors/expandThoughts.ts index 56e88fb2df8..c6af5e8552f 100644 --- a/src/selectors/expandThoughts.ts +++ b/src/selectors/expandThoughts.ts @@ -1,9 +1,9 @@ import globals from '../globals' -import { EXPAND_THOUGHT_CHAR, MAX_EXPAND_DEPTH, RANKED_ROOT, ROOT_TOKEN } from '../constants' -import { attribute, attributeEquals, getChildPath, getContexts, getAllChildren, isContextViewActive, simplifyPath } from '../selectors' +import { EXPAND_THOUGHT_CHAR, MAX_EXPAND_DEPTH, HOME_PATH, HOME_TOKEN } from '../constants' +import { attribute, attributeEquals, getChildPath, getContexts, getAllChildren, isContextViewActive, simplifyPath, rootedParentOf } from '../selectors' import { Child, Context, Index, Path, ThoughtContext } from '../types' import { State } from '../util/initialState' -import { equalThoughtRanked, hashContext, head, headValue, isFunction, isURL, keyValueBy, parentOf, pathToContext, publishMode, rootedParentOf, unroot } from '../util' +import { equalThoughtRanked, hashContext, head, headValue, isFunction, isURL, keyValueBy, parentOf, pathToContext, publishMode, unroot } from '../util' /** Get the value of the Child | ThoughtContext. */ const childValue = (child: Child | ThoughtContext, showContexts: boolean) => showContexts @@ -61,14 +61,14 @@ const expandThoughts = (state: State, path: Path | null, { depth = 0 }: { depth? console.error(new Error('expandThoughts: Invalid empty Path received.')) return {} } - else if (path && path.length > 1 && equalThoughtRanked(path[0], RANKED_ROOT[0])) { + else if (path && path.length > 1 && equalThoughtRanked(path[0], HOME_PATH[0])) { // log error instead of throwing since it can cause the pullQueue to enter an infinite loop - console.error(new Error('expandThoughts: Invalid Path; Non-root Paths should omit ' + ROOT_TOKEN)) + console.error(new Error('expandThoughts: Invalid Path; Non-root Paths should omit ' + HOME_TOKEN)) return {} } const simplePath = !path || path.length === 0 - ? RANKED_ROOT + ? HOME_PATH : simplifyPath(state, path) /** Returns true if the child should be pinned open. */ @@ -76,7 +76,7 @@ const expandThoughts = (state: State, path: Path | null, { depth = 0 }: { depth? attribute(state, pathToContext(getChildPath(state, child, simplePath)), '=pin') const context = pathToContext(simplePath) - const rootedPath = path || RANKED_ROOT + const rootedPath = path || HOME_PATH const showContexts = isContextViewActive(state, context) const childrenUnfiltered = showContexts @@ -111,7 +111,7 @@ const expandThoughts = (state: State, path: Path | null, { depth = 0 }: { depth? // this allows expansion of column 1 when the cursor is on column 2 in the table view, and uncles of the cursor that end in ":" // RECURSION ...path && path.length >= 1 && depth <= 1 - ? expandThoughts(state, rootedParentOf(path), { depth: depth + 1 }) + ? expandThoughts(state, rootedParentOf(state, path), { depth: depth + 1 }) : {} } diff --git a/src/selectors/getChildren.ts b/src/selectors/getChildren.ts index 01f68af1c09..84c77c7bd65 100644 --- a/src/selectors/getChildren.ts +++ b/src/selectors/getChildren.ts @@ -1,9 +1,8 @@ import _ from 'lodash' import { State } from '../util/initialState' import { appendChildPath, getChildPath, getSortPreference, hasChild, isContextViewActive } from '../selectors' -import { compareByRank, compareThought, hashContext, isFunction, sort, unroot, pathToContext, equalThoughtRanked, head } from '../util' +import { compareByRank, compareThought, hashContext, isAbsolute, isFunction, sort, pathToContext, equalThoughtRanked, head, unroot } from '../util' import { Child, ComparatorFunction, Context, ContextHash, ThoughtContext, SimplePath, Parent, Path } from '../types' - /** A selector that retrieves thoughts from a context and performs other functions like sorting or filtering. */ type GetThoughts = (state: State, context: Context) => Child[] @@ -90,8 +89,25 @@ const pathHeadValue = (state: State, path: Path, child: Child | ThoughtContext) } /** Checks if the child is visible and also checks if the child lies within the cursor. */ -export const isChildVisibleWithCursorCheck = _.curry((state: State, path: Path, simplePath: SimplePath, child: Child | ThoughtContext) => { +const isChildVisibleWithCursorCheck = _.curry((state: State, path: Path, simplePath: SimplePath, child: Child | ThoughtContext) => { return state.showHiddenThoughts || isChildVisible(state, pathToContext(simplePath), { value: pathHeadValue(state, path, child), rank: child.rank }) || isChildInCursor(state, path, simplePath, child) }) + +/** Checks if the child is created after latest absolute context toggle. */ +const isCreatedAfterAbsoluteToggle = _.curry((state: State, child: Child | ThoughtContext) => + child.lastUpdated && state.absoluteContextTime +&& child.lastUpdated > state.absoluteContextTime) + +/** + * Children filter predicate used for rendering. + * + * 1. Checks if the child is visible. + * 2. Checks if child is within cursor. + * 3. Checks if child is created after latest absolute context toggle if starting context is absolute. + */ +export const childrenFilterPredicate = _.curry((state: State, path: Path, simplePath: SimplePath, child: Child | ThoughtContext) => { + return isChildVisibleWithCursorCheck(state, path, simplePath, child) && + (!isAbsolute(state.rootContext) || isCreatedAfterAbsoluteToggle(state, child)) +}) diff --git a/src/selectors/getRankAfter.ts b/src/selectors/getRankAfter.ts index 2f6a1729088..d2958f40285 100644 --- a/src/selectors/getRankAfter.ts +++ b/src/selectors/getRankAfter.ts @@ -1,15 +1,16 @@ -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { getChildrenRanked } from '../selectors' -import { equalThoughtValue, headRank, headValue, pathToContext, rootedParentOf } from '../util' +import { equalThoughtValue, headRank, headValue, pathToContext } from '../util' import { SimplePath } from '../types' import { State } from '../util/initialState' +import rootedParentOf from './rootedParentOf' /** Gets a new rank after the given thought in a list but before the following thought. */ const getRankAfter = (state: State, simplePath: SimplePath) => { const value = headValue(simplePath) const rank = headRank(simplePath) - const parentPath = rootedParentOf(simplePath) + const parentPath = rootedParentOf(state, simplePath) const children = getChildrenRanked(state, pathToContext(parentPath)) // if there are no children, start with rank 0 @@ -18,7 +19,7 @@ const getRankAfter = (state: State, simplePath: SimplePath) => { } // if there is no value, it means nothing is selected // get rank after the last child - else if (value === undefined || value === ROOT_TOKEN) { + else if (value === undefined || value === HOME_TOKEN) { // guard against NaN/undefined return (children[children.length - 1].rank || 0) + 1 } diff --git a/src/selectors/getRankBefore.ts b/src/selectors/getRankBefore.ts index 862efa9b93d..6601b022314 100644 --- a/src/selectors/getRankBefore.ts +++ b/src/selectors/getRankBefore.ts @@ -1,14 +1,15 @@ import { getChildrenRanked } from '../selectors' -import { headRank, headValue, pathToContext, rootedParentOf } from '../util' +import { headRank, headValue, pathToContext } from '../util' import { State } from '../util/initialState' import { SimplePath } from '../types' +import rootedParentOf from './rootedParentOf' /** Gets a new rank before the given thought in a list but after the previous thought. */ const getRankBefore = (state: State, simplePath: SimplePath) => { const value = headValue(simplePath) const rank = headRank(simplePath) - const parentPath = rootedParentOf(simplePath) + const parentPath = rootedParentOf(state, simplePath) const children = getChildrenRanked(state, pathToContext(parentPath)) // if there are no children, start with rank 0 diff --git a/src/selectors/getRootPath.ts b/src/selectors/getRootPath.ts new file mode 100644 index 00000000000..dbd2323e319 --- /dev/null +++ b/src/selectors/getRootPath.ts @@ -0,0 +1,18 @@ +import { ABSOLUTE_PATH, ABSOLUTE_TOKEN, HOME_PATH, HOME_TOKEN } from '../constants' +import { SimplePath } from '../types' +import { State } from '../util/initialState' + +const RootPathMap: Record = { + [HOME_TOKEN]: HOME_PATH, + [ABSOLUTE_TOKEN]: ABSOLUTE_PATH +} + +/** + * Get ranked or absolute ranked root based on rootContext. + */ +const getRootPath = (state: State) => { + const startingToken = state.rootContext[0] + return RootPathMap[startingToken] +} + +export default getRootPath diff --git a/src/selectors/getThoughtAfter.ts b/src/selectors/getThoughtAfter.ts index 1cc2c4231ce..23bca224ae0 100644 --- a/src/selectors/getThoughtAfter.ts +++ b/src/selectors/getThoughtAfter.ts @@ -1,14 +1,15 @@ -import { equalThoughtValue, headRank, headValue, pathToContext, rootedParentOf } from '../util' +import { equalThoughtValue, headRank, headValue, pathToContext } from '../util' import { getChildrenSorted } from '../selectors' import { State } from '../util/initialState' import { SimplePath } from '../types' +import rootedParentOf from './rootedParentOf' /** Gets a new rank after the given thought in a list but before the following thought. */ const getThoughtAfter = (state: State, simplePath: SimplePath) => { const value = headValue(simplePath) const rank = headRank(simplePath) - const parentPath = rootedParentOf(simplePath) + const parentPath = rootedParentOf(state, simplePath) const children = getChildrenSorted(state, pathToContext(parentPath)) if (children.length === 0) { diff --git a/src/selectors/getThoughtBefore.ts b/src/selectors/getThoughtBefore.ts index 74349813489..374bc069a48 100644 --- a/src/selectors/getThoughtBefore.ts +++ b/src/selectors/getThoughtBefore.ts @@ -1,14 +1,15 @@ -import { headRank, headValue, pathToContext, rootedParentOf } from '../util' +import { headRank, headValue, pathToContext } from '../util' import { getChildrenSorted } from '../selectors' import { State } from '../util/initialState' import { SimplePath } from '../types' +import rootedParentOf from './rootedParentOf' /** Gets a new rank before the given thought in a list but after the previous thought. */ const getThoughtBefore = (state: State, simplePath: SimplePath) => { const value = headValue(simplePath) const rank = headRank(simplePath) - const parentPath = rootedParentOf(simplePath) + const parentPath = rootedParentOf(state, simplePath) const children = getChildrenSorted(state, pathToContext(parentPath)) // if there are no children, start with rank 0 diff --git a/src/selectors/index.ts b/src/selectors/index.ts index 4418071d16b..ae7c0a333b4 100644 --- a/src/selectors/index.ts +++ b/src/selectors/index.ts @@ -20,6 +20,7 @@ export { default as getNgrams } from './getNgrams' export { default as getPrevRank } from './getPrevRank' export { default as getRankAfter } from './getRankAfter' export { default as getRankBefore } from './getRankBefore' +export { default as getRootPath } from './getRootPath' export { default as getSetting } from './getSetting' export { default as getSortPreference } from './getSortPreference' export { default as getStyle } from './getStyle' @@ -37,6 +38,7 @@ export { default as isPending } from './isPending' export { default as isTutorial } from './isTutorial' export { default as lastThoughtsFromContextChain } from './lastThoughtsFromContextChain' export { default as nextSibling } from './nextSibling' +export { default as rootedParentOf } from './rootedParentOf' export { default as pathExists } from './pathExists' export { default as prevSibling } from './prevSibling' export { default as rankThoughtsFirstMatch } from './rankThoughtsFirstMatch' @@ -45,4 +47,4 @@ export { default as splitChain } from './splitChain' export { default as subtree } from './subtree' export { default as theme } from './theme' export { default as thoughtsEditingFromChain } from './thoughtsEditingFromChain' -export { firstVisibleChild, getAllChildren, getAllChildrenByContextHash, getChildren, getChildrenRanked, getAllChildrenSorted, getChildrenSorted, getParent, hasChildren, isChildVisibleWithCursorCheck, isChildVisible } from './getChildren' +export { firstVisibleChild, getAllChildren, getAllChildrenByContextHash, getChildren, getChildrenRanked, getAllChildrenSorted, getChildrenSorted, getParent, hasChildren, isChildVisible, childrenFilterPredicate } from './getChildren' diff --git a/src/selectors/pathExists.ts b/src/selectors/pathExists.ts index d368f750e98..761da6b06ad 100644 --- a/src/selectors/pathExists.ts +++ b/src/selectors/pathExists.ts @@ -1,11 +1,11 @@ import { State } from '../util/initialState' -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { hasChild } from '../selectors' import { isRoot } from '../util' /** Returns true if every child in the path exists. */ const pathExists = (state: State, pathUnranked: string[]) => isRoot(pathUnranked) || - pathUnranked.every((value, i) => hasChild(state, i === 0 ? [ROOT_TOKEN] : pathUnranked.slice(0, i), value)) + pathUnranked.every((value, i) => hasChild(state, i === 0 ? [HOME_TOKEN] : pathUnranked.slice(0, i), value)) export default pathExists diff --git a/src/selectors/rankThoughtsFirstMatch.ts b/src/selectors/rankThoughtsFirstMatch.ts index c16bd4300ec..6c5a2beba33 100644 --- a/src/selectors/rankThoughtsFirstMatch.ts +++ b/src/selectors/rankThoughtsFirstMatch.ts @@ -1,21 +1,22 @@ -import { RANKED_ROOT, ROOT_TOKEN } from '../constants' +import { HOME_PATH, HOME_TOKEN } from '../constants' import { contextChainToPath, equalArrays, equalThoughtRanked, head, headValue, isRoot, pathToContext, unroot } from '../util' import { getContexts, getContextsSortedAndRanked, getThought, getChildrenRanked, isContextViewActive, splitChain } from '../selectors' import { State } from '../util/initialState' import { Child, Context, Path } from '../types' +import getRootPath from './getRootPath' /** Ranks the thoughts from their rank in their context. */ // if there is a duplicate thought in the same context, takes the first // NOTE: path is pathToContexted const rankThoughtsFirstMatch = (state: State, pathUnranked: string[]): Path => { - if (isRoot(pathUnranked)) return RANKED_ROOT + if (isRoot(pathUnranked)) return getRootPath(state) - let pathResult: Path = RANKED_ROOT // eslint-disable-line fp/no-let - let prevParentContext = [ROOT_TOKEN] // eslint-disable-line fp/no-let + let pathResult: Path = HOME_PATH // eslint-disable-line fp/no-let + let prevParentContext = [HOME_TOKEN] // eslint-disable-line fp/no-let return pathUnranked.map((value, i) => { const thought = getThought(state, value) - const contextPathUnranked = i === 0 ? [ROOT_TOKEN] : pathUnranked.slice(0, i) + const contextPathUnranked = i === 0 ? [HOME_TOKEN] : pathUnranked.slice(0, i) const contextChain = splitChain(state, pathResult) const path = contextChainToPath(contextChain) const context = unroot(prevParentContext).concat(headValue(path)) as Context diff --git a/src/selectors/rootedParentOf.ts b/src/selectors/rootedParentOf.ts new file mode 100644 index 00000000000..8c936f41e5c --- /dev/null +++ b/src/selectors/rootedParentOf.ts @@ -0,0 +1,27 @@ +import { ABSOLUTE_PATH, ABSOLUTE_TOKEN, HOME_PATH, HOME_TOKEN } from '../constants' +import { parentOf } from '../util' +import { Context, Path } from '../types' +import { State } from '../util/initialState' + +const RootPathMap: Record = { + [HOME_TOKEN]: HOME_PATH, + [ABSOLUTE_TOKEN]: ABSOLUTE_PATH +} + +/** Checks if an object is of type Path. */ +const isPath = (o: Context | Path): o is Path => + o.length > 0 && Object.prototype.hasOwnProperty.call(o[0], 'value') + +/** Get the parentOf thoughts or the root ranked path if there are none. */ +const rootedParentOf = (state: State, thoughts: T): T => { + + const startingRankedRoot = RootPathMap[state.rootContext[0]] + + return thoughts && thoughts.length > 1 + ? parentOf(thoughts) as T + : isPath(thoughts) + ? startingRankedRoot as T + : state.rootContext as T +} + +export default rootedParentOf diff --git a/src/selectors/thoughtsEditingFromChain.ts b/src/selectors/thoughtsEditingFromChain.ts index 28fddca83fa..96e9d01be66 100644 --- a/src/selectors/thoughtsEditingFromChain.ts +++ b/src/selectors/thoughtsEditingFromChain.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' import { splitChain } from '../selectors' import { head } from '../util' import { Path } from '../types' @@ -15,7 +15,7 @@ const thoughtsEditingFromChain = (state: State, path: Path) => { // the penultimate context in the context chain, which is the thoughts that is being edited in the context view const thoughtsEditing = contextChain && contextChain.length > 1 ? contextChain[contextChain.length - 2] - : RANKED_ROOT + : HOME_PATH return contextFromChain.concat(head(thoughtsEditing)) } diff --git a/src/shortcuts/__tests__/deleteEmptyThoughtOrOutdent.ts b/src/shortcuts/__tests__/deleteEmptyThoughtOrOutdent.ts index 830aa2b3951..d8d77950910 100644 --- a/src/shortcuts/__tests__/deleteEmptyThoughtOrOutdent.ts +++ b/src/shortcuts/__tests__/deleteEmptyThoughtOrOutdent.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { HOME_PATH, HOME_TOKEN } from '../../constants' import { exportContext, rankThoughtsFirstMatch } from '../../selectors' import { importText, setCursor } from '../../action-creators' @@ -17,9 +17,9 @@ it('do nothing when there is no cursor', () => { executeShortcut(deleteEmptyThoughtOrOutdent, { store }) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - a` expect(exported).toEqual(expectedOutput) @@ -31,7 +31,7 @@ it('outdent on pressing backspace at the beginning of the thought', () => { // import thoughts store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -42,9 +42,9 @@ it('outdent on pressing backspace at the beginning of the thought', () => { executeShortcut(deleteEmptyThoughtOrOutdent, { store }) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - a - b - c` @@ -58,7 +58,7 @@ it('do not outdent thought with siblings', () => { // import thoughts store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -70,9 +70,9 @@ it('do not outdent thought with siblings', () => { executeShortcut(deleteEmptyThoughtOrOutdent, { store }) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - a - b - cd` diff --git a/src/shortcuts/__tests__/indentOnSpace.ts b/src/shortcuts/__tests__/indentOnSpace.ts index 6e95a5b44fe..04ed8a00c08 100644 --- a/src/shortcuts/__tests__/indentOnSpace.ts +++ b/src/shortcuts/__tests__/indentOnSpace.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { HOME_PATH, HOME_TOKEN } from '../../constants' import { exportContext, rankThoughtsFirstMatch } from '../../selectors' import { importText, setCursor } from '../../action-creators' import { createTestStore } from '../../test-helpers/createTestStore' @@ -10,7 +10,7 @@ it('indent on adding space at the beginning of the thought', () => { const store = createTestStore() store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -22,9 +22,9 @@ it('indent on adding space at the beginning of the thought', () => { executeShortcut(indentOnSpace, { store }) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - a - b - c @@ -38,7 +38,7 @@ it('prevent indent on adding space at the beginning of the immovable thought', ( const store = createTestStore() store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -51,10 +51,10 @@ it('prevent indent on adding space at the beginning of the immovable thought', ( executeShortcut(indentOnSpace, { store }) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') // indent shouldn't happen and output should remain the same - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - a - b - c diff --git a/src/shortcuts/__tests__/newThoughtOrOutdent.ts b/src/shortcuts/__tests__/newThoughtOrOutdent.ts index f50932ed8dc..49d966365c6 100644 --- a/src/shortcuts/__tests__/newThoughtOrOutdent.ts +++ b/src/shortcuts/__tests__/newThoughtOrOutdent.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { HOME_PATH, HOME_TOKEN } from '../../constants' import { exportContext } from '../../selectors' import { importText } from '../../action-creators' import { createTestStore } from '../../test-helpers/createTestStore' @@ -14,7 +14,7 @@ it('empty thought should outdent when hit enter', () => { // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -38,9 +38,9 @@ it('empty thought should outdent when hit enter', () => { // this should cause outdent instead of creating new thought executeShortcut(newThoughtOrOutdent, { store }) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - a - b - c diff --git a/src/shortcuts/__tests__/pinSubthoughts.ts b/src/shortcuts/__tests__/pinSubthoughts.ts index e26ea3a247f..2c98fa3e058 100644 --- a/src/shortcuts/__tests__/pinSubthoughts.ts +++ b/src/shortcuts/__tests__/pinSubthoughts.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' import { createTestStore } from '../../test-helpers/createTestStore' import { attribute } from '../../selectors' import { importText } from '../../action-creators' @@ -13,7 +13,7 @@ it('toggle on =pinChildren attribute of cursor (initial state without =pinChildr // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -39,7 +39,7 @@ it('toggle on =pinChildren attribute of cursor (initial state =pinChildren set t // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - =pinChildren @@ -67,7 +67,7 @@ it('toggle off =pinChildren attribute of cursor', () => { // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - =pinChildren diff --git a/src/shortcuts/__tests__/proseView.ts b/src/shortcuts/__tests__/proseView.ts index 6fa13d544e0..91b52db8494 100644 --- a/src/shortcuts/__tests__/proseView.ts +++ b/src/shortcuts/__tests__/proseView.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' import { createTestStore } from '../../test-helpers/createTestStore' import { attribute } from '../../selectors' import { importText } from '../../action-creators' @@ -15,7 +15,7 @@ it('toggle on prose view of parent of cursor (inital state without =view attribu // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -37,7 +37,7 @@ it('toggle on prose view of parent of cursor (inital state with =view attribute // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - =view @@ -61,7 +61,7 @@ it('toggle off prose view of parent of cursor', () => { // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - =view diff --git a/src/shortcuts/__tests__/redo.ts b/src/shortcuts/__tests__/redo.ts index 056d2ac42fa..692cbe0f2aa 100644 --- a/src/shortcuts/__tests__/redo.ts +++ b/src/shortcuts/__tests__/redo.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { HOME_PATH, HOME_TOKEN } from '../../constants' import { exportContext } from '../../selectors' import { importText } from '../../action-creators' import { createTestStore } from '../../test-helpers/createTestStore' @@ -10,7 +10,7 @@ it('redo thought change', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b` @@ -20,15 +20,15 @@ it('redo thought change', () => { type: 'existingThoughtChange', newValue: 'aa', oldValue: 'a', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'a', rank: 0 }] }, { type: 'undoAction' } ]) - const exportedBeforeRedo = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exportedBeforeRedo = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutputAfterUndo = `- ${ROOT_TOKEN} + const expectedOutputAfterUndo = `- ${HOME_TOKEN} - a - b` @@ -37,9 +37,9 @@ it('redo thought change', () => { // redo thought change store.dispatch({ type: 'redoAction' }) - const exportedAfterRedo = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exportedAfterRedo = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutputAfterRedo = `- ${ROOT_TOKEN} + const expectedOutputAfterRedo = `- ${HOME_TOKEN} - aa - b` @@ -53,7 +53,7 @@ it('group contiguous navigation actions preceding a thought change on redo', () store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -67,7 +67,7 @@ it('group contiguous navigation actions preceding a thought change on redo', () type: 'existingThoughtChange', newValue: 'arizona', oldValue: 'a', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'a', rank: 0 }] }, setCursorFirstMatchActionCreator(['arizona', 'b']), @@ -96,8 +96,8 @@ it('group contiguous navigation actions preceding a thought change on redo', () const cursorAfterSecondRedo = store.getState().cursor expect(cursorAfterSecondRedo).toMatchObject([{ value: 'arizona' }, { value: 'boston' }]) - const exportedAfterRedo = exportContext(state, [ROOT_TOKEN], 'text/plain') - const expectedOutputAfterRedo = `- ${ROOT_TOKEN} + const exportedAfterRedo = exportContext(state, [HOME_TOKEN], 'text/plain') + const expectedOutputAfterRedo = `- ${HOME_TOKEN} - arizona - boston - c` @@ -110,7 +110,7 @@ it('redo contiguous changes', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - A - B` @@ -119,29 +119,29 @@ it('redo contiguous changes', () => { type: 'existingThoughtChange', newValue: 'Atlantic', oldValue: 'A', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'A', rank: 0 }] }, { type: 'existingThoughtChange', newValue: 'Atlantic ', oldValue: 'Atlantic', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'Atlantic', rank: 0 }] }, { type: 'existingThoughtChange', newValue: 'Atlantic City', oldValue: 'Atlantic ', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'Atlantic ', rank: 0 }] }, { type: 'undoAction' } ]) - const exportedBeforeRedo = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exportedBeforeRedo = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutputBeforeRedo = `- ${ROOT_TOKEN} + const expectedOutputBeforeRedo = `- ${HOME_TOKEN} - A - B` @@ -151,9 +151,9 @@ it('redo contiguous changes', () => { type: 'redoAction', }) - const exportedAfterRedo = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exportedAfterRedo = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutputAfterRedo = `- ${ROOT_TOKEN} + const expectedOutputAfterRedo = `- ${HOME_TOKEN} - Atlantic City - B` diff --git a/src/shortcuts/__tests__/toggleSort.ts b/src/shortcuts/__tests__/toggleSort.ts index aa408173a72..16dbab02033 100644 --- a/src/shortcuts/__tests__/toggleSort.ts +++ b/src/shortcuts/__tests__/toggleSort.ts @@ -1,4 +1,4 @@ -import { EM_TOKEN, RANKED_ROOT } from '../../constants' +import { EM_TOKEN, HOME_PATH } from '../../constants' import { createTestStore } from '../../test-helpers/createTestStore' import { attribute, rankThoughtsFirstMatch } from '../../selectors' import { existingThoughtChange, importText } from '../../action-creators' @@ -14,7 +14,7 @@ it('toggle on sort preference of cursor (initial state without =sort attribute)' // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - d @@ -37,7 +37,7 @@ it('toggle off sort preference of cursor (initial state with =sort/Alphabetical) // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - =sort @@ -62,7 +62,7 @@ it('override global Alphabetical with local None', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - d diff --git a/src/shortcuts/__tests__/toggleTableView.ts b/src/shortcuts/__tests__/toggleTableView.ts index 393f8518e63..2765a2fa7fa 100644 --- a/src/shortcuts/__tests__/toggleTableView.ts +++ b/src/shortcuts/__tests__/toggleTableView.ts @@ -1,4 +1,4 @@ -import { RANKED_ROOT } from '../../constants' +import { HOME_PATH } from '../../constants' import { createTestStore } from '../../test-helpers/createTestStore' import { attribute } from '../../selectors' import { importText } from '../../action-creators' @@ -12,7 +12,7 @@ it('toggle on table view of parent of cursor (initial state without =view attrib store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -35,7 +35,7 @@ it('toggle on table view of parent of cursor (initial state =view attribute set store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - =view @@ -61,7 +61,7 @@ it('toggle on table view of parent of cursor (initial state without =view attrib // import thoughts store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - =view diff --git a/src/shortcuts/__tests__/undo.ts b/src/shortcuts/__tests__/undo.ts index 62e251176ac..d55902086eb 100644 --- a/src/shortcuts/__tests__/undo.ts +++ b/src/shortcuts/__tests__/undo.ts @@ -1,4 +1,4 @@ -import { MODALS, RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { MODALS, HOME_PATH, HOME_TOKEN } from '../../constants' import { exportContext } from '../../selectors' import { importText, newThought, setCursor } from '../../action-creators' import { createTestStore } from '../../test-helpers/createTestStore' @@ -44,7 +44,7 @@ it('undo thought change', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b` @@ -54,15 +54,15 @@ it('undo thought change', () => { type: 'existingThoughtChange', newValue: 'aa', oldValue: 'a', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'a', rank: 0 }] }, { type: 'undoAction' } ]) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - a - b` @@ -75,7 +75,7 @@ it('group all navigation actions following an undoable(non-navigation) action an store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -104,8 +104,8 @@ it('group all navigation actions following an undoable(non-navigation) action an expect(cursorAfterFirstUndo).toMatchObject([ { value: 'a' }]) - const exportedAfterFirstUndo = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') - const expectedOutputAfterFirstUndo = `- ${ROOT_TOKEN} + const exportedAfterFirstUndo = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') + const expectedOutputAfterFirstUndo = `- ${HOME_TOKEN} - a - b1 - c @@ -119,8 +119,8 @@ it('group all navigation actions following an undoable(non-navigation) action an expect(cursorAfterSecondUndo).toMatchObject([ { value: 'a' }, { value: 'b' }]) - const exportedAfterSecondUndo = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') - const expectedOutputAfterSecondUndo = `- ${ROOT_TOKEN} + const exportedAfterSecondUndo = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') + const expectedOutputAfterSecondUndo = `- ${HOME_TOKEN} - a - b - c @@ -134,7 +134,7 @@ it('ignore dead actions/Combine dispensible actions with the preceding patch', ( store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -156,9 +156,9 @@ it('ignore dead actions/Combine dispensible actions with the preceding patch', ( { type: 'undoAction' } ]) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - a - b - c @@ -171,7 +171,7 @@ it('state remains unchanged if there are no inverse patches', () => { const store = createTestStore() store.dispatch(importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b @@ -193,7 +193,7 @@ it('newThought action should be merged with the succeeding patch', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - a - b` @@ -202,7 +202,7 @@ it('newThought action should be merged with the succeeding patch', () => { { type: 'newThought', value: 'd' }, { type: 'existingThoughtChange', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], oldValue: 'd', newValue: 'd1', rankInContext: 3, @@ -217,9 +217,9 @@ it('newThought action should be merged with the succeeding patch', () => { { type: 'undoAction' } ]) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - a - b - c` @@ -233,7 +233,7 @@ it('undo contiguous changes', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - A - B` @@ -242,22 +242,22 @@ it('undo contiguous changes', () => { type: 'existingThoughtChange', newValue: 'Atlantic', oldValue: 'A', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'A', rank: 0 }] }, { type: 'existingThoughtChange', newValue: 'Atlantic City', oldValue: 'Atlantic', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'Atlantic', rank: 0 }] }, { type: 'undoAction' } ]) - const exported = exportContext(store.getState(), [ROOT_TOKEN], 'text/plain') + const exported = exportContext(store.getState(), [HOME_TOKEN], 'text/plain') - const expectedOutput = `- ${ROOT_TOKEN} + const expectedOutput = `- ${HOME_TOKEN} - A - B` @@ -270,7 +270,7 @@ it('state.alert is omitted from the undo patch', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - A - B` @@ -294,7 +294,7 @@ it('clear patches when any undoable action is dispatched', () => { store.dispatch([ importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: ` - A - B`, @@ -304,7 +304,7 @@ it('clear patches when any undoable action is dispatched', () => { type: 'existingThoughtChange', newValue: 'Atlantic', oldValue: 'A', - context: [ROOT_TOKEN], + context: [HOME_TOKEN], path: [{ value: 'A', rank: 0 }] }, { type: 'newThought', value: 'New Jersey' }, diff --git a/src/shortcuts/bindContext.tsx b/src/shortcuts/bindContext.tsx index 978936590cd..6903e41f06b 100644 --- a/src/shortcuts/bindContext.tsx +++ b/src/shortcuts/bindContext.tsx @@ -1,6 +1,6 @@ import React from 'react' -import { isDocumentEditable, pathToContext, rootedParentOf } from '../util' -import { isContextViewActive, lastThoughtsFromContextChain, splitChain } from '../selectors' +import { isDocumentEditable, pathToContext } from '../util' +import { isContextViewActive, lastThoughtsFromContextChain, rootedParentOf, splitChain } from '../selectors' import { toggleAttribute } from '../action-creators' import { Icon as IconType, Shortcut } from '../types' @@ -24,7 +24,7 @@ const bindContextShortcut: Shortcut = { const { cursor } = state if (!cursor) return - const contextRanked = rootedParentOf(cursor) + const contextRanked = rootedParentOf(state, cursor) if (!cursor || !isContextViewActive(state, pathToContext(contextRanked))) return diff --git a/src/shortcuts/deleteEmptyThoughtOrOutdent.tsx b/src/shortcuts/deleteEmptyThoughtOrOutdent.tsx index 640cee6adb8..3123cb39d79 100644 --- a/src/shortcuts/deleteEmptyThoughtOrOutdent.tsx +++ b/src/shortcuts/deleteEmptyThoughtOrOutdent.tsx @@ -3,7 +3,7 @@ import { Key } from 'ts-key-enum' import { asyncFocus, ellipsize, headValue, isDivider, isDocumentEditable, parentOf, pathToContext } from '../util' import { getChildren, getThoughtBefore, getAllChildren, getChildrenRanked, hasChild, isContextViewActive, lastThoughtsFromContextChain, simplifyPath, splitChain } from '../selectors' import { State } from '../util/initialState' -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' import { isTouch } from '../browser' import { alert, deleteEmptyThought as deleteEmptyThoughtActionCreator, error, outdent } from '../action-creators' import { Icon as IconType, Shortcut, Thunk } from '../types' @@ -94,7 +94,7 @@ const isMergedThoughtDuplicate = (state: State) => { const mergedThoughtValue = prevThought.value + headValue(cursor) const context = pathToContext(showContexts && contextChain.length > 1 ? contextChain[contextChain.length - 2] : !showContexts && path.length > 1 ? parentOf(path) : - RANKED_ROOT) + HOME_PATH) const siblings = getAllChildren(state, context) const isDuplicate = !siblings.every(thought => thought.value !== mergedThoughtValue) return isDuplicate diff --git a/src/shortcuts/note.ts b/src/shortcuts/note.ts index 20c1350d2a6..4a06159bf45 100644 --- a/src/shortcuts/note.ts +++ b/src/shortcuts/note.ts @@ -4,7 +4,7 @@ import PencilIcon from '../components/icons/PencilIcon' import { asyncFocus, editableNode, isDocumentEditable, pathToContext, setSelection } from '../util' import { setAttribute, setNoteFocus } from '../action-creators' import { Shortcut } from '../types' -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' const noteShortcut: Shortcut = { id: 'note', @@ -68,7 +68,7 @@ const noteShortcut: Shortcut = { isActive: getState => { const state = getState() const { cursor } = state - const context = pathToContext(cursor ? simplifyPath(state, cursor) : RANKED_ROOT) + const context = pathToContext(cursor ? simplifyPath(state, cursor) : HOME_PATH) return attribute(state, context, '=note') != null } } diff --git a/src/shortcuts/pinOpen.tsx b/src/shortcuts/pinOpen.tsx index 1bdd9d710be..b23aa2e1e17 100644 --- a/src/shortcuts/pinOpen.tsx +++ b/src/shortcuts/pinOpen.tsx @@ -3,7 +3,7 @@ import { attributeEquals, simplifyPath } from '../selectors' import { parentOf, pathToContext } from '../util' import { toggleAttribute } from '../action-creators' import { Icon as IconType, Shortcut } from '../types' -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' // eslint-disable-next-line jsdoc/require-jsdoc const Icon = ({ size = 20, style }: IconType) => @@ -36,7 +36,7 @@ const pinOpenShortcut: Shortcut = { isActive: getState => { const state = getState() const { cursor } = state - const context = pathToContext(cursor ? simplifyPath(state, cursor) : RANKED_ROOT) + const context = pathToContext(cursor ? simplifyPath(state, cursor) : HOME_PATH) return attributeEquals(state, context, '=pin', 'true') } } diff --git a/src/shortcuts/pinSubthoughts.tsx b/src/shortcuts/pinSubthoughts.tsx index e56ecc4bbdc..9eca4302813 100644 --- a/src/shortcuts/pinSubthoughts.tsx +++ b/src/shortcuts/pinSubthoughts.tsx @@ -3,7 +3,7 @@ import { attributeEquals, simplifyPath } from '../selectors' import { pathToContext } from '../util' import { toggleAttribute } from '../action-creators' import { Icon as IconType, Shortcut } from '../types' -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' // eslint-disable-next-line jsdoc/require-jsdoc const Icon = ({ size = 20, style }: IconType) => @@ -40,7 +40,7 @@ const pinSubthoughtsShortcut: Shortcut = { isActive: getState => { const state = getState() const { cursor } = state - const context = pathToContext(cursor ? simplifyPath(state, cursor) : RANKED_ROOT) + const context = pathToContext(cursor ? simplifyPath(state, cursor) : HOME_PATH) return attributeEquals(state, context, '=pinChildren', 'true') } } diff --git a/src/shortcuts/proseView.tsx b/src/shortcuts/proseView.tsx index 6e39aab893a..086b28c3a45 100644 --- a/src/shortcuts/proseView.tsx +++ b/src/shortcuts/proseView.tsx @@ -3,7 +3,7 @@ import { attributeEquals, simplifyPath } from '../selectors' import { isDocumentEditable, pathToContext } from '../util' import { toggleAttribute } from '../action-creators' import { Icon as IconType, Shortcut } from '../types' -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' // eslint-disable-next-line jsdoc/require-jsdoc const Icon = ({ fill = 'black', size = 20, style }: IconType) => @@ -41,7 +41,7 @@ const proseViewShortcut: Shortcut = { isActive: getState => { const state = getState() const { cursor } = state - const context = pathToContext(cursor ? simplifyPath(state, cursor) : RANKED_ROOT) + const context = pathToContext(cursor ? simplifyPath(state, cursor) : HOME_PATH) return attributeEquals(state, context, '=view', 'Prose') } } diff --git a/src/shortcuts/splitSentences.tsx b/src/shortcuts/splitSentences.tsx index 4e0c621b5f2..5bd7146de17 100644 --- a/src/shortcuts/splitSentences.tsx +++ b/src/shortcuts/splitSentences.tsx @@ -4,7 +4,7 @@ import { parentOf, headValue, pathToContext } from '../util' import { alert, splitSentences } from '../action-creators' import { Action } from 'redux' import { getAllChildren, isContextViewActive } from '../selectors' -import { ROOT_TOKEN } from '../constants' +import { HOME_TOKEN } from '../constants' import { Thunk, Icon as IconType, Shortcut } from '../types' // eslint-disable-next-line jsdoc/require-jsdoc @@ -34,7 +34,7 @@ const splitSentencesShortcut: Shortcut = { const showContexts = cursor && isContextViewActive(state, parentOf(pathToContext(cursor))) const context = cursor && (showContexts && cursor.length > 2 ? pathToContext(parentOf(parentOf(cursor))) : !showContexts && cursor.length > 1 ? pathToContext(parentOf(cursor)) - : [ROOT_TOKEN]) + : [HOME_TOKEN]) const siblings = context && getAllChildren(state, context).map(({ value }) => value) const duplicates = _.intersection(sentences, siblings) if (duplicates.length !== 0) { diff --git a/src/shortcuts/toggleSort.tsx b/src/shortcuts/toggleSort.tsx index cc037e09e2a..444b71b218a 100644 --- a/src/shortcuts/toggleSort.tsx +++ b/src/shortcuts/toggleSort.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' import { attributeEquals, getSetting, simplifyPath } from '../selectors' import { setCursor, toggleAttribute } from '../action-creators' import { pathToContext } from '../util' @@ -29,7 +29,7 @@ const toggleSortShortcut: Shortcut = { const globalSort = getSetting(state, ['Global Sort']) const sortPreference = globalSort === 'Alphabetical' ? 'None' : 'Alphabetical' - const simplePath = simplifyPath(state, cursor || RANKED_ROOT) + const simplePath = simplifyPath(state, cursor || HOME_PATH) const context = pathToContext(simplePath) dispatch(toggleAttribute({ @@ -46,7 +46,7 @@ const toggleSortShortcut: Shortcut = { const state = getState() const { cursor } = state - const context = pathToContext(cursor ? simplifyPath(state, cursor) : RANKED_ROOT) + const context = pathToContext(cursor ? simplifyPath(state, cursor) : HOME_PATH) return attributeEquals(state, context, '=sort', 'Alphabetical') } diff --git a/src/shortcuts/toggleTableView.tsx b/src/shortcuts/toggleTableView.tsx index 79dda1e60ec..8a383986069 100644 --- a/src/shortcuts/toggleTableView.tsx +++ b/src/shortcuts/toggleTableView.tsx @@ -3,7 +3,7 @@ import { attributeEquals, simplifyPath } from '../selectors' import { pathToContext } from '../util' import { toggleAttribute } from '../action-creators' import { Icon as IconType, Shortcut } from '../types' -import { RANKED_ROOT } from '../constants' +import { HOME_PATH } from '../constants' // eslint-disable-next-line jsdoc/require-jsdoc const Icon = ({ size = 20, style }: IconType) => @@ -38,7 +38,7 @@ const toggleTableViewShortcut: Shortcut = { isActive: getState => { const state = getState() const { cursor } = state - const context = pathToContext(cursor ? simplifyPath(state, cursor) : RANKED_ROOT) + const context = pathToContext(cursor ? simplifyPath(state, cursor) : HOME_PATH) return attributeEquals(state, context, '=view', 'Table') } } diff --git a/src/test-helpers/dataProviderTest.ts b/src/test-helpers/dataProviderTest.ts index 37c53e7af86..d43b2e05ddf 100644 --- a/src/test-helpers/dataProviderTest.ts +++ b/src/test-helpers/dataProviderTest.ts @@ -1,6 +1,6 @@ import _ from 'lodash' import all from 'it-all' -import { EM_TOKEN, RANKED_ROOT, ROOT_TOKEN } from '../constants' +import { ABSOLUTE_TOKEN, EM_TOKEN, HOME_PATH, HOME_TOKEN } from '../constants' import getDescendantThoughts from '../data-providers/data-helpers/getDescendantThoughts' import getManyDescendants from '../data-providers/data-helpers/getManyDescendants' import getContext from '../data-providers/data-helpers/getContext' @@ -64,7 +64,7 @@ expect.extend({ /** Import text into the root of a blank initial state. */ const importThoughts = (text: string) => { - const stateNew = importText(initialState(), { path: RANKED_ROOT, text }) + const stateNew = importText(initialState(), { path: HOME_PATH, text }) return { contextIndex: stateNew.thoughts.contextIndex, thoughtIndex: stateNew.thoughts.thoughtIndex, @@ -263,25 +263,25 @@ const dataProviderTest = (provider: DataProvider) => { test('default', async () => { const { contextIndex, thoughtIndex } = importThoughts(` - - x - - y - - z - - a - - b - `) + - x + - y + - z + - a + - b + `) await provider.updateContextIndex(contextIndex) await provider.updateThoughtIndex(thoughtIndex) - const thoughtChunks = await all(getDescendantThoughts(provider, [ROOT_TOKEN])) + const thoughtChunks = await all(getDescendantThoughts(provider, [HOME_TOKEN])) const thoughts = thoughtChunks.reduce(_.ary(mergeThoughts, 2)) expect(thoughts.contextIndex).toEqual( - _.omit(contextIndex, hashContext([EM_TOKEN])) + _.omit(contextIndex, hashContext([EM_TOKEN]), hashContext([ABSOLUTE_TOKEN])) ) // do not match em context, since we are just asserting the imported thoughts - const thoughtIndexWithoutEm = _.omit(thoughtIndex, hashThought(EM_TOKEN)) + const thoughtIndexWithoutEm = _.omit(thoughtIndex, hashThought(EM_TOKEN), hashThought(ABSOLUTE_TOKEN)) // support optional id property // dexie returns an id while firebase does not @@ -307,11 +307,11 @@ const dataProviderTest = (provider: DataProvider) => { await provider.updateContextIndex(contextIndex) await provider.updateThoughtIndex(thoughtIndex) - const thoughtChunks = await all(getDescendantThoughts(provider, [ROOT_TOKEN])) + const thoughtChunks = await all(getDescendantThoughts(provider, [HOME_TOKEN])) const thoughts = thoughtChunks.reduce(_.ary(mergeThoughts, 2)) expect(thoughts.contextIndex).toEqual( - _.omit(contextIndex, hashContext([EM_TOKEN])) + _.omit(contextIndex, hashContext([EM_TOKEN]), hashContext([ABSOLUTE_TOKEN])) ) // support optional id property @@ -466,7 +466,7 @@ const dataProviderTest = (provider: DataProvider) => { await provider.updateContextIndex(contextIndex) await provider.updateThoughtIndex(thoughtIndex) - const thoughtChunks = await all(getDescendantThoughts(provider, [ROOT_TOKEN])) + const thoughtChunks = await all(getDescendantThoughts(provider, [HOME_TOKEN])) // flatten the thought chunks // preserve chunk order @@ -616,7 +616,7 @@ const dataProviderTest = (provider: DataProvider) => { const stateNew = reducerFlow([ - importText({ path: RANKED_ROOT, text: rootText }), + importText({ path: HOME_PATH, text: rootText }), importText({ path: [{ value: EM_TOKEN, rank: 0 }], text: emText }), ])(initialState()) diff --git a/src/util/__tests__/importHtml.ts b/src/util/__tests__/importHtml.ts index a1bd8d23fe1..ad319ab8479 100644 --- a/src/util/__tests__/importHtml.ts +++ b/src/util/__tests__/importHtml.ts @@ -1,5 +1,5 @@ -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' -import { importHtml, mergeUpdates, removeRoot } from '../../util' +import { HOME_PATH, HOME_TOKEN } from '../../constants' +import { importHtml, mergeUpdates, removeHome } from '../../util' import { exportContext } from '../../selectors' import { initialState, State } from '../../util/initialState' import { SimplePath } from '../../types' @@ -10,7 +10,7 @@ const importExport = (html: string) => { const { contextIndexUpdates: contextIndex, thoughtIndexUpdates: thoughtIndex, - } = importHtml(state, RANKED_ROOT, html) + } = importHtml(state, HOME_PATH, html) const stateNew = { ...state, thoughts: { @@ -19,10 +19,10 @@ const importExport = (html: string) => { thoughtIndex, } } - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') // remove root, de-indent (trim), and append newline to make tests cleaner - return removeRoot(exported) + return removeHome(exported) } it('simple', () => { @@ -319,14 +319,14 @@ it('paste multiple thoughts in non-empty cursor', () => {
  • y
  • ` - const state1 = importHtmlReducer(initialState(), RANKED_ROOT, initialHtml) + const state1 = importHtmlReducer(initialState(), HOME_PATH, initialHtml) const simplePath = [{ value: 'a', rank: 0 }, { value: 'b', rank: 0 }] as SimplePath const state2 = importHtmlReducer(state1, simplePath, importedHtml) - const exported = exportContext(state2, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(state2, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b - x @@ -362,14 +362,14 @@ it('set cursor on last thought after importing multiple thoughts in non-empty cu
  • y
  • ` - const state1 = importHtmlReducer(initialState(), RANKED_ROOT, initialHtml) + const state1 = importHtmlReducer(initialState(), HOME_PATH, initialHtml) const simplePath = [{ value: 'a', rank: 0 }, { value: 'b', rank: 0 }] as SimplePath const state2 = importHtmlReducer(state1, simplePath, importedHtml) - const exported = exportContext(state2, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(state2, [HOME_TOKEN], 'text/plain') - expect(exported).toBe(`- ${ROOT_TOKEN} + expect(exported).toBe(`- ${HOME_TOKEN} - a - b - x diff --git a/src/util/__tests__/importText.ts b/src/util/__tests__/importText.ts index 354f2fe26cc..be35ce57d66 100644 --- a/src/util/__tests__/importText.ts +++ b/src/util/__tests__/importText.ts @@ -1,5 +1,5 @@ import { validate as uuidValidate } from 'uuid' -import { EM_TOKEN, RANKED_ROOT, ROOT_TOKEN } from '../../constants' +import { ABSOLUTE_TOKEN, EM_TOKEN, HOME_PATH, HOME_TOKEN } from '../../constants' import { hashContext, hashThought, never, reducerFlow, timestamp } from '../../util' import { initialState, State } from '../../util/initialState' import { exportContext, getParent, rankThoughtsFirstMatch } from '../../selectors' @@ -9,8 +9,8 @@ import { SimplePath } from '../../types' /** Helper function that imports html and exports it as plaintext. */ const importExport = (text: string) => { - const stateNew = importText(initialState(), { path: RANKED_ROOT, text }) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const stateNew = importText(initialState(), { path: HOME_PATH, text }) + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') // remove root, de-indent (trim), and append newline to make tests cleaner const exportedWithoutRoot = exported.slice(exported.indexOf('\n')) @@ -31,10 +31,10 @@ it('basic import with proper thought structure', () => { const now = timestamp() - const stateNew = importText(initialState(now), { path: RANKED_ROOT, text, lastUpdated: now }) + const stateNew = importText(initialState(now), { path: HOME_PATH, text, lastUpdated: now }) const { contextIndex, thoughtIndex } = stateNew.thoughts - const childAId = getParent(stateNew, [ROOT_TOKEN])?.children[0]?.id + const childAId = getParent(stateNew, [HOME_TOKEN])?.children[0]?.id const childBId = getParent(stateNew, ['a'])?.children[0]?.id expect(contextIndex).toMatchObject({ @@ -46,9 +46,9 @@ it('basic import with proper thought structure', () => { // TODO: Is this expected? pending: true, }, - [hashContext([ROOT_TOKEN])]: { - id: hashContext([ROOT_TOKEN]), - context: [ROOT_TOKEN], + [hashContext([HOME_TOKEN])]: { + id: hashContext([HOME_TOKEN]), + context: [HOME_TOKEN], children: [{ // tautological; full assertion below id: childAId, @@ -56,6 +56,13 @@ it('basic import with proper thought structure', () => { rank: 0, }], }, + [hashContext([ABSOLUTE_TOKEN])]: { + // id: hashContext([ABSOLUTE_TOKEN]), + context: [ABSOLUTE_TOKEN], + children: [], + lastUpdated: never(), + pending: true + }, [hashContext(['a'])]: { id: hashContext(['a']), context: ['a'], @@ -75,8 +82,8 @@ it('basic import with proper thought structure', () => { expect(uuidValidate(childBId!)).toBe(true) expect(thoughtIndex).toMatchObject({ - [hashThought(ROOT_TOKEN)]: { - value: ROOT_TOKEN, + [hashThought(HOME_TOKEN)]: { + value: HOME_TOKEN, contexts: [], created: now, lastUpdated: never(), @@ -87,11 +94,17 @@ it('basic import with proper thought structure', () => { created: now, lastUpdated: never(), }, + [hashThought(ABSOLUTE_TOKEN)]: { + value: ABSOLUTE_TOKEN, + contexts: [], + created: now, + lastUpdated: never(), + }, [hashThought('a')]: { value: 'a', contexts: [{ id: childAId, - context: [ROOT_TOKEN], + context: [HOME_TOKEN], rank: 0, }], created: now, @@ -133,8 +146,8 @@ it('import and merge descendants', () => { const now = timestamp() const newState = reducerFlow([ - importText({ text: initialText, path: RANKED_ROOT, lastUpdated: now }), - newThought({ at: RANKED_ROOT, value: '' }), + importText({ text: initialText, path: HOME_PATH, lastUpdated: now }), + newThought({ at: HOME_PATH, value: '' }), (state: State) => importText(state, { path: rankThoughtsFirstMatch(state, ['']), text: mergeText, @@ -142,9 +155,9 @@ it('import and merge descendants', () => { }) ])(initialState(now)) - const exported = exportContext(newState, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(newState, [HOME_TOKEN], 'text/plain') - const expectedExport = `- ${ROOT_TOKEN} + const expectedExport = `- ${HOME_TOKEN} - a - b - c @@ -158,8 +171,8 @@ it('import and merge descendants', () => { const { contextIndex } = newState.thoughts expect(contextIndex).toMatchObject({ - [hashContext([ROOT_TOKEN])]: { - context: [ROOT_TOKEN], + [hashContext([HOME_TOKEN])]: { + context: [HOME_TOKEN], children: [{ value: 'a', rank: 0, @@ -269,17 +282,17 @@ it('two root thoughts', () => { it('skip root token', () => { - const text = `- ${ROOT_TOKEN} + const text = `- ${HOME_TOKEN} - a - b - c - d` - const stateNew = importText(initialState(), { path: RANKED_ROOT, text }) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const stateNew = importText(initialState(), { path: HOME_PATH, text }) + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') expect(exported) - .toBe(`- ${ROOT_TOKEN} + .toBe(`- ${HOME_TOKEN} - a - b - c @@ -294,11 +307,11 @@ it('skip em token', () => { - c - d` - const stateNew = importText(initialState(), { path: RANKED_ROOT, text }) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const stateNew = importText(initialState(), { path: HOME_PATH, text }) + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') expect(exported) - .toBe(`- ${ROOT_TOKEN} + .toBe(`- ${HOME_TOKEN} - a - b - c @@ -315,7 +328,7 @@ it('duplicate thoughts', () => { ` const now = timestamp() - const imported = importText(initialState(), { path: RANKED_ROOT, text, lastUpdated: now }) + const imported = importText(initialState(), { path: HOME_PATH, text, lastUpdated: now }) const lexeme = imported.thoughts.thoughtIndex[hashThought('m')] const childAId = lexeme.contexts[0]?.id @@ -431,7 +444,7 @@ it('imports Roam json', () => { it('replace empty cursor', () => { - const text = `- ${ROOT_TOKEN} + const text = `- ${HOME_TOKEN} - a - b` @@ -442,7 +455,7 @@ it('replace empty cursor', () => { const stateNew = reducerFlow([ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), // manually change `b` to empty thought since importText skips empty thoughts existingThoughtChange({ @@ -459,10 +472,10 @@ it('replace empty cursor', () => { ])(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') expect(exported) - .toBe(`- ${ROOT_TOKEN} + .toBe(`- ${HOME_TOKEN} - a - x - y`) @@ -470,7 +483,7 @@ it('replace empty cursor', () => { it('replace empty cursor without affecting siblings', () => { - const text = `- ${ROOT_TOKEN} + const text = `- ${HOME_TOKEN} - a - b - c @@ -483,7 +496,7 @@ it('replace empty cursor without affecting siblings', () => { const stateNew = reducerFlow([ - importText({ path: RANKED_ROOT, text }), + importText({ path: HOME_PATH, text }), // manually change `c` to empty thought since importText skips empty thoughts existingThoughtChange({ @@ -500,10 +513,10 @@ it('replace empty cursor without affecting siblings', () => { ])(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') expect(exported) - .toBe(`- ${ROOT_TOKEN} + .toBe(`- ${HOME_TOKEN} - a - b - x @@ -529,10 +542,10 @@ it('import as subthoughts of non-empty cursor', () => { ])(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') expect(exported) - .toBe(`- ${ROOT_TOKEN} + .toBe(`- ${HOME_TOKEN} - a - x - y`) @@ -548,14 +561,14 @@ it('decode HTML entities', () => { ` const stateNew = importText({ - path: RANKED_ROOT, + path: HOME_PATH, text: paste, })(initialState()) - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') expect(exported) - .toBe(`- ${ROOT_TOKEN} + .toBe(`- ${HOME_TOKEN} - one & two - three < four`) }) diff --git a/src/util/__tests__/removeHome.ts b/src/util/__tests__/removeHome.ts new file mode 100644 index 00000000000..e8ec02086c5 --- /dev/null +++ b/src/util/__tests__/removeHome.ts @@ -0,0 +1,26 @@ +import { HOME_TOKEN } from '../../constants' +import { removeHome } from '../removeHome' + +it('remove home thought', () => { + const exported = `- ${HOME_TOKEN} + - a + - b + - c` + + const expectedResult = ` +- a + - b +- c +` + + expect(removeHome(exported)).toBe(expectedResult) +}) + +it('do not remove first thought if it is not a home root', () => { + const exported = `- k + - a + - b + - c` + + expect(removeHome(exported)).toBe(exported) +}) diff --git a/src/util/__tests__/removeRoot.ts b/src/util/__tests__/removeRoot.ts deleted file mode 100644 index cf9f3958d2a..00000000000 --- a/src/util/__tests__/removeRoot.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ROOT_TOKEN } from '../../constants' -import { removeRoot } from '../removeRoot' - -it('remove root thought', () => { - const exported = `- ${ROOT_TOKEN} - - a - - b - - c` - - const expectedResult = ` -- a - - b -- c -` - - expect(removeRoot(exported)).toBe(expectedResult) -}) - -it('do not remove first thought if it is not a root', () => { - const exported = `- k - - a - - b - - c` - - expect(removeRoot(exported)).toBe(exported) -}) diff --git a/src/util/__tests__/roamJsonToBlocks.ts b/src/util/__tests__/roamJsonToBlocks.ts index 13c89de27e4..72094d71466 100644 --- a/src/util/__tests__/roamJsonToBlocks.ts +++ b/src/util/__tests__/roamJsonToBlocks.ts @@ -1,5 +1,5 @@ -import { RANKED_ROOT, ROOT_TOKEN } from '../../constants' -import { hashContext, hashThought, removeRoot } from '../../util' +import { HOME_PATH, HOME_TOKEN } from '../../constants' +import { hashContext, hashThought, removeHome } from '../../util' import { exportContext } from '../../selectors' import { State, initialState } from '../initialState' import { RoamBlock, RoamPage, roamJsonToBlocks } from '../roamJsonToBlocks' @@ -71,7 +71,7 @@ const importExport = (roamJson: RoamPage[]) => { const { contextIndexUpdates: contextIndex, thoughtIndexUpdates: thoughtIndex, - } = importJSON(state, RANKED_ROOT as SimplePath, thoughtsJSON, { skipRoot: false }) + } = importJSON(state, HOME_PATH as SimplePath, thoughtsJSON, { skipRoot: false }) const stateNew: State = { ...initialState(), @@ -81,8 +81,8 @@ const importExport = (roamJson: RoamPage[]) => { thoughtIndex, } } - const exported = exportContext(stateNew, [ROOT_TOKEN], 'text/plain') - return removeRoot(exported) + const exported = exportContext(stateNew, [HOME_TOKEN], 'text/plain') + return removeHome(exported) } test('it should convert a flat Roam json into a list of thoughts', () => { @@ -203,7 +203,7 @@ test('it should save create-time as created and edit-time as lastUpdated', () => const { contextIndexUpdates: contextIndex, thoughtIndexUpdates: thoughtIndex, - } = importJSON(initialState(), RANKED_ROOT as SimplePath, blocks, { skipRoot: false }) + } = importJSON(initialState(), HOME_PATH as SimplePath, blocks, { skipRoot: false }) /** Gets the edit-time of a RoamBlock. */ const editTimeOf = (value: string) => { diff --git a/src/util/importJSON.ts b/src/util/importJSON.ts index 2380cd6e40b..8d9f5418b99 100644 --- a/src/util/importJSON.ts +++ b/src/util/importJSON.ts @@ -1,6 +1,6 @@ import _ from 'lodash' -import { EM_TOKEN, ID, ROOT_TOKEN } from '../constants' -import { getThought, getAllChildren, nextSibling, getNextRank } from '../selectors' +import { EM_TOKEN, HOME_TOKEN } from '../constants' +import { getNextRank, getThought, getAllChildren, nextSibling, rootedParentOf } from '../selectors' import { Block, Child, Context, Index, Lexeme, Parent, SimplePath, Timestamp, ThoughtIndices } from '../types' import { State } from '../util/initialState' @@ -15,7 +15,6 @@ import { parentOf, pathToContext, removeContext, - rootedParentOf, timestamp, unroot, } from '../util' @@ -40,7 +39,7 @@ const skipRootThought = (blocks: Block[]) => { /** Generates a Parent and Lexeme for inserting a new thought into a context. */ const insertThought = (state: State, parentOld: Parent, value: string, context: Context, rank: number, created: Timestamp = timestamp(), lastUpdated: Timestamp = timestamp()): ThoughtPair => { - const rootContext = context.length > 0 ? context : [ROOT_TOKEN] + const rootContext = context.length > 0 ? context : [HOME_TOKEN] const id = createId() const lexemeOld = getThought(state, value) @@ -96,7 +95,7 @@ const saveThoughts = (state: State, contextIndexUpdates: Index, thoughtI } const updates = blocks.reduce((accum, block, index) => { - const skipLevel = block.scope === ROOT_TOKEN || block.scope === EM_TOKEN + const skipLevel = block.scope === HOME_TOKEN || block.scope === EM_TOKEN const rank = startRank + index * rankIncrement if (!skipLevel) { const value = block.scope.trim() @@ -169,7 +168,7 @@ export const importJSON = (state: State, simplePath: SimplePath, blocks: Block[] // use getNextRank instead of getRankAfter because if dest is not empty then we need to import thoughts inside it const rankStart = destEmpty ? destThought.rank : getNextRank(state, pathToContext(simplePath)) const rankIncrement = getRankIncrement(state, blocks, context, destThought, rankStart) - const rootedContext = pathToContext(rootedParentOf(simplePath)) + const rootedContext = pathToContext(rootedParentOf(state, simplePath)) const contextEncoded = hashContext(rootedContext) // if the thought where we are pasting is empty, replace it instead of adding to it @@ -188,7 +187,7 @@ export const importJSON = (state: State, simplePath: SimplePath, blocks: Block[] } } - const importPath = (destEmpty ? rootedParentOf : ID)(simplePath) + const importPath = destEmpty ? rootedParentOf(state, simplePath) : simplePath const importContext = pathToContext(importPath) const blocksNormalized = skipRoot ? skipRootThought(blocks) : blocks diff --git a/src/util/index.ts b/src/util/index.ts index 65fc62c45ce..1b902d9d002 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -52,9 +52,11 @@ export { isElementHiddenByAutoFocus } from './isElementHiddenByAutoFocus' export { isElementInViewport } from './isElementInViewport' export { isEM } from './isEM' export { isFunction } from './isFunction' +export { isHome } from './isHome' export { isHTML } from './isHTML' export { isPath } from './isPath' export { isRoot } from './isRoot' +export { isAbsolute } from './isAbsolute' export { isThoughtArchived } from './isThoughtArchived' export { isURL } from './isURL' export { joinConjunction } from './joinConjunction' @@ -80,9 +82,8 @@ export { reducerFlow } from './reducerFlow' export { regExpEscapeSelector } from './regExpEscapeSelector' export { removeContext } from './removeContext' export { removeDuplicatedContext } from './removeDuplicatedContext' -export { removeRoot } from './removeRoot' +export { removeHome } from './removeHome' export { roamJsonToBlocks } from './roamJsonToBlocks' -export { rootedParentOf } from './rootedParentOf' export { scrollIntoViewIfNeeded } from './scrollIntoViewIfNeeded' export { selectNextEditable } from './selectNextEditable' export { setSelection } from './setSelection' diff --git a/src/util/initialState.ts b/src/util/initialState.ts index 9cd4814f51f..48c47089c26 100644 --- a/src/util/initialState.ts +++ b/src/util/initialState.ts @@ -1,4 +1,4 @@ -import { EM_TOKEN, MODALS, ROOT_TOKEN, SCHEMA_LATEST } from '../constants' +import { ABSOLUTE_TOKEN, EM_TOKEN, MODALS, HOME_TOKEN, SCHEMA_LATEST } from '../constants' import globals from '../globals' import { Alert, Child, Context, Index, Lexeme, Parent, Patch, Path, SimplePath, Timestamp, ThoughtsInterface, User } from '../types' import { ExistingThoughtChangePayload } from '../reducers/existingThoughtChange' @@ -48,6 +48,7 @@ export interface State { autologin: boolean, contextViews: Index, cursor: Path | null, + cursorBeforeQuickAdd: Path | null, cursorBeforeSearch: Path | null, cursorHistory: Path[], cursorInitialized: boolean, @@ -87,6 +88,9 @@ export interface State { showTopControls: boolean, showBreadcrumbs: boolean, splitPosition: number, + rootContext: Context, + absoluteContextTime?: Timestamp, + transientFocus?: boolean, status: string, thoughts: ThoughtsInterface, toolbarOverlay?: string | null, @@ -98,10 +102,15 @@ export interface State { export const initialThoughts = (created: Timestamp = timestamp()): ThoughtsInterface => { const contextIndex = { - [hashContext([ROOT_TOKEN])]: { - // Note: Context hash is being used as id. Data provider tests are breaking because root and em context lack id. So adding it here - id: hashContext([ROOT_TOKEN]), - context: [ROOT_TOKEN], + [hashContext([HOME_TOKEN])]: { + context: [HOME_TOKEN], + children: [], + // start pending to trigger pullQueue fetch + pending: true, + lastUpdated: never() + }, + [hashContext([ABSOLUTE_TOKEN])]: { + context: [ABSOLUTE_TOKEN], children: [], // start pending to trigger pullQueue fetch pending: true, @@ -118,8 +127,15 @@ export const initialThoughts = (created: Timestamp = timestamp()): ThoughtsInter } const thoughtIndex = { - [hashThought(ROOT_TOKEN)]: { - value: ROOT_TOKEN, + [hashThought(HOME_TOKEN)]: { + value: HOME_TOKEN, + contexts: [], + // set to beginning of epoch to ensure that server thoughtIndex is always considered newer from init thoughtIndex + created, + lastUpdated: never() + }, + [hashThought(ABSOLUTE_TOKEN)]: { + value: ABSOLUTE_TOKEN, contexts: [], // set to beginning of epoch to ensure that server thoughtIndex is always considered newer from init thoughtIndex created, @@ -153,6 +169,7 @@ export const initialState = (created: Timestamp = timestamp()) => { contextViews: {}, cursor: null, cursorBeforeSearch: null, + cursorBeforeQuickAdd: null, cursorHistory: [], cursorInitialized: false, // tracks if the cursor has been restored from the url on first load and ensures it only happens once cursorOffset: 0, @@ -181,6 +198,7 @@ export const initialState = (created: Timestamp = timestamp()) => { showBreadcrumbs: true, // eslint-disable-next-line no-mixed-operators splitPosition: parseJsonSafe(typeof localStorage !== 'undefined' ? localStorage.getItem('splitPosition') : null, 0), + rootContext: [HOME_TOKEN], /* status: 'disconnected' Logged out or yet to connect to firebase, but not in explicit offline mode. 'connecting' Connecting to firebase. diff --git a/src/util/isAbsolute.ts b/src/util/isAbsolute.ts new file mode 100644 index 00000000000..41e96efee8e --- /dev/null +++ b/src/util/isAbsolute.ts @@ -0,0 +1,13 @@ +import { ABSOLUTE_TOKEN } from '../constants' +import { Child, ThoughtContext } from '../types' + +/** Returns true if the thoughts or path is the absolute context. */ +export const isAbsolute = (thoughts: (string | Child | ThoughtContext)[]): boolean => { + return thoughts.length === 1 && + !!thoughts[0] && + ( + (thoughts[0] as Child).value === ABSOLUTE_TOKEN || + (thoughts[0] as string) === ABSOLUTE_TOKEN || + ((thoughts[0] as ThoughtContext).context && isAbsolute((thoughts[0] as ThoughtContext).context)) + ) +} diff --git a/src/util/isHome.ts b/src/util/isHome.ts new file mode 100644 index 00000000000..2ff468e9723 --- /dev/null +++ b/src/util/isHome.ts @@ -0,0 +1,13 @@ +import { HOME_TOKEN } from '../constants' +import { Child, ThoughtContext } from '../types' + +/** Returns true if the thoughts or path is the home context. */ +export const isHome = (thoughts: (string | Child | ThoughtContext)[]): boolean => { + return thoughts.length === 1 && + !!thoughts[0] && + ( + (thoughts[0] as Child).value === HOME_TOKEN || + (thoughts[0] as string) === HOME_TOKEN || + ((thoughts[0] as ThoughtContext).context && isHome((thoughts[0] as ThoughtContext).context)) + ) +} diff --git a/src/util/isRoot.ts b/src/util/isRoot.ts index 4b98ffc2add..ce6e6f7f17a 100644 --- a/src/util/isRoot.ts +++ b/src/util/isRoot.ts @@ -1,12 +1,13 @@ -import { ROOT_TOKEN } from '../constants' +import { ROOT_CONTEXTS } from '../constants' import { Child, ThoughtContext } from '../types' -/** Returns true if the thoughts or path is the root thought. */ -export const isRoot = (thoughts: (string | Child | ThoughtContext)[]): boolean => - thoughts.length === 1 && +/** Returns true if the thoughts or path is the root context. */ +export const isRoot = (thoughts: (string | Child | ThoughtContext)[]): boolean => { + return thoughts.length === 1 && !!thoughts[0] && ( - (thoughts[0] as Child).value === ROOT_TOKEN || - (thoughts[0] as string) === ROOT_TOKEN || + ROOT_CONTEXTS.includes((thoughts[0] as Child).value) || + ROOT_CONTEXTS.includes(thoughts[0] as string) || ((thoughts[0] as ThoughtContext).context && isRoot((thoughts[0] as ThoughtContext).context)) ) +} diff --git a/src/util/nextThought.ts b/src/util/nextThought.ts index 0758e86f505..9811641b6b5 100644 --- a/src/util/nextThought.ts +++ b/src/util/nextThought.ts @@ -1,7 +1,7 @@ import _ from 'lodash' -import { ALLOW_SINGLE_CONTEXT, RANKED_ROOT, ROOT_TOKEN } from '../constants' -import { parentOf, equalArrays, head, pathToContext, once, rootedParentOf, unroot } from '../util' -import { firstVisibleChild, getContexts, getContextsSortedAndRanked, getThought, isContextViewActive, splitChain, nextSibling as thoughtNextSibling } from '../selectors' +import { ALLOW_SINGLE_CONTEXT, HOME_PATH, HOME_TOKEN } from '../constants' +import { parentOf, equalArrays, head, pathToContext, once, unroot } from '../util' +import { firstVisibleChild, getContexts, getContextsSortedAndRanked, getThought, isContextViewActive, splitChain, nextSibling as thoughtNextSibling, rootedParentOf } from '../selectors' import { State } from '../util/initialState' import { Child, Context, Path, SimplePath, ThoughtContext } from '../types' @@ -20,11 +20,11 @@ const contextWithThoughtRank = (state: State, contextInfo: ThoughtContext | null /** Returns the matching context. */ const matchedContext = () => { const contextToMatch = contextInfo.context.slice(0, index + 1) - // const filterRoot = context => context.filter(item => item !== ROOT_TOKEN) + // const filterRoot = context => context.filter(item => item !== HOME_TOKEN) return thought.contexts.find(thoughtContext => equalArrays([...unroot(thoughtContext.context), thought.value], contextToMatch))! } // the root thought doesn't have a rank - return value === ROOT_TOKEN ? RANKED_ROOT[0] : { value, rank: matchedContext().rank } + return value === HOME_TOKEN ? HOME_PATH[0] : { value, rank: matchedContext().rank } }) as SimplePath } @@ -122,7 +122,7 @@ const nextInContextView = (state: State, value: string, rank: number, path: Path } // if the focus is on or within a context else if (isContextViewActive(state, pathToContext(rankedContext))) { - const firstChild = once(() => firstVisibleChild(state, getContextFromContextChain(state, contextChain) || [ROOT_TOKEN])) + const firstChild = once(() => firstVisibleChild(state, getContextFromContextChain(state, contextChain) || [HOME_TOKEN])) const nextSibling = nextSiblingContext(state, rank, context) const rankedContextHead = head(rankedContext) @@ -161,7 +161,7 @@ const nextInThoughtView = (state: State, value: string, context: Context, rank: if (contextChain.length > 1 && _.last(contextChain)!.length === 1) return null - const firstChild = !ignoreChildren && firstVisibleChild(state, contextChain.length > 1 ? getContextFromContextChain(state, contextChain) : pathToContext(path) || [ROOT_TOKEN]) + const firstChild = !ignoreChildren && firstVisibleChild(state, contextChain.length > 1 ? getContextFromContextChain(state, contextChain) : pathToContext(path) || [HOME_TOKEN]) const thoughtViewPath = once(() => !ignoreChildren && contextChain.length > 1 ? getPathFromContextChain(state, contextChain) : path) // pathToContext is expensive than duplicate condition check hence using the former @@ -175,7 +175,7 @@ const nextInThoughtView = (state: State, value: string, context: Context, rank: * Only calculate uncle if not at root. */ const nextUncle = () => { - const parentContext = context.length === 1 ? [ROOT_TOKEN] : parentOf(thoughtViewContext()) + const parentContext = context.length === 1 ? [HOME_TOKEN] : parentOf(thoughtViewContext()) const contextChainForParentThought = [...contextChain.slice(0, -1), parentOf(head(contextChain))] const parentPath = parentOf(thoughtViewPath()) return nextInThoughtView(state, parentThought.value, parentContext, parentThought.rank, parentPath, contextChainForParentThought, true) @@ -214,9 +214,9 @@ const nextInThoughtView = (state: State, value: string, context: Context, rank: } /** Gets the next thought whether it is a child, sibling, or uncle, and its respective contextChain. */ -export const nextThought = (state: State, path: Path = RANKED_ROOT) => { +export const nextThought = (state: State, path: Path = HOME_PATH) => { const { value, rank } = head(path) - const parentPath = rootedParentOf(path) + const parentPath = rootedParentOf(state, path) const contextChain = splitChain(state, path) const context = pathToContext(parentPath) diff --git a/src/util/removeRoot.ts b/src/util/removeHome.ts similarity index 61% rename from src/util/removeRoot.ts rename to src/util/removeHome.ts index 4662d39baf4..92a3027e249 100644 --- a/src/util/removeRoot.ts +++ b/src/util/removeHome.ts @@ -1,13 +1,13 @@ -import { isRoot } from './isRoot' +import { isHome } from './isHome' /** - * Remove root, de-indent (trim), and append newline. + * Remove home token, de-indent (trim), and append newline. */ -export const removeRoot = (exported: string) => { +export const removeHome = (exported: string) => { const firstLineBreakIndex = exported.indexOf('\n') const firstThought = exported.slice(0, firstLineBreakIndex).slice(1).trim() - return isRoot([firstThought]) + return isHome([firstThought]) ? exported .slice(firstLineBreakIndex) .split('\n') diff --git a/src/util/rootedParentOf.ts b/src/util/rootedParentOf.ts deleted file mode 100644 index f3c8515f505..00000000000 --- a/src/util/rootedParentOf.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { RANKED_ROOT, ROOT_TOKEN } from '../constants' -import { parentOf } from '../util' -import { Context, Path } from '../types' - -/** Checks if an object is of type Path. */ -const isPath = (o: Context | Path): o is Path => - o.length > 0 && Object.prototype.hasOwnProperty.call(o[0], 'value') - -/** Get the parentOf thoughts or the root if there are none. */ -export const rootedParentOf = (thoughts: T): T => - thoughts && thoughts.length > 1 - ? parentOf(thoughts) as T - : isPath(thoughts) - ? RANKED_ROOT as unknown as T - : [ROOT_TOKEN] as T diff --git a/src/util/unroot.ts b/src/util/unroot.ts index eeab564c5ca..dffaf4087d4 100644 --- a/src/util/unroot.ts +++ b/src/util/unroot.ts @@ -1,7 +1,7 @@ import { isRoot } from './isRoot' import { Context, Path } from '../types' -/** Removes ROOT_TOKEN from the beginning of a Context or Path. */ +/** Removes root tokens from the beginning of a Context or Path. */ export const unroot = (thoughts: T): T => thoughts.length > 0 && isRoot(thoughts.slice(0, 1)) ? thoughts.slice(1) as T