feat: item linking (#1779)

This commit is contained in:
Aman Harwara
2022-10-11 23:54:00 +05:30
committed by GitHub
parent d22c164e5d
commit e3f28421ff
68 changed files with 2064 additions and 1277 deletions

View File

@@ -1,10 +1,10 @@
import { NoteContent } from './../../../Syncable/Note/NoteContent'
import { NoteContent } from '../../../Syncable/Note/NoteContent'
import { ContentType } from '@standardnotes/common'
import { DecryptedItem, EncryptedItem } from '../../../Abstract/Item'
import { DecryptedPayload, EncryptedPayload, PayloadTimestampDefaults } from '../../../Abstract/Payload'
import { ItemCollection } from './ItemCollection'
import { FillItemContent } from '../../../Abstract/Content/ItemContent'
import { TagNotesIndex } from './TagNotesIndex'
import { TagItemsIndex } from './TagItemsIndex'
import { ItemDelta } from '../../Index/ItemDelta'
import { AnyItemInterface } from '../../../Abstract/Item/Interfaces/UnionTypes'
@@ -24,10 +24,10 @@ describe('tag notes index', () => {
return new EncryptedItem(payload)
}
const createDecryptedItem = (uuid?: string) => {
const createDecryptedItem = (uuid?: string, content_type = ContentType.Note) => {
const payload = new DecryptedPayload({
uuid: uuid || String(Math.random()),
content_type: ContentType.Note,
content_type,
content: FillItemContent<NoteContent>({
title: 'foo',
}),
@@ -46,20 +46,33 @@ describe('tag notes index', () => {
}
}
it('should count both notes and files', () => {
const collection = new ItemCollection()
const index = new TagItemsIndex(collection)
const decryptedNote = createDecryptedItem('note')
const decryptedFile = createDecryptedItem('file')
collection.set([decryptedNote, decryptedFile])
index.onChange(createChangeDelta(decryptedNote))
index.onChange(createChangeDelta(decryptedFile))
expect(index.allCountableItemsCount()).toEqual(2)
})
it('should decrement count after decrypted note becomes errored', () => {
const collection = new ItemCollection()
const index = new TagNotesIndex(collection)
const index = new TagItemsIndex(collection)
const decryptedItem = createDecryptedItem()
collection.set(decryptedItem)
index.onChange(createChangeDelta(decryptedItem))
expect(index.allCountableNotesCount()).toEqual(1)
expect(index.allCountableItemsCount()).toEqual(1)
const encryptedItem = createEncryptedItem(decryptedItem.uuid)
collection.set(encryptedItem)
index.onChange(createChangeDelta(encryptedItem))
expect(index.allCountableNotesCount()).toEqual(0)
expect(index.allCountableItemsCount()).toEqual(0)
})
})

View File

@@ -7,22 +7,22 @@ import { ItemDelta } from '../../Index/ItemDelta'
import { isDecryptedItem, ItemInterface } from '../../../Abstract/Item'
type AllNotesUuidSignifier = undefined
export type TagNoteCountChangeObserver = (tagUuid: Uuid | AllNotesUuidSignifier) => void
export type TagItemCountChangeObserver = (tagUuid: Uuid | AllNotesUuidSignifier) => void
export class TagNotesIndex implements SNIndex {
private tagToNotesMap: Partial<Record<Uuid, Set<Uuid>>> = {}
private allCountableNotes = new Set<Uuid>()
export class TagItemsIndex implements SNIndex {
private tagToItemsMap: Partial<Record<Uuid, Set<Uuid>>> = {}
private allCountableItems = new Set<Uuid>()
constructor(private collection: ItemCollection, public observers: TagNoteCountChangeObserver[] = []) {}
constructor(private collection: ItemCollection, public observers: TagItemCountChangeObserver[] = []) {}
private isNoteCountable = (note: ItemInterface) => {
if (isDecryptedItem(note)) {
return !note.archived && !note.trashed
private isItemCountable = (item: ItemInterface) => {
if (isDecryptedItem(item)) {
return !item.archived && !item.trashed
}
return false
}
public addCountChangeObserver(observer: TagNoteCountChangeObserver): () => void {
public addCountChangeObserver(observer: TagItemCountChangeObserver): () => void {
this.observers.push(observer)
const thislessEventObservers = this.observers
@@ -37,30 +37,32 @@ export class TagNotesIndex implements SNIndex {
}
}
public allCountableNotesCount(): number {
return this.allCountableNotes.size
public allCountableItemsCount(): number {
return this.allCountableItems.size
}
public countableNotesForTag(tag: SNTag): number {
return this.tagToNotesMap[tag.uuid]?.size || 0
public countableItemsForTag(tag: SNTag): number {
return this.tagToItemsMap[tag.uuid]?.size || 0
}
public onChange(delta: ItemDelta): void {
const notes = [...delta.changed, ...delta.inserted, ...delta.discarded].filter(
(i) => i.content_type === ContentType.Note,
const items = [...delta.changed, ...delta.inserted, ...delta.discarded].filter(
(i) => i.content_type === ContentType.Note || i.content_type === ContentType.File,
)
const tags = [...delta.changed, ...delta.inserted].filter(isDecryptedItem).filter(isTag)
this.receiveNoteChanges(notes)
this.receiveItemChanges(items)
this.receiveTagChanges(tags)
}
private receiveTagChanges(tags: SNTag[]): void {
for (const tag of tags) {
const uuids = tag.noteReferences.map((ref) => ref.uuid)
const countableUuids = uuids.filter((uuid) => this.allCountableNotes.has(uuid))
const previousSet = this.tagToNotesMap[tag.uuid]
this.tagToNotesMap[tag.uuid] = new Set(countableUuids)
const uuids = tag.references
.filter((ref) => ref.content_type === ContentType.Note || ref.content_type === ContentType.File)
.map((ref) => ref.uuid)
const countableUuids = uuids.filter((uuid) => this.allCountableItems.has(uuid))
const previousSet = this.tagToItemsMap[tag.uuid]
this.tagToItemsMap[tag.uuid] = new Set(countableUuids)
if (previousSet?.size !== countableUuids.length) {
this.notifyObservers(tag.uuid)
@@ -68,26 +70,26 @@ export class TagNotesIndex implements SNIndex {
}
}
private receiveNoteChanges(notes: ItemInterface[]): void {
const previousAllCount = this.allCountableNotes.size
private receiveItemChanges(items: ItemInterface[]): void {
const previousAllCount = this.allCountableItems.size
for (const note of notes) {
const isCountable = this.isNoteCountable(note)
for (const item of items) {
const isCountable = this.isItemCountable(item)
if (isCountable) {
this.allCountableNotes.add(note.uuid)
this.allCountableItems.add(item.uuid)
} else {
this.allCountableNotes.delete(note.uuid)
this.allCountableItems.delete(item.uuid)
}
const associatedTagUuids = this.collection.uuidsThatReferenceUuid(note.uuid)
const associatedTagUuids = this.collection.uuidsThatReferenceUuid(item.uuid)
for (const tagUuid of associatedTagUuids) {
const set = this.setForTag(tagUuid)
const previousCount = set.size
if (isCountable) {
set.add(note.uuid)
set.add(item.uuid)
} else {
set.delete(note.uuid)
set.delete(item.uuid)
}
if (previousCount !== set.size) {
this.notifyObservers(tagUuid)
@@ -95,16 +97,16 @@ export class TagNotesIndex implements SNIndex {
}
}
if (previousAllCount !== this.allCountableNotes.size) {
if (previousAllCount !== this.allCountableItems.size) {
this.notifyObservers(undefined)
}
}
private setForTag(uuid: Uuid): Set<Uuid> {
let set = this.tagToNotesMap[uuid]
let set = this.tagToItemsMap[uuid]
if (!set) {
set = new Set()
this.tagToNotesMap[uuid] = set
this.tagToItemsMap[uuid] = set
}
return set
}