refactor: de-couple linking controller from active item (#2108)
This commit is contained in:
@@ -27,6 +27,7 @@ import ApplicationProvider from '../ApplicationProvider'
|
||||
import CommandProvider from '../CommandProvider'
|
||||
import PanesSystemComponent from '../Panes/PanesSystemComponent'
|
||||
import DotOrgNotice from './DotOrgNotice'
|
||||
import LinkingControllerProvider from '@/Controllers/LinkingControllerProvider'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -177,62 +178,61 @@ const ApplicationView: FunctionComponent<Props> = ({ application, mainApplicatio
|
||||
application={application}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
>
|
||||
<div className={platformString + ' main-ui-view sn-component h-full'}>
|
||||
<FileDragNDropProvider
|
||||
application={application}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
filesController={viewControllerManager.filesController}
|
||||
>
|
||||
<PanesSystemComponent />
|
||||
</FileDragNDropProvider>
|
||||
|
||||
<>
|
||||
<Footer application={application} applicationGroup={mainApplicationGroup} />
|
||||
<SessionsModal application={application} viewControllerManager={viewControllerManager} />
|
||||
<PreferencesViewWrapper viewControllerManager={viewControllerManager} application={application} />
|
||||
<RevisionHistoryModal
|
||||
<LinkingControllerProvider controller={viewControllerManager.linkingController}>
|
||||
<div className={platformString + ' main-ui-view sn-component h-full'}>
|
||||
<FileDragNDropProvider
|
||||
application={application}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
subscriptionController={viewControllerManager.subscriptionController}
|
||||
/>
|
||||
</>
|
||||
|
||||
{renderChallenges()}
|
||||
|
||||
<>
|
||||
<NotesContextMenu
|
||||
application={application}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
/>
|
||||
<TagContextMenuWrapper
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
/>
|
||||
<FileContextMenuWrapper
|
||||
filesController={viewControllerManager.filesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
/>
|
||||
<PurchaseFlowWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<ConfirmSignoutContainer
|
||||
applicationGroup={mainApplicationGroup}
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
/>
|
||||
<ToastContainer />
|
||||
<FilePreviewModalWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<PermissionsModalWrapper application={application} />
|
||||
<ConfirmDeleteAccountContainer
|
||||
application={application}
|
||||
viewControllerManager={viewControllerManager}
|
||||
/>
|
||||
</>
|
||||
{application.routeService.isDotOrg && <DotOrgNotice />}
|
||||
</div>
|
||||
>
|
||||
<PanesSystemComponent />
|
||||
</FileDragNDropProvider>
|
||||
<>
|
||||
<Footer application={application} applicationGroup={mainApplicationGroup} />
|
||||
<SessionsModal application={application} viewControllerManager={viewControllerManager} />
|
||||
<PreferencesViewWrapper viewControllerManager={viewControllerManager} application={application} />
|
||||
<RevisionHistoryModal
|
||||
application={application}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
subscriptionController={viewControllerManager.subscriptionController}
|
||||
/>
|
||||
</>
|
||||
{renderChallenges()}
|
||||
<>
|
||||
<NotesContextMenu
|
||||
application={application}
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
notesController={viewControllerManager.notesController}
|
||||
linkingController={viewControllerManager.linkingController}
|
||||
historyModalController={viewControllerManager.historyModalController}
|
||||
/>
|
||||
<TagContextMenuWrapper
|
||||
navigationController={viewControllerManager.navigationController}
|
||||
featuresController={viewControllerManager.featuresController}
|
||||
/>
|
||||
<FileContextMenuWrapper
|
||||
filesController={viewControllerManager.filesController}
|
||||
selectionController={viewControllerManager.selectionController}
|
||||
/>
|
||||
<PurchaseFlowWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<ConfirmSignoutContainer
|
||||
applicationGroup={mainApplicationGroup}
|
||||
viewControllerManager={viewControllerManager}
|
||||
application={application}
|
||||
/>
|
||||
<ToastContainer />
|
||||
<FilePreviewModalWrapper application={application} viewControllerManager={viewControllerManager} />
|
||||
<PermissionsModalWrapper application={application} />
|
||||
<ConfirmDeleteAccountContainer
|
||||
application={application}
|
||||
viewControllerManager={viewControllerManager}
|
||||
/>
|
||||
</>
|
||||
{application.routeService.isDotOrg && <DotOrgNotice />}
|
||||
</div>
|
||||
</LinkingControllerProvider>
|
||||
</PremiumModalProvider>
|
||||
</ResponsivePaneProvider>
|
||||
</AndroidBackHandlerProvider>
|
||||
|
||||
@@ -20,6 +20,7 @@ import { ElementIds } from '@/Constants/ElementIDs'
|
||||
import Menu from '../Menu/Menu'
|
||||
import { getLinkingSearchResults } from '@/Utils/Items/Search/getSearchResults'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { DecryptedItem } from '@standardnotes/snjs'
|
||||
|
||||
type Props = {
|
||||
linkingController: LinkingController
|
||||
@@ -27,6 +28,7 @@ type Props = {
|
||||
focusedId: string | undefined
|
||||
setFocusedId: (id: string) => void
|
||||
hoverLabel?: string
|
||||
item: DecryptedItem
|
||||
}
|
||||
|
||||
const ItemLinkAutocompleteInput = ({
|
||||
@@ -35,12 +37,16 @@ const ItemLinkAutocompleteInput = ({
|
||||
focusedId,
|
||||
setFocusedId,
|
||||
hoverLabel,
|
||||
item,
|
||||
}: Props) => {
|
||||
const application = useApplication()
|
||||
const { tags, linkItemToSelectedItem, createAndAddNewTag, isEntitledToNoteLinking, activeItem } = linkingController
|
||||
|
||||
const { getLinkedTagsForItem, linkItems, createAndAddNewTag, isEntitledToNoteLinking } = linkingController
|
||||
|
||||
const tagsLinkedToItem = getLinkedTagsForItem(item) || []
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const { unlinkedItems, shouldShowCreateTag } = getLinkingSearchResults(searchQuery, application, activeItem)
|
||||
const { unlinkedItems, shouldShowCreateTag } = getLinkingSearchResults(searchQuery, application, item)
|
||||
|
||||
const [dropdownVisible, setDropdownVisible] = useState(false)
|
||||
const [dropdownMaxHeight, setDropdownMaxHeight] = useState<number | 'auto'>('auto')
|
||||
@@ -122,7 +128,7 @@ const ItemLinkAutocompleteInput = ({
|
||||
<input
|
||||
ref={inputRef}
|
||||
className={classNames(
|
||||
`${tags.length > 0 ? 'w-80' : 'mr-10 w-70'}`,
|
||||
`${tagsLinkedToItem.length > 0 ? 'w-80' : 'mr-10 w-70'}`,
|
||||
'bg-transparent text-sm text-text focus:border-b-2 focus:border-solid focus:border-info lg:text-xs',
|
||||
'no-border h-7 focus:shadow-none focus:outline-none',
|
||||
)}
|
||||
@@ -141,7 +147,7 @@ const ItemLinkAutocompleteInput = ({
|
||||
{areSearchResultsVisible && (
|
||||
<DisclosurePanel
|
||||
className={classNames(
|
||||
tags.length > 0 ? 'w-80' : 'mr-10 w-70',
|
||||
tagsLinkedToItem.length > 0 ? 'w-80' : 'mr-10 w-70',
|
||||
'absolute z-dropdown-menu flex flex-col overflow-y-auto rounded bg-default py-2 shadow-main',
|
||||
)}
|
||||
style={{
|
||||
@@ -159,7 +165,8 @@ const ItemLinkAutocompleteInput = ({
|
||||
>
|
||||
<LinkedItemSearchResults
|
||||
createAndAddNewTag={createAndAddNewTag}
|
||||
linkItemToSelectedItem={linkItemToSelectedItem}
|
||||
linkItems={linkItems}
|
||||
item={item}
|
||||
results={unlinkedItems}
|
||||
searchQuery={searchQuery}
|
||||
shouldShowCreateTag={shouldShowCreateTag}
|
||||
|
||||
@@ -11,23 +11,30 @@ import { LinkableItem } from '@/Utils/Items/Search/LinkableItem'
|
||||
import { ItemLink } from '@/Utils/Items/Search/ItemLink'
|
||||
import { FOCUS_TAGS_INPUT_COMMAND, keyboardStringForShortcut } from '@standardnotes/ui-services'
|
||||
import { useCommandService } from '../CommandProvider'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { useItemLinks } from '@/Hooks/useItemLinks'
|
||||
|
||||
type Props = {
|
||||
linkingController: LinkingController
|
||||
}
|
||||
|
||||
const LinkedItemBubblesContainer = ({ linkingController }: Props) => {
|
||||
const application = useApplication()
|
||||
const activeItem = application.itemControllerGroup.activeItemViewController?.item
|
||||
|
||||
const { toggleAppPane } = useResponsiveAppPane()
|
||||
|
||||
const commandService = useCommandService()
|
||||
|
||||
const {
|
||||
allItemLinks,
|
||||
notesLinkingToActiveItem,
|
||||
filesLinkingToActiveItem,
|
||||
unlinkItemFromSelectedItem: unlinkItem,
|
||||
activateItem,
|
||||
} = linkingController
|
||||
const { unlinkItemFromSelectedItem: unlinkItem, activateItem } = linkingController
|
||||
|
||||
const { notesLinkedToItem, filesLinkedToItem, tagsLinkedToItem, notesLinkingToItem, filesLinkingToItem } =
|
||||
useItemLinks(activeItem)
|
||||
|
||||
const allItemsLinkedToItem: ItemLink[] = useMemo(
|
||||
() => new Array<ItemLink>().concat(notesLinkedToItem, filesLinkedToItem, tagsLinkedToItem),
|
||||
[filesLinkedToItem, notesLinkedToItem, tagsLinkedToItem],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
return commandService.addCommandHandler({
|
||||
@@ -47,11 +54,11 @@ const LinkedItemBubblesContainer = ({ linkingController }: Props) => {
|
||||
)
|
||||
|
||||
const [focusedId, setFocusedId] = useState<string>()
|
||||
const focusableIds = allItemLinks
|
||||
const focusableIds = allItemsLinkedToItem
|
||||
.map((link) => link.id)
|
||||
.concat(
|
||||
notesLinkingToActiveItem.map((link) => link.id),
|
||||
filesLinkingToActiveItem.map((link) => link.id),
|
||||
notesLinkingToItem.map((link) => link.id),
|
||||
filesLinkingToItem.map((link) => link.id),
|
||||
[ElementIds.ItemLinkAutocompleteInput],
|
||||
)
|
||||
|
||||
@@ -84,9 +91,9 @@ const LinkedItemBubblesContainer = ({ linkingController }: Props) => {
|
||||
)
|
||||
|
||||
const isItemBidirectionallyLinked = (link: ItemLink) => {
|
||||
const existsInAllItemLinks = !!allItemLinks.find((item) => link.item.uuid === item.item.uuid)
|
||||
const existsInNotesLinkingToItem = !!notesLinkingToActiveItem.find((item) => link.item.uuid === item.item.uuid)
|
||||
const existsInFilesLinkingToItem = !!filesLinkingToActiveItem.find((item) => link.item.uuid === item.item.uuid)
|
||||
const existsInAllItemLinks = !!allItemsLinkedToItem.find((item) => link.item.uuid === item.item.uuid)
|
||||
const existsInNotesLinkingToItem = !!notesLinkingToItem.find((item) => link.item.uuid === item.item.uuid)
|
||||
const existsInFilesLinkingToItem = !!filesLinkingToItem.find((item) => link.item.uuid === item.item.uuid)
|
||||
|
||||
return (
|
||||
existsInAllItemLinks &&
|
||||
@@ -94,16 +101,20 @@ const LinkedItemBubblesContainer = ({ linkingController }: Props) => {
|
||||
)
|
||||
}
|
||||
|
||||
if (!activeItem) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'note-view-linking-container hidden min-w-80 max-w-full flex-wrap items-center gap-2 bg-transparent md:-mr-2 md:flex',
|
||||
allItemLinks.length || notesLinkingToActiveItem.length ? 'mt-1' : 'mt-0.5',
|
||||
allItemsLinkedToItem.length || notesLinkingToItem.length ? 'mt-1' : 'mt-0.5',
|
||||
)}
|
||||
>
|
||||
{allItemLinks
|
||||
.concat(notesLinkingToActiveItem)
|
||||
.concat(filesLinkingToActiveItem)
|
||||
{allItemsLinkedToItem
|
||||
.concat(notesLinkingToItem)
|
||||
.concat(filesLinkingToItem)
|
||||
.map((link) => (
|
||||
<LinkedItemBubble
|
||||
link={link}
|
||||
@@ -123,6 +134,7 @@ const LinkedItemBubblesContainer = ({ linkingController }: Props) => {
|
||||
focusPreviousItem={focusPreviousItem}
|
||||
setFocusedId={setFocusedId}
|
||||
hoverLabel={`Focus input to add a link (${shortcut})`}
|
||||
item={activeItem}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -10,22 +10,24 @@ import { useCallback } from 'react'
|
||||
|
||||
type Props = {
|
||||
createAndAddNewTag: LinkingController['createAndAddNewTag']
|
||||
linkItemToSelectedItem: LinkingController['linkItemToSelectedItem']
|
||||
linkItems: LinkingController['linkItems']
|
||||
results: LinkableItem[]
|
||||
searchQuery: string
|
||||
shouldShowCreateTag: boolean
|
||||
onClickCallback?: () => void
|
||||
isEntitledToNoteLinking: boolean
|
||||
item: LinkableItem
|
||||
}
|
||||
|
||||
const LinkedItemSearchResults = ({
|
||||
createAndAddNewTag,
|
||||
linkItemToSelectedItem,
|
||||
linkItems,
|
||||
results,
|
||||
searchQuery,
|
||||
shouldShowCreateTag,
|
||||
onClickCallback,
|
||||
isEntitledToNoteLinking,
|
||||
item,
|
||||
}: Props) => {
|
||||
const onClickAddNew = useCallback(
|
||||
(searchQuery: string) => {
|
||||
@@ -44,7 +46,7 @@ const LinkedItemSearchResults = ({
|
||||
key={result.uuid}
|
||||
className="flex w-full items-center justify-between gap-4 overflow-hidden py-2 px-3 hover:bg-contrast hover:text-foreground focus:bg-info-backdrop"
|
||||
onClick={() => {
|
||||
void linkItemToSelectedItem(result)
|
||||
void linkItems(item, result)
|
||||
onClickCallback?.()
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { FilesController } from '@/Controllers/FilesController'
|
||||
import { LinkingController } from '@/Controllers/LinkingController'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useRef, useCallback } from 'react'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import RoundIconButton from '../Button/RoundIconButton'
|
||||
import Popover from '../Popover/Popover'
|
||||
import StyledTooltip from '../StyledTooltip/StyledTooltip'
|
||||
@@ -16,6 +17,9 @@ type Props = {
|
||||
}
|
||||
|
||||
const LinkedItemsButton = ({ linkingController, filesController, onClickPreprocessing, featuresController }: Props) => {
|
||||
const application = useApplication()
|
||||
const activeItem = application.itemControllerGroup.activeItemViewController?.item
|
||||
|
||||
const { isLinkingPanelOpen, setIsLinkingPanelOpen } = linkingController
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
@@ -27,6 +31,10 @@ const LinkedItemsButton = ({ linkingController, filesController, onClickPreproce
|
||||
setIsLinkingPanelOpen(willMenuOpen)
|
||||
}, [isLinkingPanelOpen, onClickPreprocessing, setIsLinkingPanelOpen])
|
||||
|
||||
if (!activeItem) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTooltip label="Linked items panel">
|
||||
@@ -34,6 +42,7 @@ const LinkedItemsButton = ({ linkingController, filesController, onClickPreproce
|
||||
</StyledTooltip>
|
||||
<Popover togglePopover={toggleMenu} anchorElement={buttonRef.current} open={isLinkingPanelOpen} className="pb-2">
|
||||
<LinkedItemsPanel
|
||||
item={activeItem}
|
||||
isOpen={isLinkingPanelOpen}
|
||||
linkingController={linkingController}
|
||||
filesController={filesController}
|
||||
|
||||
@@ -12,44 +12,45 @@ import Icon from '../Icon/Icon'
|
||||
import DecoratedInput from '../Input/DecoratedInput'
|
||||
import LinkedItemSearchResults from './LinkedItemSearchResults'
|
||||
import { LinkedItemsSectionItem } from './LinkedItemsSectionItem'
|
||||
import { DecryptedItem } from '@standardnotes/snjs'
|
||||
|
||||
const LinkedItemsPanel = ({
|
||||
linkingController,
|
||||
filesController,
|
||||
featuresController,
|
||||
isOpen,
|
||||
item,
|
||||
}: {
|
||||
linkingController: LinkingController
|
||||
filesController: FilesController
|
||||
featuresController: FeaturesController
|
||||
isOpen: boolean
|
||||
item: DecryptedItem
|
||||
}) => {
|
||||
const {
|
||||
tags,
|
||||
linkedFiles,
|
||||
filesLinkingToActiveItem,
|
||||
notesLinkedToItem,
|
||||
notesLinkingToActiveItem,
|
||||
allItemLinks: allLinkedItems,
|
||||
linkItemToSelectedItem,
|
||||
getLinkedTagsForItem,
|
||||
getFilesLinksForItem,
|
||||
getLinkedNotesForItem,
|
||||
getNotesLinkingToItem,
|
||||
linkItems,
|
||||
unlinkItemFromSelectedItem,
|
||||
activateItem,
|
||||
createAndAddNewTag,
|
||||
isEntitledToNoteLinking,
|
||||
activeItem,
|
||||
} = linkingController
|
||||
|
||||
const tagsLinkedToItem = getLinkedTagsForItem(item) || []
|
||||
const { filesLinkedToItem, filesLinkingToItem } = getFilesLinksForItem(item)
|
||||
const notesLinkedToItem = getLinkedNotesForItem(item) || []
|
||||
const notesLinkingToItem = getNotesLinkingToItem(item) || []
|
||||
|
||||
const { entitledToFiles } = featuresController
|
||||
const application = useApplication()
|
||||
|
||||
const searchInputRef = useRef<HTMLInputElement | null>(null)
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const isSearching = !!searchQuery.length
|
||||
const { linkedResults, unlinkedItems, shouldShowCreateTag } = getLinkingSearchResults(
|
||||
searchQuery,
|
||||
application,
|
||||
activeItem,
|
||||
)
|
||||
const { linkedResults, unlinkedItems, shouldShowCreateTag } = getLinkingSearchResults(searchQuery, application, item)
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && searchInputRef.current) {
|
||||
@@ -64,7 +65,7 @@ const LinkedItemsPanel = ({
|
||||
}
|
||||
|
||||
void filesController.selectAndUploadNewFiles((file) => {
|
||||
void linkItemToSelectedItem(file)
|
||||
void linkItems(item, file)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -73,7 +74,7 @@ const LinkedItemsPanel = ({
|
||||
<form
|
||||
className={classNames(
|
||||
'sticky top-0 z-10 bg-default px-2.5 pt-2.5',
|
||||
allLinkedItems.length || linkedResults.length || unlinkedItems.length || notesLinkingToActiveItem.length
|
||||
linkedResults.length || unlinkedItems.length || notesLinkingToItem.length
|
||||
? 'border-b border-border pb-2.5'
|
||||
: 'pb-1',
|
||||
)}
|
||||
@@ -105,7 +106,7 @@ const LinkedItemsPanel = ({
|
||||
<div className="mt-3 mb-1 px-3 text-menu-item font-semibold uppercase text-passive-0">Unlinked</div>
|
||||
<LinkedItemSearchResults
|
||||
createAndAddNewTag={createAndAddNewTag}
|
||||
linkItemToSelectedItem={linkItemToSelectedItem}
|
||||
linkItems={linkItems}
|
||||
results={unlinkedItems}
|
||||
searchQuery={searchQuery}
|
||||
shouldShowCreateTag={shouldShowCreateTag}
|
||||
@@ -114,6 +115,7 @@ const LinkedItemsPanel = ({
|
||||
setSearchQuery('')
|
||||
searchInputRef.current?.focus()
|
||||
}}
|
||||
item={item}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -137,11 +139,11 @@ const LinkedItemsPanel = ({
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{!!tags.length && (
|
||||
{!!tagsLinkedToItem.length && (
|
||||
<div>
|
||||
<div className="mt-3 mb-1 px-3 text-menu-item font-semibold uppercase text-passive-0">Linked Tags</div>
|
||||
<div className="my-1">
|
||||
{tags.map((link) => (
|
||||
{tagsLinkedToItem.map((link) => (
|
||||
<LinkedItemsSectionItem
|
||||
key={link.id}
|
||||
item={link.item}
|
||||
@@ -165,7 +167,7 @@ const LinkedItemsPanel = ({
|
||||
<Icon type="add" />
|
||||
Upload and link file(s)
|
||||
</button>
|
||||
{linkedFiles.map((link) => (
|
||||
{filesLinkedToItem.map((link) => (
|
||||
<LinkedItemsSectionItem
|
||||
key={link.id}
|
||||
item={link.item}
|
||||
@@ -178,13 +180,13 @@ const LinkedItemsPanel = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!!filesLinkingToActiveItem.length && (
|
||||
{!!filesLinkingToItem.length && (
|
||||
<div>
|
||||
<div className="mt-3 mb-1 px-3 text-menu-item font-semibold uppercase text-passive-0">
|
||||
Files Linking To Current File
|
||||
</div>
|
||||
<div className="my-1">
|
||||
{filesLinkingToActiveItem.map((link) => (
|
||||
{filesLinkingToItem.map((link) => (
|
||||
<LinkedItemsSectionItem
|
||||
key={link.id}
|
||||
item={link.item}
|
||||
@@ -214,13 +216,13 @@ const LinkedItemsPanel = ({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!!notesLinkingToActiveItem.length && (
|
||||
{!!notesLinkingToItem.length && (
|
||||
<div>
|
||||
<div className="mt-3 mb-1 px-3 text-menu-item font-semibold uppercase text-passive-0">
|
||||
Notes Linking To This Note
|
||||
</div>
|
||||
<div className="my-1">
|
||||
{notesLinkingToActiveItem.map((link) => (
|
||||
{notesLinkingToItem.map((link) => (
|
||||
<LinkedItemsSectionItem
|
||||
key={link.id}
|
||||
item={link.item}
|
||||
|
||||
Reference in New Issue
Block a user