Changeset View
Changeset View
Standalone View
Standalone View
web/navigation-panels/chat-thread-ancestors.react.js
// @flow | // @flow | ||||
import classNames from 'classnames'; | import classnames from 'classnames'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { ChevronRight } from 'react-feather'; | |||||
import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; | |||||
import { useAncestorThreads } from 'lib/shared/ancestor-threads.js'; | import { useAncestorThreads } from 'lib/shared/ancestor-threads.js'; | ||||
import { colorIsDark } from 'lib/shared/thread-utils.js'; | |||||
import { useKeyserverAdmin } from 'lib/shared/user-utils.js'; | |||||
import type { ThreadInfo } from 'lib/types/thread-types.js'; | import type { ThreadInfo } from 'lib/types/thread-types.js'; | ||||
import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; | import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; | ||||
import css from './chat-thread-ancestors.css'; | import css from './chat-thread-ancestors.css'; | ||||
import CommIcon from '../CommIcon.react.js'; | |||||
const SHOW_SEE_FULL_STRUCTURE = false; | |||||
type ThreadAncestorsProps = { | type ThreadAncestorsProps = { | ||||
+threadInfo: ThreadInfo, | +threadInfo: ThreadInfo, | ||||
}; | }; | ||||
function ThreadAncestors(props: ThreadAncestorsProps): React.Node { | function ThreadAncestors(props: ThreadAncestorsProps): React.Node { | ||||
const { threadInfo } = props; | const { threadInfo } = props; | ||||
const { color: threadColor } = threadInfo; | |||||
const darkColor = colorIsDark(threadColor); | |||||
const threadColorStyle = React.useMemo( | |||||
() => ({ | |||||
backgroundColor: `#${threadColor}`, | |||||
color: darkColor | |||||
? 'var(--thread-ancestor-color-light)' | |||||
: 'var(--thread-ancestor-color-dark)', | |||||
}), | |||||
[darkColor, threadColor], | |||||
); | |||||
const fullStructureButtonColorStyle = React.useMemo( | |||||
() => ({ color: `#${threadColor}` }), | |||||
[threadColor], | |||||
); | |||||
const ancestorThreads = useAncestorThreads(threadInfo); | |||||
const community = ancestorThreads[0] ?? threadInfo; | const ancestorThreadsWithCommunity = useAncestorThreads(threadInfo); | ||||
const keyserverAdmin = useKeyserverAdmin(community); | |||||
const keyserverOwnerUsername = keyserverAdmin?.username; | |||||
const community = ancestorThreadsWithCommunity[0] ?? threadInfo; | |||||
const resolvedCommunity = useResolvedThreadInfo(community); | const resolvedCommunity = useResolvedThreadInfo(community); | ||||
const keyserverInfo = React.useMemo( | const threadHasNoAncestors = community === threadInfo; | ||||
() => ( | |||||
<div className={css.ancestorKeyserver}> | const ancestorThreads = ancestorThreadsWithCommunity.slice(1); | ||||
<div className={css.ancestorKeyserverOperator}> | |||||
<CommIcon icon="cloud-filled" size={12} /> | |||||
<span>{keyserverOwnerUsername}</span> | |||||
</div> | |||||
<div | |||||
style={threadColorStyle} | |||||
className={classNames(css.ancestorName, css.ancestorKeyserverName)} | |||||
> | |||||
{resolvedCommunity.uiName} | |||||
</div> | |||||
</div> | |||||
), | |||||
[resolvedCommunity.uiName, keyserverOwnerUsername, threadColorStyle], | |||||
); | |||||
const middlePath = React.useMemo(() => { | const chevronRight = React.useMemo(() => { | ||||
if (ancestorThreads.length < 2) { | if (threadHasNoAncestors) { | ||||
return null; | return null; | ||||
} | } | ||||
return ( | return <ChevronRight size={20} className={css.chevronRight} />; | ||||
<> | }, [threadHasNoAncestors]); | ||||
<SWMansionIcon | |||||
className={css.ancestorSeparator} | |||||
icon="chevron-right" | |||||
size={12} | |||||
/> | |||||
<div style={threadColorStyle} className={css.ancestorName}> | |||||
… | |||||
</div> | |||||
</> | |||||
); | |||||
}, [ancestorThreads.length, threadColorStyle]); | |||||
const threadHasNoAncestors = community === threadInfo; | |||||
const { uiName } = useResolvedThreadInfo(threadInfo); | const { uiName } = useResolvedThreadInfo(threadInfo); | ||||
const currentThread = React.useMemo(() => { | |||||
const path = React.useMemo(() => { | |||||
if (threadHasNoAncestors) { | if (threadHasNoAncestors) { | ||||
return null; | return null; | ||||
} | } | ||||
const ancestors = ancestorThreads.map(ancestor => ( | |||||
<ThreadAncestor threadInfo={ancestor} key={ancestor.id} /> | |||||
)); | |||||
const chatNameClasses = classnames(css.ancestorName, css.chatName); | |||||
return ( | return ( | ||||
<> | <div className={css.ancestorThreadsContainer}> | ||||
<SWMansionIcon | {ancestors} | ||||
className={css.ancestorSeparator} | <div className={chatNameClasses}>{uiName}</div> | ||||
icon="chevron-right" | |||||
size={12} | |||||
/> | |||||
<div style={threadColorStyle} className={css.ancestorName}> | |||||
{uiName} | |||||
</div> | </div> | ||||
</> | |||||
); | ); | ||||
}, [threadHasNoAncestors, threadColorStyle, uiName]); | }, [ancestorThreads, threadHasNoAncestors, uiName]); | ||||
let seeFullStructure = null; | return ( | ||||
if (SHOW_SEE_FULL_STRUCTURE) { | <div className={css.container}> | ||||
seeFullStructure = ( | <div className={css.communityName}>{resolvedCommunity.uiName}</div> | ||||
<button | {chevronRight} | ||||
style={fullStructureButtonColorStyle} | {path} | ||||
className={css.seeFullStructure} | </div> | ||||
> | |||||
See full structure | |||||
</button> | |||||
); | ); | ||||
} | } | ||||
function ThreadAncestor(props: ThreadAncestorsProps): React.Node { | |||||
const { uiName } = useResolvedThreadInfo(props.threadInfo); | |||||
const chevronClasses = classnames(css.ancestorSeparator, css.chevronRight); | |||||
return ( | return ( | ||||
<> | <> | ||||
<div className={css.ancestorThreadsContainer}> | <div className={css.ancestorName}>{uiName}</div> | ||||
{keyserverInfo} | <ChevronRight size={12} className={chevronClasses} /> | ||||
{middlePath} | |||||
{currentThread} | |||||
</div> | |||||
{seeFullStructure} | |||||
</> | </> | ||||
); | ); | ||||
} | } | ||||
export default ThreadAncestors; | export default ThreadAncestors; |