Skip to content

Commit

Permalink
refactor: merge themes into components (#2388)
Browse files Browse the repository at this point in the history
  • Loading branch information
moughxyz authored Aug 6, 2023
1 parent 1d60af0 commit d2f8a36
Show file tree
Hide file tree
Showing 33 changed files with 117 additions and 150 deletions.
14 changes: 11 additions & 3 deletions packages/models/src/Domain/Runtime/Feature/UIFeature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import {
isThemeFeatureDescription,
} from '@standardnotes/features'
import { ComponentInterface } from '../../Syncable/Component/ComponentInterface'
import { isTheme } from '../../Syncable/Theme'
import { isItemBasedFeature, isNativeFeature } from './TypeGuards'
import { UIFeatureInterface } from './UIFeatureInterface'
import { Uuid } from '@standardnotes/domain-core'
import { ThemePackageInfo, isTheme } from '../../Syncable/Component'

export class UIFeature<F extends UIFeatureDescriptionTypes> implements UIFeatureInterface<F> {
constructor(public readonly item: ComponentInterface | F) {}
Expand All @@ -40,6 +40,14 @@ export class UIFeature<F extends UIFeatureDescriptionTypes> implements UIFeature
throw new Error('Cannot cast item to component')
}

get asTheme(): ComponentInterface<ThemePackageInfo> {
if (isItemBasedFeature(this.item)) {
return this.item
}

throw new Error('Cannot cast item to component')
}

get asFeatureDescription(): F {
if (isNativeFeature(this.item)) {
return this.item
Expand Down Expand Up @@ -145,7 +153,7 @@ export class UIFeature<F extends UIFeatureDescriptionTypes> implements UIFeature

get layerable(): boolean {
if (isItemBasedFeature(this.item) && isTheme(this.item)) {
return this.item.layerable
return this.item.layerableTheme
} else if (isThemeFeatureDescription(this.asFeatureDescription)) {
return this.asFeatureDescription.layerable ?? false
}
Expand All @@ -155,7 +163,7 @@ export class UIFeature<F extends UIFeatureDescriptionTypes> implements UIFeature

get dockIcon(): ThemeDockIcon | undefined {
if (isItemBasedFeature(this.item) && isTheme(this.item)) {
return this.item.package_info.dock_icon
return this.asTheme.package_info.dock_icon
} else if (isThemeFeatureDescription(this.asFeatureDescription)) {
return this.asFeatureDescription.dock_icon
}
Expand Down
10 changes: 5 additions & 5 deletions packages/models/src/Domain/Syncable/Component/Component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { PayloadSource } from './../../Abstract/Payload/Types/PayloadSource'
import { DecryptedPayload } from './../../Abstract/Payload/Implementations/DecryptedPayload'
import { ContentType } from '@standardnotes/domain-core'
import { FillItemContent } from '../../Abstract/Content/ItemContent'
import { SNComponent } from './Component'
import { ComponentItem } 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', () => {
const component = new SNComponent(
const component = new ComponentItem(
new DecryptedPayload(
{
uuid: String(Math.random()),
Expand All @@ -29,7 +29,7 @@ describe('component model', () => {
})

it('invalid hosted url should fallback to url', () => {
const component = new SNComponent(
const component = new ComponentItem(
new DecryptedPayload(
{
uuid: String(Math.random()),
Expand All @@ -49,7 +49,7 @@ describe('component model', () => {
})

it('should return noteType as specified in package_info', () => {
const component = new SNComponent(
const component = new ComponentItem(
new DecryptedPayload(
{
uuid: String(Math.random()),
Expand All @@ -69,7 +69,7 @@ describe('component model', () => {
})

it('should return unknown as noteType if no note type defined in package_info', () => {
const component = new SNComponent(
const component = new ComponentItem(
new DecryptedPayload(
{
uuid: String(Math.random()),
Expand Down
66 changes: 20 additions & 46 deletions packages/models/src/Domain/Syncable/Component/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,16 @@ import { DecryptedPayloadInterface } from '../../Abstract/Payload/Interfaces/Dec
import { HistoryEntryInterface } from '../../Runtime/History'
import { ItemContent } from '../../Abstract/Content/ItemContent'
import { Predicate } from '../../Runtime/Predicate/Predicate'
import { ItemInterface } from '../../Abstract/Item/Interfaces/ItemInterface'
import { DecryptedItemInterface } from './../../Abstract/Item/Interfaces/DecryptedItem'
import { ComponentPackageInfo } from './PackageInfo'
import { isDecryptedItem } from '../../Abstract/Item'
import { ComponentPackageInfo, ThemePackageInfo } from './PackageInfo'
import { ContentType } from '@standardnotes/domain-core'

export function isComponent(x: ItemInterface): x is ComponentInterface {
if (!isDecryptedItem(x as DecryptedItemInterface)) {
return false
}

return x.content_type === ContentType.TYPES.Component
}

export function isComponentOrTheme(x: ItemInterface): x is ComponentInterface {
if (!isDecryptedItem(x as DecryptedItemInterface)) {
return false
}

return x.content_type === ContentType.TYPES.Component || x.content_type === ContentType.TYPES.Theme
}

/**
* Components are mostly iframe based extensions that communicate with the SN parent
* via the postMessage API. However, a theme can also be a component, which is activated
* only by its url.
*/
export class SNComponent extends DecryptedItem<ComponentContent> implements ComponentInterface {
export class ComponentItem extends DecryptedItem<ComponentContent> implements ComponentInterface {
public readonly legacyComponentData: Record<string, unknown>
/** Items that have requested a component to be disabled in its context */
public readonly disassociatedItemIds: string[]
Expand All @@ -61,7 +43,6 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
public readonly valid_until: Date
public readonly legacyActive: boolean
public readonly legacy_url?: string
public readonly isMobileDefault: boolean

constructor(payload: DecryptedPayloadInterface<ComponentContent>) {
super(payload)
Expand All @@ -78,13 +59,18 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
this.valid_until = new Date(payload.content.valid_until || 0)
this.offlineOnly = payload.content.offlineOnly ?? false
this.name = payload.content.name
this.area = payload.content.area

if (this.content_type === ContentType.TYPES.Theme) {
this.area = ComponentArea.Themes
} else {
this.area = payload.content.area
}

this.package_info = payload.content.package_info || {}
this.permissions = payload.content.permissions || []
this.autoupdateDisabled = payload.content.autoupdateDisabled ?? false
this.disassociatedItemIds = payload.content.disassociatedItemIds || []
this.associatedItemIds = payload.content.associatedItemIds || []
this.isMobileDefault = payload.content.isMobileDefault ?? false

/**
* @legacy
Expand Down Expand Up @@ -116,15 +102,11 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
return FindNativeFeature(this.identifier)?.name || this.name
}

public override singletonPredicate(): Predicate<SNComponent> {
const uniqueIdentifierPredicate = new Predicate<SNComponent>('identifier', '=', this.identifier)
public override singletonPredicate(): Predicate<ComponentItem> {
const uniqueIdentifierPredicate = new Predicate<ComponentItem>('identifier', '=', this.identifier)
return uniqueIdentifierPredicate
}

public isEditor(): boolean {
return this.area === ComponentArea.Editor
}

public isTheme(): boolean {
return this.content_type === ContentType.TYPES.Theme || this.area === ComponentArea.Themes
}
Expand All @@ -134,10 +116,6 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
return this.getAppDomainValue(AppDataField.DefaultEditor) === true
}

public getLastSize(): unknown {
return this.getAppDomainValue(AppDataField.LastSize)
}

public hasValidHostedUrl(): boolean {
return (this.hosted_url || this.legacy_url) != undefined
}
Expand All @@ -149,19 +127,6 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
return [...componentKeys, ...superKeys] as (keyof ItemContent)[]
}

/**
* An associative component depends on being explicitly activated for a
* given item, compared to a dissaciative component, which is enabled by
* default in areas unrelated to a certain item.
*/
public static associativeAreas(): ComponentArea[] {
return [ComponentArea.Editor]
}

public isAssociative(): boolean {
return SNComponent.associativeAreas().includes(this.area)
}

public isExplicitlyEnabledForItem(uuid: string): boolean {
return this.associatedItemIds.indexOf(uuid) !== -1
}
Expand Down Expand Up @@ -199,4 +164,13 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
public get deprecationMessage(): string | undefined {
return this.package_info.deprecation_message
}

get layerableTheme(): boolean {
if (!this.isTheme()) {
return false
}

const themePackageInfo = this.package_info as ThemePackageInfo
return themePackageInfo?.layerable ?? false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export type ComponentContentSpecialized = {
valid_until: Date | number

legacy_url?: string
isMobileDefault?: boolean
isDeprecated?: boolean

/** @deprecated */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ComponentArea, ComponentPermission, NoteType, ThirdPartyFeatureDescription } from '@standardnotes/features'
import { ComponentPackageInfo } from './PackageInfo'
import { ComponentPackageInfo, ThemePackageInfo } from './PackageInfo'
import { DecryptedItemInterface } from '../../Abstract/Item'
import { ComponentContent } from './ComponentContent'

export interface ComponentInterface extends DecryptedItemInterface<ComponentContent> {
export interface ComponentInterface<P extends ComponentPackageInfo | ThemePackageInfo = ComponentPackageInfo>
extends DecryptedItemInterface<ComponentContent> {
/** Items that have requested a component to be disabled in its context */
disassociatedItemIds: string[]

Expand All @@ -16,16 +17,18 @@ export interface ComponentInterface extends DecryptedItemInterface<ComponentCont
offlineOnly: boolean
name: string
autoupdateDisabled: boolean
package_info: ComponentPackageInfo
package_info: P
area: ComponentArea
permissions: ComponentPermission[]
valid_until: Date
isMobileDefault: boolean
isDeprecated: boolean

isExplicitlyEnabledForItem(uuid: string): boolean
hasValidHostedUrl(): boolean

isTheme(): boolean
get layerableTheme(): boolean

isExplicitlyDisabledForItem(uuid: string): boolean
legacyIsDefaultEditor(): boolean

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { addIfUnique, removeFromArray } from '@standardnotes/utils'
import { ComponentFeatureDescription, ComponentPermission } from '@standardnotes/features'
import { AppDataField } from '../../Abstract/Item/Types/AppDataField'
import { ComponentContent } from './ComponentContent'
import { DecryptedItemMutator } from '../../Abstract/Item/Mutator/DecryptedItemMutator'

export class ComponentMutator extends DecryptedItemMutator<ComponentContent> {
set isMobileDefault(isMobileDefault: boolean) {
this.mutableContent.isMobileDefault = isMobileDefault
}

set package_info(package_info: ComponentFeatureDescription) {
this.mutableContent.package_info = package_info
}
Expand Down Expand Up @@ -56,8 +51,4 @@ export class ComponentMutator extends DecryptedItemMutator<ComponentContent> {
public removeDisassociatedItemId(uuid: string): void {
removeFromArray(this.mutableContent.disassociatedItemIds || [], uuid)
}

public setLastSize(size: string): void {
this.setAppDataItem(AppDataField.LastSize, size)
}
}
29 changes: 29 additions & 0 deletions packages/models/src/Domain/Syncable/Component/TypeGuards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ComponentInterface } from './ComponentInterface'
import { ItemInterface } from '../../Abstract/Item/Interfaces/ItemInterface'
import { DecryptedItemInterface } from '../../Abstract/Item/Interfaces/DecryptedItem'
import { isDecryptedItem } from '../../Abstract/Item'
import { ContentType } from '@standardnotes/domain-core'

export function isComponent(x: ItemInterface): x is ComponentInterface {
if (!isDecryptedItem(x as DecryptedItemInterface)) {
return false
}

return x.content_type === ContentType.TYPES.Component
}

export function isTheme(x: ItemInterface): x is ComponentInterface {
if (!isDecryptedItem(x as DecryptedItemInterface)) {
return false
}

return x.content_type === ContentType.TYPES.Theme
}

export function isComponentOrTheme(x: ItemInterface): x is ComponentInterface {
if (!isDecryptedItem(x as DecryptedItemInterface)) {
return false
}

return x.content_type === ContentType.TYPES.Component || x.content_type === ContentType.TYPES.Theme
}
2 changes: 1 addition & 1 deletion packages/models/src/Domain/Syncable/Component/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export * from './Component'
export * from './ComponentMutator'
export * from './ComponentContent'
export * from './ComponentInterface'
export * from '../../Runtime/Feature/UIFeature'
export * from './PackageInfo'
export * from './TypeGuards'
2 changes: 1 addition & 1 deletion packages/models/src/Domain/Syncable/Editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface EditorContent extends ItemContent {

/**
* @deprecated
* Editor objects are depracated in favor of SNComponent objects
* Editor objects are depracated in favor of ComponentItem objects
*/
export class SNEditor extends DecryptedItem<EditorContent> {
public readonly notes: SNNote[] = []
Expand Down
18 changes: 0 additions & 18 deletions packages/models/src/Domain/Syncable/Theme/Theme.ts

This file was deleted.

7 changes: 0 additions & 7 deletions packages/models/src/Domain/Syncable/Theme/ThemeInterface.ts

This file was deleted.

4 changes: 0 additions & 4 deletions packages/models/src/Domain/Syncable/Theme/ThemeMutator.ts

This file was deleted.

3 changes: 0 additions & 3 deletions packages/models/src/Domain/Syncable/Theme/index.ts

This file was deleted.

Loading

0 comments on commit d2f8a36

Please sign in to comment.