fix: issue with not being able to unlink a file from a note (#1836)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { PLAIN_EDITOR_NAME } from '@/Constants/Constants'
|
||||
import { sanitizeHtmlString, SNNote } from '@standardnotes/snjs'
|
||||
import { isFile, sanitizeHtmlString, SNNote } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useRef } from 'react'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
@@ -32,7 +32,7 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps> = ({
|
||||
const editorForNote = application.componentManager.editorForNote(item as SNNote)
|
||||
const editorName = editorForNote?.name ?? PLAIN_EDITOR_NAME
|
||||
const [icon, tint] = application.iconsController.getIconAndTintForNoteType(editorForNote?.package_info.note_type)
|
||||
const hasFiles = application.items.getSortedFilesLinkingToItem(item).length > 0
|
||||
const hasFiles = application.items.itemsReferencingItem(item).filter(isFile).length > 0
|
||||
|
||||
const openNoteContextMenu = (posX: number, posY: number) => {
|
||||
notesController.setContextMenuOpen(false)
|
||||
|
||||
@@ -103,7 +103,7 @@ const LinkedItemBubble = ({
|
||||
<span className="max-w-290px flex items-center overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{tagTitle && <span className="text-passive-1">{tagTitle.titlePrefix}</span>}
|
||||
<span className="flex items-center gap-1">
|
||||
{link.relationWithSelectedItem === 'indirect' && link.item.content_type !== ContentType.Tag && (
|
||||
{link.type === 'linked-by' && link.item.content_type !== ContentType.Tag && (
|
||||
<span className={!isBidirectional ? 'hidden group-focus:block' : ''}>Linked By:</span>
|
||||
)}
|
||||
{link.item.title}
|
||||
|
||||
@@ -14,7 +14,14 @@ import {
|
||||
ClassicFileSaver,
|
||||
parseFileName,
|
||||
} from '@standardnotes/filepicker'
|
||||
import { ChallengeReason, ClientDisplayableError, ContentType, FileItem, InternalEventBus } from '@standardnotes/snjs'
|
||||
import {
|
||||
ChallengeReason,
|
||||
ClientDisplayableError,
|
||||
ContentType,
|
||||
FileItem,
|
||||
InternalEventBus,
|
||||
isFile,
|
||||
} from '@standardnotes/snjs'
|
||||
import { addToast, dismissToast, ToastType, updateToast } from '@standardnotes/toast'
|
||||
import { action, makeObservable, observable, reaction } from 'mobx'
|
||||
import { WebApplication } from '../Application/Application'
|
||||
@@ -99,7 +106,7 @@ export class FilesController extends AbstractViewController {
|
||||
reloadAttachedFiles = () => {
|
||||
const note = this.notesController.firstSelectedNote
|
||||
if (note) {
|
||||
this.attachedFiles = this.application.items.getSortedFilesLinkingToItem(note)
|
||||
this.attachedFiles = this.application.items.itemsReferencingItem(note).filter(isFile)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,13 @@ import {
|
||||
IconType,
|
||||
InternalEventBus,
|
||||
ItemContent,
|
||||
ItemsClientInterface,
|
||||
naturalSort,
|
||||
NoteViewController,
|
||||
PrefKey,
|
||||
SNNote,
|
||||
SNTag,
|
||||
isFile,
|
||||
isNote,
|
||||
} from '@standardnotes/snjs'
|
||||
import { action, computed, makeObservable, observable } from 'mobx'
|
||||
import { AbstractViewController } from './Abstract/AbstractViewController'
|
||||
@@ -27,12 +28,10 @@ import { SubscriptionController } from './Subscription/SubscriptionController'
|
||||
|
||||
export type LinkableItem = DecryptedItemInterface<ItemContent>
|
||||
|
||||
type RelationWithSelectedItem = ReturnType<ItemsClientInterface['relationshipTypeForItems']>
|
||||
|
||||
export type ItemLink<ItemType extends LinkableItem = LinkableItem> = {
|
||||
id: string
|
||||
item: ItemType
|
||||
relationWithSelectedItem: RelationWithSelectedItem
|
||||
type: 'linked' | 'linked-by'
|
||||
}
|
||||
|
||||
export class LinkingController extends AbstractViewController {
|
||||
@@ -143,68 +142,68 @@ export class LinkingController extends AbstractViewController {
|
||||
this.reloadNotesLinkingToItem()
|
||||
}
|
||||
|
||||
createLinkFromItem = <ItemType extends LinkableItem = LinkableItem>(
|
||||
item: ItemType,
|
||||
relation?: RelationWithSelectedItem,
|
||||
): ItemLink<ItemType> => {
|
||||
const relationWithSelectedItem = relation ? relation : this.itemRelationshipWithSelectedItem(item)
|
||||
|
||||
createLinkFromItem = <I extends LinkableItem = LinkableItem>(itemA: I, type: 'linked' | 'linked-by'): ItemLink<I> => {
|
||||
return {
|
||||
id: `${item.uuid}-${relationWithSelectedItem}`,
|
||||
item,
|
||||
relationWithSelectedItem,
|
||||
id: `${itemA.uuid}-${type}`,
|
||||
item: itemA,
|
||||
type,
|
||||
}
|
||||
}
|
||||
|
||||
reloadLinkedFiles() {
|
||||
if (!this.activeItem) {
|
||||
if (!this.activeItem || this.application.items.isTemplateItem(this.activeItem)) {
|
||||
return
|
||||
}
|
||||
|
||||
const isActiveItemAFile = this.activeItem instanceof FileItem
|
||||
const referencesOfActiveItem = naturalSort(
|
||||
this.application.items.referencesForItem(this.activeItem).filter(isFile),
|
||||
'title',
|
||||
)
|
||||
|
||||
const linkedFiles = this.application.items
|
||||
.getSortedLinkedFilesForItem(this.activeItem)
|
||||
.map((item) => this.createLinkFromItem(item, isActiveItemAFile ? 'direct' : 'indirect'))
|
||||
const referencingActiveItem = naturalSort(
|
||||
this.application.items.itemsReferencingItem(this.activeItem).filter(isFile),
|
||||
'title',
|
||||
)
|
||||
|
||||
const filesLinkingToActiveItem = this.application.items
|
||||
.getSortedFilesLinkingToItem(this.activeItem)
|
||||
.map((item) => this.createLinkFromItem(item, isActiveItemAFile ? 'indirect' : 'direct'))
|
||||
|
||||
if (isActiveItemAFile) {
|
||||
this.linkedFiles = linkedFiles
|
||||
this.filesLinkingToActiveItem = filesLinkingToActiveItem
|
||||
if (this.activeItem.content_type === ContentType.File) {
|
||||
this.linkedFiles = referencesOfActiveItem.map((item) => this.createLinkFromItem(item, 'linked'))
|
||||
this.filesLinkingToActiveItem = referencingActiveItem.map((item) => this.createLinkFromItem(item, 'linked-by'))
|
||||
} else {
|
||||
this.linkedFiles = filesLinkingToActiveItem
|
||||
this.filesLinkingToActiveItem = linkedFiles
|
||||
this.linkedFiles = referencingActiveItem.map((item) => this.createLinkFromItem(item, 'linked'))
|
||||
this.filesLinkingToActiveItem = referencesOfActiveItem.map((item) => this.createLinkFromItem(item, 'linked-by'))
|
||||
}
|
||||
}
|
||||
|
||||
reloadLinkedTags() {
|
||||
if (this.activeItem) {
|
||||
const tags = this.application.items
|
||||
.getSortedTagsForItem(this.activeItem)
|
||||
.map((item) => this.createLinkFromItem(item))
|
||||
this.tags = tags
|
||||
if (!this.activeItem || this.application.items.isTemplateItem(this.activeItem)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.tags = this.application.items
|
||||
.getSortedTagsForItem(this.activeItem)
|
||||
.map((item) => this.createLinkFromItem(item, 'linked'))
|
||||
}
|
||||
|
||||
reloadLinkedNotes() {
|
||||
if (this.activeItem) {
|
||||
const notes = this.application.items
|
||||
.getSortedLinkedNotesForItem(this.activeItem)
|
||||
.map((item) => this.createLinkFromItem(item, 'direct'))
|
||||
this.notesLinkedToItem = notes
|
||||
if (!this.activeItem || this.application.items.isTemplateItem(this.activeItem)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.notesLinkedToItem = naturalSort(
|
||||
this.application.items.referencesForItem(this.activeItem).filter(isNote),
|
||||
'title',
|
||||
).map((item) => this.createLinkFromItem(item, 'linked'))
|
||||
}
|
||||
|
||||
reloadNotesLinkingToItem() {
|
||||
if (this.activeItem) {
|
||||
const notes = this.application.items
|
||||
.getSortedNotesLinkingToItem(this.activeItem)
|
||||
.map((item) => this.createLinkFromItem(item, 'indirect'))
|
||||
this.notesLinkingToActiveItem = notes
|
||||
if (!this.activeItem) {
|
||||
return
|
||||
}
|
||||
|
||||
this.notesLinkingToActiveItem = naturalSort(
|
||||
this.application.items.itemsReferencingItem(this.activeItem).filter(isNote),
|
||||
'title',
|
||||
).map((item) => this.createLinkFromItem(item, 'linked-by'))
|
||||
}
|
||||
|
||||
getTitleForLinkedTag = (item: LinkableItem) => {
|
||||
@@ -263,16 +262,6 @@ export class LinkingController extends AbstractViewController {
|
||||
return undefined
|
||||
}
|
||||
|
||||
itemRelationshipWithSelectedItem = (item: LinkableItem) => {
|
||||
const activeItem = this.activeItem
|
||||
|
||||
if (!activeItem) {
|
||||
throw new Error('No active item available')
|
||||
}
|
||||
|
||||
return this.application.items.relationshipTypeForItems(activeItem, item)
|
||||
}
|
||||
|
||||
unlinkItemFromSelectedItem = async (itemToUnlink: ItemLink) => {
|
||||
const selectedItem = this.selectionController.firstSelectedItem
|
||||
|
||||
@@ -280,13 +269,7 @@ export class LinkingController extends AbstractViewController {
|
||||
return
|
||||
}
|
||||
|
||||
const selectedItemReferencesItemToUnlink = itemToUnlink.relationWithSelectedItem === 'direct'
|
||||
|
||||
if (selectedItemReferencesItemToUnlink) {
|
||||
await this.application.items.unlinkItem(selectedItem, itemToUnlink.item)
|
||||
} else {
|
||||
await this.application.items.unlinkItem(itemToUnlink.item, selectedItem)
|
||||
}
|
||||
await this.application.items.unlinkItems(selectedItem, itemToUnlink.item)
|
||||
|
||||
void this.application.sync.sync()
|
||||
this.reloadAllLinks()
|
||||
@@ -404,9 +387,11 @@ export class LinkingController extends AbstractViewController {
|
||||
const linkedResults = searchResults
|
||||
.filter(isAlreadyLinked)
|
||||
.slice(0, 20)
|
||||
.map((item) => this.createLinkFromItem(item))
|
||||
.map((item) => this.createLinkFromItem(item, 'linked'))
|
||||
|
||||
const isResultExistingTag = (result: DecryptedItemInterface<ItemContent>) =>
|
||||
result.content_type === ContentType.Tag && result.title === searchQuery
|
||||
|
||||
const shouldShowCreateTag =
|
||||
!linkedResults.find((link) => isResultExistingTag(link.item)) && !unlinkedResults.find(isResultExistingTag)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user