Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quick add #1042

Merged
merged 3 commits into from
Feb 20, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions src/components/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ 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, ABSOLUTE_PATH, HOME_PATH, TRANSIENT_ABSOLUTE_CHILD_PATH, TUTORIAL2_STEP_SUCCESS } from '../constants'
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'
Expand All @@ -20,15 +20,20 @@ import { SimplePath } from '../types'
const tutorialLocal = localStorage['Settings/Tutorial'] === 'On'
const tutorialStepLocal = +(localStorage['Settings/Tutorial Step'] || 1)

const TransientChildPath = [{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

camelCase

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 = <Editable
transient={true}
path={TRANSIENT_ABSOLUTE_CHILD_PATH}
simplePath={TRANSIENT_ABSOLUTE_CHILD_PATH as SimplePath}
path={TransientChildPath}
simplePath={TransientChildPath}
rank={0}
/>

Expand All @@ -43,14 +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 isAbsoluteToken = isAbsolute(rootContext)
const children = getAllChildren(state, [rootContext[0]])
const isAbsoluteContext = isAbsolute(rootContext)
const children = getAllChildren(state, rootContext)

const rankedRoot = isAbsoluteToken ? ABSOLUTE_PATH : HOME_PATH
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, [rootContext[0]], '=sort') || 'None'
const rootSort = attribute(state, rootContext, '=sort') || 'None'

return {
search,
Expand All @@ -60,7 +65,7 @@ const mapStateToProps = (state: State) => {
rootThoughtsLength,
noteFocus,
rootSort,
isAbsoluteToken,
isAbsoluteContext,
rootContext,
}
}
Expand Down Expand Up @@ -92,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, isAbsoluteToken } = props
const { search, isTutorialLocal, tutorialStep, showModal, showRemindMeLaterModal, cursorBack: moveCursorBack, toggleSidebar, rootThoughtsLength, noteFocus, rootSort, isAbsoluteContext } = props
const contentRef = useRef<HTMLDivElement>(null)

/** Removes the cursor if the click goes all the way through to the content. Extends cursorBack with logic for closing modals. */
Expand Down Expand Up @@ -135,8 +140,8 @@ const Content: ContentComponent = props => {
? <Search />
: <React.Fragment>
{rootThoughtsLength === 0 ?
isAbsoluteToken ? TransientEditable : <NewThoughtInstructions childrenLength={rootThoughtsLength} isTutorial={isTutorialLocal} /> : <Subthoughts
simplePath={isAbsoluteToken ? ABSOLUTE_PATH : HOME_PATH}
isAbsoluteContext ? TransientEditable : <NewThoughtInstructions childrenLength={rootThoughtsLength} isTutorial={isTutorialLocal} /> : <Subthoughts
simplePath={isAbsoluteContext ? ABSOLUTE_PATH : HOME_PATH}
expandable={true}
sort={rootSort}
/>}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Subthoughts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ export const SubthoughtsComponent = ({
simply using index i as key will result in very sophisticated rerendering when new Empty thoughts are added.
The main problem is that when a new Thought is added it will get key (index) of the previous thought,
causing React DOM to think it as old component that needs re-render and thus the new thoughyt won't be able to mount itself as a new component.
Path

By using child's rank we have unique key for every new thought.
Using unique rank will help React DOM to properly identify old components and the new one. Thus eliminating sophisticated
re-renders.
Expand Down
12 changes: 1 addition & 11 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** Defines app-wide constants. */

import raw from 'raw.macro'
import { Index, Path, SimplePath } from './types'
import { Index, SimplePath } from './types'

// maximum number of characters of children to allow expansion
export const MAX_DISTANCE_FROM_CURSOR = 3
Expand Down Expand Up @@ -110,16 +110,6 @@ 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 ROOT_PATH_MAP: Record<string, Path> = {
[HOME_TOKEN]: HOME_PATH,
[ABSOLUTE_TOKEN]: ABSOLUTE_PATH
}

export const TRANSIENT_ABSOLUTE_CHILD_PATH = [{
value: '',
rank: 0,
}]

export const ALLOW_SINGLE_CONTEXT = false

export const FIREBASE_CONFIG = {
Expand Down
4 changes: 2 additions & 2 deletions src/reducers/newThought.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
getPrevRank,
getRankAfter,
getRankBefore,
getRoot,
getRootPath,
getSetting,
hasChild,
isContextViewActive,
Expand Down Expand Up @@ -91,7 +91,7 @@ const newThought = (state: State, payload: NewThoughtPayload | string) => {
(tutorialStep === TUTORIAL_STEP_SECONDTHOUGHT_ENTER ||
tutorialStep === TUTORIAL_STEP_FIRSTTHOUGHT_ENTER))

const path = at || state.cursor || getRoot(state)
const path = at || state.cursor || getRootPath(state)

const simplePath = simplifyPath(state, path)

Expand Down
6 changes: 4 additions & 2 deletions src/selectors/getChildren.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,16 @@ 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)
const isCreatedAfterAbsoluteToggle = _.curry((state: State, child: Child | ThoughtContext) =>
child.lastUpdated && state.absoluteContextTime
&& child.lastUpdated > state.absoluteContextTime)

/**
* Children filter predicate used for rendering.
Expand Down
12 changes: 0 additions & 12 deletions src/selectors/getRoot.ts

This file was deleted.

18 changes: 18 additions & 0 deletions src/selectors/getRootPath.ts
Original file line number Diff line number Diff line change
@@ -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<string, SimplePath> = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

camelCase

[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
4 changes: 2 additions & 2 deletions src/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +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 getRoot } from './getRoot'
export { default as getRootPath } from './getRootPath'
export { default as getSetting } from './getSetting'
export { default as getSortPreference } from './getSortPreference'
export { default as getStyle } from './getStyle'
Expand All @@ -47,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, childrenFilterPredicate } from './getChildren'
export { firstVisibleChild, getAllChildren, getAllChildrenByContextHash, getChildren, getChildrenRanked, getAllChildrenSorted, getChildrenSorted, getParent, hasChildren, isChildVisible, childrenFilterPredicate } from './getChildren'
4 changes: 2 additions & 2 deletions src/selectors/rankThoughtsFirstMatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { contextChainToPath, equalArrays, equalThoughtRanked, head, headValue, i
import { getContexts, getContextsSortedAndRanked, getThought, getChildrenRanked, isContextViewActive, splitChain } from '../selectors'
import { State } from '../util/initialState'
import { Child, Context, Path } from '../types'
import getRoot from './getRoot'
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 getRoot(state)
if (isRoot(pathUnranked)) return getRootPath(state)

let pathResult: Path = HOME_PATH // eslint-disable-line fp/no-let
let prevParentContext = [HOME_TOKEN] // eslint-disable-line fp/no-let
Expand Down
13 changes: 8 additions & 5 deletions src/selectors/rootedParentOf.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { ROOT_PATH_MAP } from '../constants'
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<string, Path> = {
[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 = <T extends Context | Path>(state: State, thoughts: T): T => {

const rootToken = state.rootContext[0]

const startingRankedRoot = ROOT_PATH_MAP[rootToken]
const startingRankedRoot = RootPathMap[state.rootContext[0]]

return thoughts && thoughts.length > 1
? parentOf(thoughts) as T
: isPath(thoughts)
? startingRankedRoot as T
: [rootToken] as T
: state.rootContext as T
}

export default rootedParentOf
2 changes: 1 addition & 1 deletion src/util/isAbsolute.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ABSOLUTE_TOKEN } from '../constants'
import { Child, ThoughtContext } from '../types'

/** Returns true if the thoughts or path is the absolute root. */
/** 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] &&
Expand Down
2 changes: 1 addition & 1 deletion src/util/isHome.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HOME_TOKEN } from '../constants'
import { Child, ThoughtContext } from '../types'

/** Returns true if the thoughts or path is the home root. */
/** 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] &&
Expand Down