Skip to content

Commit

Permalink
refactor: note editor relationships (#1821)
Browse files Browse the repository at this point in the history
  • Loading branch information
moughxyz committed Oct 18, 2022
1 parent c83dc48 commit 2b66ff8
Show file tree
Hide file tree
Showing 28 changed files with 357 additions and 299 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"css-loader": "^6.7.1",
"eslint": "^8.17.0",
"eslint-plugin-prettier": "^4.2.1",
"husky": "^8.0.0",
"husky": "^8.0.1",
"lint-staged": "^13.0.1",
"npm-check-updates": "^14.1.1",
"prettier": "^2.6.2",
Expand Down
1 change: 1 addition & 0 deletions packages/features/src/Domain/Component/NoteType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export enum NoteType {
RichText = 'rich-text',
Spreadsheet = 'spreadsheet',
Task = 'task',
Plain = 'plain-text',
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export enum AppDataField {
NotAvailableOnMobile = 'notAvailableOnMobile',
MobileActive = 'mobileActive',
LastSize = 'lastSize',
PrefersPlainEditor = 'prefersPlainEditor',
LegacyPrefersPlainEditor = 'prefersPlainEditor',
ComponentInstallError = 'installError',
}
39 changes: 39 additions & 0 deletions packages/models/src/Domain/Syncable/Component/Component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FillItemContent } from '../../Abstract/Content/ItemContent'
import { SNComponent } from './Component'
import { ComponentContent } from './ComponentContent'
import { PayloadTimestampDefaults } from '../../Abstract/Payload'
import { NoteType } from '@standardnotes/features'

describe('component model', () => {
it('valid hosted url should ignore url', () => {
Expand Down Expand Up @@ -46,4 +47,42 @@ describe('component model', () => {
expect(component.hasValidHostedUrl()).toBe(true)
expect(component.hosted_url).toBe('http://foo.com')
})

it('should return noteType as specified in package_info', () => {
const component = new SNComponent(
new DecryptedPayload(
{
uuid: String(Math.random()),
content_type: ContentType.Component,
content: FillItemContent({
package_info: {
note_type: NoteType.Authentication,
},
} as ComponentContent),
...PayloadTimestampDefaults(),
},
PayloadSource.Constructor,
),
)

expect(component.noteType).toEqual(NoteType.Authentication)
})

it('should return plain as noteType if no note type defined in package_info', () => {
const component = new SNComponent(
new DecryptedPayload(
{
uuid: String(Math.random()),
content_type: ContentType.Component,
content: FillItemContent({
package_info: {},
} as ComponentContent),
...PayloadTimestampDefaults(),
},
PayloadSource.Constructor,
),
)

expect(component.noteType).toEqual(NoteType.Plain)
})
})
5 changes: 5 additions & 0 deletions packages/models/src/Domain/Syncable/Component/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ComponentFlag,
ComponentPermission,
FindNativeFeature,
NoteType,
} from '@standardnotes/features'
import { AppDataField } from '../../Abstract/Item/Types/AppDataField'
import { ComponentContent, ComponentInterface } from './ComponentContent'
Expand Down Expand Up @@ -177,6 +178,10 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
return this.package_info as ThirdPartyFeatureDescription
}

public get noteType(): NoteType {
return this.package_info.note_type || NoteType.Plain
}

public get isDeprecated(): boolean {
let flags: string[] = this.package_info.flags ?? []
flags = flags.map((flag: string) => flag.toLowerCase())
Expand Down
11 changes: 3 additions & 8 deletions packages/models/src/Domain/Syncable/Note/Note.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,9 @@ describe('SNNote Tests', () => {
expect(note.preview_html).toBeFalsy()
})

it('should set mobilePrefersPlainEditor when given a valid choice', () => {
const selected = createNote({
mobilePrefersPlainEditor: true,
})

const unselected = createNote()
it('should not set default value for note type if none is provided', () => {
const note = createNote({})

expect(selected.mobilePrefersPlainEditor).toBeTruthy()
expect(unselected.mobilePrefersPlainEditor).toBe(undefined)
expect(note.noteType).toBe(undefined)
})
})
20 changes: 14 additions & 6 deletions packages/models/src/Domain/Syncable/Note/Note.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { AppDataField } from './../../Abstract/Item/Types/AppDataField'
import { ContentType } from '@standardnotes/common'
import { FeatureIdentifier, NoteType } from '@standardnotes/features'
import { DecryptedItem } from '../../Abstract/Item/Implementations/DecryptedItem'
import { ItemInterface } from '../../Abstract/Item/Interfaces/ItemInterface'
import { AppDataField } from '../../Abstract/Item/Types/AppDataField'
import { DecryptedPayloadInterface } from '../../Abstract/Payload/Interfaces/DecryptedPayload'
import { NoteContent, NoteContentSpecialized } from './NoteContent'

Expand All @@ -10,12 +11,14 @@ export const isNote = (x: ItemInterface): x is SNNote => x.content_type === Cont
export class SNNote extends DecryptedItem<NoteContent> implements NoteContentSpecialized {
public readonly title: string
public readonly text: string
public readonly mobilePrefersPlainEditor?: boolean
public readonly hidePreview: boolean = false
public readonly preview_plain: string
public readonly preview_html: string
public readonly prefersPlainEditor: boolean
public readonly spellcheck?: boolean
public readonly noteType?: NoteType

/** The package_info.identifier of the editor (not its uuid), such as org.standardnotes.advanced-markdown */
public readonly editorIdentifier?: FeatureIdentifier | string

constructor(payload: DecryptedPayloadInterface<NoteContent>) {
super(payload)
Expand All @@ -26,9 +29,14 @@ export class SNNote extends DecryptedItem<NoteContent> implements NoteContentSpe
this.preview_html = String(this.payload.content.preview_html || '')
this.hidePreview = Boolean(this.payload.content.hidePreview)
this.spellcheck = this.payload.content.spellcheck
this.noteType = this.payload.content.noteType
this.editorIdentifier = this.payload.content.editorIdentifier

this.prefersPlainEditor = this.getAppDomainValueWithDefault(AppDataField.PrefersPlainEditor, false)

this.mobilePrefersPlainEditor = this.payload.content.mobilePrefersPlainEditor
if (!this.noteType) {
const prefersPlain = this.getAppDomainValueWithDefault(AppDataField.LegacyPrefersPlainEditor, false)
if (prefersPlain) {
this.noteType = NoteType.Plain
}
}
}
}
4 changes: 3 additions & 1 deletion packages/models/src/Domain/Syncable/Note/NoteContent.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { FeatureIdentifier, NoteType } from '@standardnotes/features'
import { ItemContent } from '../../Abstract/Content/ItemContent'

export interface NoteContentSpecialized {
title: string
text: string
mobilePrefersPlainEditor?: boolean
hidePreview?: boolean
preview_plain?: string
preview_html?: string
spellcheck?: boolean
noteType?: NoteType
editorIdentifier?: FeatureIdentifier | string
}

export type NoteContent = NoteContentSpecialized & ItemContent
24 changes: 24 additions & 0 deletions packages/models/src/Domain/Syncable/Note/NoteMutator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { NoteMutator } from './NoteMutator'
import { createNote } from './../../Utilities/Test/SpecUtils'
import { MutationType } from '../../Abstract/Item'
import { FeatureIdentifier, NoteType } from '@standardnotes/features'

describe('note mutator', () => {
it('sets noteType', () => {
const note = createNote({})
const mutator = new NoteMutator(note, MutationType.NoUpdateUserTimestamps)
mutator.noteType = NoteType.Authentication
const result = mutator.getResult()

expect(result.content.noteType).toEqual(NoteType.Authentication)
})

it('sets editorIdentifier', () => {
const note = createNote({})
const mutator = new NoteMutator(note, MutationType.NoUpdateUserTimestamps)
mutator.editorIdentifier = FeatureIdentifier.MarkdownProEditor
const result = mutator.getResult()

expect(result.content.editorIdentifier).toEqual(FeatureIdentifier.MarkdownProEditor)
})
})
14 changes: 9 additions & 5 deletions packages/models/src/Domain/Syncable/Note/NoteMutator.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AppDataField } from '../../Abstract/Item/Types/AppDataField'
import { NoteContent } from './NoteContent'
import { DecryptedItemMutator } from '../../Abstract/Item/Mutator/DecryptedItemMutator'
import { SNNote } from './Note'
import { NoteToNoteReference } from '../../Abstract/Reference/NoteToNoteReference'
import { ContentType } from '@standardnotes/common'
import { ContentReferenceType } from '../../Abstract/Item'
import { FeatureIdentifier, NoteType } from '@standardnotes/features'

export class NoteMutator extends DecryptedItemMutator<NoteContent> {
set title(title: string) {
Expand All @@ -27,14 +27,18 @@ export class NoteMutator extends DecryptedItemMutator<NoteContent> {
this.mutableContent.preview_html = preview_html
}

set prefersPlainEditor(prefersPlainEditor: boolean) {
this.setAppDataItem(AppDataField.PrefersPlainEditor, prefersPlainEditor)
}

set spellcheck(spellcheck: boolean) {
this.mutableContent.spellcheck = spellcheck
}

set noteType(noteType: NoteType) {
this.mutableContent.noteType = noteType
}

set editorIdentifier(identifier: FeatureIdentifier | string | undefined) {
this.mutableContent.editorIdentifier = identifier
}

toggleSpellcheck(): void {
if (this.mutableContent.spellcheck == undefined) {
this.mutableContent.spellcheck = false
Expand Down
55 changes: 0 additions & 55 deletions packages/models/src/Domain/Utilities/Payload/AffectorFunction.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { PayloadSource } from './../../Abstract/Payload/Types/PayloadSource'
import { extendArray, UuidGenerator } from '@standardnotes/utils'
import { ImmutablePayloadCollection } from '../../Runtime/Collection/Payload/ImmutablePayloadCollection'
import { ItemContent } from '../../Abstract/Content/ItemContent'
import { AffectorMapping } from './AffectorFunction'
import { PayloadsByUpdatingReferencingPayloadReferences } from './PayloadsByUpdatingReferencingPayloadReferences'
import { isDecryptedPayload } from '../../Abstract/Payload/Interfaces/TypeCheck'
import { FullyFormedPayloadInterface } from '../../Abstract/Payload/Interfaces/UnionTypes'
Expand Down Expand Up @@ -69,13 +68,5 @@ export function PayloadsByDuplicating<C extends ItemContent = ItemContent>(dto:
extendArray(results, updatedReferencing)
}

const affector = AffectorMapping[payload.content_type]
if (affector) {
const affected = affector(payload, copy, baseCollection)
if (affected) {
extendArray(results, affected)
}
}

return results
}
1 change: 0 additions & 1 deletion packages/models/src/Domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ export * from './Utilities/Item/FindItem'
export * from './Utilities/Item/ItemContentsDiffer'
export * from './Utilities/Item/ItemContentsEqual'
export * from './Utilities/Item/ItemGenerator'
export * from './Utilities/Payload/AffectorFunction'
export * from './Utilities/Payload/CopyPayloadWithContentOverride'
export * from './Utilities/Payload/CreatePayload'
export * from './Utilities/Payload/FindPayload'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ export interface ComponentManagerInterface {
urlOverride?: string,
): ComponentViewerInterface
presentPermissionsDialog(_dialog: PermissionDialog): void
getDefaultEditor(): SNComponent
getDefaultEditor(): SNComponent | undefined
}
33 changes: 33 additions & 0 deletions packages/snjs/lib/Client/NoteViewController.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { SNApplication } from './../Application/Application'
import { ContentType } from '@standardnotes/common'
import { MutatorService } from './../Services/Mutator/MutatorService'
import { SNComponentManager } from './../Services/ComponentManager/ComponentManager'
import { NoteType } from '@standardnotes/features'
import { NoteViewController } from './NoteViewController'

describe('note view controller', () => {
let application: SNApplication

beforeEach(() => {
application = {} as jest.Mocked<SNApplication>
application.streamItems = jest.fn()

const componentManager = {} as jest.Mocked<SNComponentManager>
componentManager.getDefaultEditor = jest.fn()
Object.defineProperty(application, 'componentManager', { value: componentManager })

const mutator = {} as jest.Mocked<MutatorService>
mutator.createTemplateItem = jest.fn()
Object.defineProperty(application, 'mutator', { value: mutator })
})

it('should create notes with plaintext note type', async () => {
const controller = new NoteViewController(application)
await controller.initialize(false)

expect(application.mutator.createTemplateItem).toHaveBeenCalledWith(
ContentType.Note,
expect.objectContaining({ noteType: NoteType.Plain }),
)
})
})
4 changes: 4 additions & 0 deletions packages/snjs/lib/Client/NoteViewController.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NoteType } from '@standardnotes/features'
import {
NoteMutator,
SNNote,
Expand Down Expand Up @@ -66,9 +67,12 @@ export class NoteViewController implements ItemViewControllerInterface {

async initialize(addTagHierarchy: boolean): Promise<void> {
if (!this.item) {
const editor = this.application.componentManager.getDefaultEditor()
const note = this.application.mutator.createTemplateItem<NoteContent, SNNote>(ContentType.Note, {
text: '',
title: this.defaultTitle || '',
noteType: editor?.noteType || NoteType.Plain,
editorIdentifier: editor?.identifier,
references: [],
})

Expand Down

0 comments on commit 2b66ff8

Please sign in to comment.