refactor: native feature management (#2350)

This commit is contained in:
Mo
2023-07-12 12:56:08 -05:00
committed by GitHub
parent 49f7581cd8
commit 078ef3772c
223 changed files with 3996 additions and 3438 deletions

View File

@@ -1,4 +1,6 @@
import { EditorFeatureDescription } from '../Feature/EditorFeatureDescription'
import { FindNativeFeature } from '../Feature/Features'
import { IframeComponentFeatureDescription } from '../Feature/IframeComponentFeatureDescription'
import { FeatureIdentifier } from './../Feature/FeatureIdentifier'
import { EditorIdentifier } from './EditorIdentifier'
@@ -15,13 +17,9 @@ export enum NoteType {
}
export function noteTypeForEditorIdentifier(identifier: EditorIdentifier): NoteType {
if (identifier === FeatureIdentifier.PlainEditor) {
return NoteType.Plain
} else if (identifier === FeatureIdentifier.SuperEditor) {
return NoteType.Super
}
const feature = FindNativeFeature(identifier as FeatureIdentifier)
const feature = FindNativeFeature<EditorFeatureDescription | IframeComponentFeatureDescription>(
identifier as FeatureIdentifier,
)
if (feature && feature.note_type) {
return feature.note_type
}

View File

@@ -0,0 +1,14 @@
import { EditorFeatureDescription } from './EditorFeatureDescription'
import { ThemeFeatureDescription } from './ThemeFeatureDescription'
import { ComponentFeatureDescription } from './ComponentFeatureDescription'
import { IframeComponentFeatureDescription } from './IframeComponentFeatureDescription'
import { ClientFeatureDescription } from './ClientFeatureDescription'
import { ServerFeatureDescription } from './ServerFeatureDescription'
export type AnyFeatureDescription =
| ComponentFeatureDescription
| EditorFeatureDescription
| ThemeFeatureDescription
| IframeComponentFeatureDescription
| ClientFeatureDescription
| ServerFeatureDescription

View File

@@ -0,0 +1,24 @@
import { PermissionName } from '../Permission/PermissionName'
import { FeatureIdentifier } from './FeatureIdentifier'
import { ComponentFlag } from '../Component/ComponentFlag'
import { RoleFields } from './RoleFields'
export type BaseFeatureDescription = RoleFields & {
deletion_warning?: string
deprecated?: boolean
deprecation_message?: string
description?: string
expires_at?: number
/** Whether the client controls availability of this feature (such as the dark theme) */
clientControlled?: boolean
flags?: ComponentFlag[]
identifier: FeatureIdentifier
marketing_url?: string
name: string
no_expire?: boolean
no_mobile?: boolean
thumbnail_url?: string
permission_name: PermissionName
}

View File

@@ -0,0 +1,11 @@
import { PermissionName } from '../Permission/PermissionName'
import { FeatureIdentifier } from './FeatureIdentifier'
import { RoleFields } from './RoleFields'
export type ClientFeatureDescription = RoleFields & {
identifier: FeatureIdentifier
permission_name: PermissionName
description: string
name: string
deprecated?: boolean
}

View File

@@ -0,0 +1,9 @@
import { ComponentArea } from '../Component/ComponentArea'
import { BaseFeatureDescription } from './BaseFeatureDescription'
export type ComponentFeatureDescription = BaseFeatureDescription & {
/** The relative path of the index.html file or the main css file if theme, within the component folder itself */
index_path: string
content_type: string
area: ComponentArea
}

View File

@@ -0,0 +1,10 @@
import { NoteType } from '../Component/NoteType'
import { BaseFeatureDescription } from './BaseFeatureDescription'
export type EditorFeatureDescription = BaseFeatureDescription & {
file_type: 'txt' | 'html' | 'md' | 'json'
/** Whether an editor is interchangable with another editor that has the same file_type */
interchangeable: boolean
note_type: NoteType
spellcheckControl: boolean
}

View File

@@ -1,81 +0,0 @@
import { ComponentPermission } from '../Component/ComponentPermission'
import { ComponentArea } from '../Component/ComponentArea'
import { PermissionName } from '../Permission/PermissionName'
import { FeatureIdentifier } from './FeatureIdentifier'
import { ComponentFlag } from '../Component/ComponentFlag'
import { NoteType } from '../Component/NoteType'
import { ThemeDockIcon } from '../Component/ThemeDockIcon'
type RoleFields = {
/** Server populated */
role_name?: string
/** Statically populated. Non-influencing; used as a reference by other static consumers (such as email service) */
availableInRoles: string[]
}
export type BaseFeatureDescription = RoleFields & {
deletion_warning?: string
deprecated?: boolean
deprecation_message?: string
description?: string
expires_at?: number
/** Whether the client controls availability of this feature (such as the dark theme) */
clientControlled?: boolean
flags?: ComponentFlag[]
identifier: FeatureIdentifier
marketing_url?: string
name?: string
no_expire?: boolean
no_mobile?: boolean
thumbnail_url?: string
permission_name: PermissionName
}
export type ServerFeatureDescription = RoleFields & {
name?: string
identifier: FeatureIdentifier
permission_name: PermissionName
}
export type ClientFeatureDescription = RoleFields & {
identifier: FeatureIdentifier
permission_name: PermissionName
description: string
name: string
}
export type ComponentFeatureDescription = BaseFeatureDescription & {
/** The relative path of the index.html file or the main css file if theme, within the component folder itself */
index_path: string
content_type: string
area: ComponentArea
}
export type ThirdPartyFeatureDescription = ComponentFeatureDescription & {
url: string
}
export type IframeComponentFeatureDescription = ComponentFeatureDescription & {
component_permissions: ComponentPermission[]
}
export type EditorFeatureDescription = IframeComponentFeatureDescription & {
file_type: 'txt' | 'html' | 'md' | 'json'
/** Whether an editor is interchangable with another editor that has the same file_type */
interchangeable: boolean
note_type: NoteType
spellcheckControl?: boolean
}
export type ThemeFeatureDescription = ComponentFeatureDescription & {
/** Some themes can be layered on top of other themes */
layerable?: boolean
dock_icon?: ThemeDockIcon
isDark?: boolean
}
export type FeatureDescription = BaseFeatureDescription &
Partial<ComponentFeatureDescription & EditorFeatureDescription & ThemeFeatureDescription>

View File

@@ -1,14 +1,51 @@
import { FeatureDescription } from './FeatureDescription'
import { AnyFeatureDescription } from './AnyFeatureDescription'
import { ThemeFeatureDescription } from './ThemeFeatureDescription'
import { EditorFeatureDescription } from './EditorFeatureDescription'
import { FeatureIdentifier } from './FeatureIdentifier'
import { serverFeatures } from '../Lists/ServerFeatures'
import { clientFeatures } from '../Lists/ClientFeatures'
import { GetDeprecatedFeatures } from '../Lists/DeprecatedFeatures'
import { experimentalFeatures } from '../Lists/ExperimentalFeatures'
import { IframeEditors } from '../Lists/IframeEditors'
import { themes } from '../Lists/Themes'
import { nativeEditors } from '../Lists/NativeEditors'
export function GetFeatures(): FeatureDescription[] {
return [...serverFeatures(), ...clientFeatures(), ...experimentalFeatures(), ...GetDeprecatedFeatures()]
export function GetFeatures(): AnyFeatureDescription[] {
return [
...serverFeatures(),
...clientFeatures(),
...themes(),
...nativeEditors(),
...IframeEditors(),
...experimentalFeatures(),
...GetDeprecatedFeatures(),
]
}
export function FindNativeFeature(identifier: FeatureIdentifier): FeatureDescription | undefined {
return GetFeatures().find((f) => f.identifier === identifier)
export function FindNativeFeature<T extends AnyFeatureDescription>(identifier: FeatureIdentifier): T | undefined {
return GetFeatures().find((f) => f.identifier === identifier) as T
}
export function FindNativeTheme(identifier: FeatureIdentifier): ThemeFeatureDescription | undefined {
return themes().find((t) => t.identifier === identifier)
}
export function GetIframeAndNativeEditors(): EditorFeatureDescription[] {
return [...IframeEditors(), ...nativeEditors()]
}
export function GetSuperNoteFeature(): EditorFeatureDescription {
return FindNativeFeature(FeatureIdentifier.SuperEditor) as EditorFeatureDescription
}
export function GetPlainNoteFeature(): EditorFeatureDescription {
return FindNativeFeature(FeatureIdentifier.PlainEditor) as EditorFeatureDescription
}
export function GetNativeThemes(): ThemeFeatureDescription[] {
return themes()
}
export function GetDarkThemeFeature(): ThemeFeatureDescription {
return themes().find((t) => t.identifier === FeatureIdentifier.DarkTheme) as ThemeFeatureDescription
}

View File

@@ -0,0 +1,7 @@
import { ComponentPermission } from '../Component/ComponentPermission'
import { ComponentFeatureDescription } from './ComponentFeatureDescription'
import { EditorFeatureDescription } from './EditorFeatureDescription'
export type IframeComponentFeatureDescription = (EditorFeatureDescription & ComponentFeatureDescription) & {
component_permissions: ComponentPermission[]
}

View File

@@ -0,0 +1,7 @@
export type RoleFields = {
/** Server populated */
role_name?: string
/** Statically populated. Non-influencing; used as a reference by other static consumers (such as email service) */
availableInRoles: string[]
}

View File

@@ -0,0 +1,11 @@
import { PermissionName } from '../Permission/PermissionName'
import { FeatureIdentifier } from './FeatureIdentifier'
import { RoleFields } from './RoleFields'
export type ServerFeatureDescription = RoleFields & {
name: string
description?: string
identifier: FeatureIdentifier
permission_name: PermissionName
deprecated?: boolean
}

View File

@@ -0,0 +1,9 @@
import { ThemeDockIcon } from '../Component/ThemeDockIcon'
import { ComponentFeatureDescription } from './ComponentFeatureDescription'
export type ThemeFeatureDescription = ComponentFeatureDescription & {
/** Some themes can be layered on top of other themes */
layerable?: boolean
dock_icon?: ThemeDockIcon
isDark?: boolean
}

View File

@@ -0,0 +1,5 @@
import { ComponentFeatureDescription } from './ComponentFeatureDescription'
export type ThirdPartyFeatureDescription = ComponentFeatureDescription & {
url: string
}

View File

@@ -0,0 +1,28 @@
import { ContentType } from '@standardnotes/domain-core'
import { AnyFeatureDescription } from './AnyFeatureDescription'
import { ThemeFeatureDescription } from './ThemeFeatureDescription'
import { EditorFeatureDescription } from './EditorFeatureDescription'
import { IframeComponentFeatureDescription } from './IframeComponentFeatureDescription'
import { ComponentFeatureDescription } from './ComponentFeatureDescription'
import { ComponentArea } from '../Component/ComponentArea'
export function isThemeFeatureDescription(feature: AnyFeatureDescription): feature is ThemeFeatureDescription {
return 'content_type' in feature && feature.content_type === ContentType.TYPES.Theme
}
export function isIframeComponentFeatureDescription(
feature: AnyFeatureDescription,
): feature is IframeComponentFeatureDescription {
return (
'content_type' in feature &&
feature.content_type === ContentType.TYPES.Component &&
[ComponentArea.Editor, ComponentArea.EditorStack].includes(feature.area)
)
}
export function isEditorFeatureDescription(feature: AnyFeatureDescription): feature is EditorFeatureDescription {
return (
(feature as EditorFeatureDescription).note_type != undefined ||
(feature as ComponentFeatureDescription).area === ComponentArea.Editor
)
}

View File

@@ -0,0 +1,10 @@
import { ComponentFeatureDescription } from './ComponentFeatureDescription'
import { EditorFeatureDescription } from './EditorFeatureDescription'
import { IframeComponentFeatureDescription } from './IframeComponentFeatureDescription'
import { ThemeFeatureDescription } from './ThemeFeatureDescription'
export type UIFeatureDescriptionTypes =
| IframeComponentFeatureDescription
| ThemeFeatureDescription
| EditorFeatureDescription
| ComponentFeatureDescription

View File

@@ -1,14 +1,10 @@
import { FeatureDescription } from '../Feature/FeatureDescription'
import { PermissionName } from '../Permission/PermissionName'
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
import { RoleName } from '@standardnotes/domain-core'
import { themes } from './Themes'
import { editors } from './Editors'
import { ClientFeatureDescription } from '../Feature/ClientFeatureDescription'
export function clientFeatures(): FeatureDescription[] {
export function clientFeatures(): ClientFeatureDescription[] {
return [
...themes(),
...editors(),
{
name: 'Tag Nesting',
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
@@ -16,14 +12,7 @@ export function clientFeatures(): FeatureDescription[] {
permission_name: PermissionName.TagNesting,
description: 'Organize your tags into folders.',
},
{
name: 'Super Notes',
identifier: FeatureIdentifier.SuperEditor,
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
permission_name: PermissionName.SuperEditor,
description:
'A new way to edit notes. Type / to bring up the block selection menu, or @ to embed images or link other tags and notes. Type - then space to start a list, or [] then space to start a checklist. Drag and drop an image or file to embed it in your note. Cmd/Ctrl + F to bring up search and replace.',
},
{
name: 'Smart Filters',
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],

View File

@@ -1,18 +1,16 @@
import { AnyFeatureDescription } from '../Feature/AnyFeatureDescription'
import { EditorFeatureDescription } from '../Feature/EditorFeatureDescription'
import { IframeComponentFeatureDescription } from '../Feature/IframeComponentFeatureDescription'
import { ContentType, RoleName } from '@standardnotes/domain-core'
import {
EditorFeatureDescription,
IframeComponentFeatureDescription,
FeatureDescription,
} from '../Feature/FeatureDescription'
import { PermissionName } from '../Permission/PermissionName'
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
import { NoteType } from '../Component/NoteType'
import { FillEditorComponentDefaults } from './Utilities/FillEditorComponentDefaults'
import { FillIframeEditorDefaults } from './Utilities/FillEditorComponentDefaults'
import { ComponentAction } from '../Component/ComponentAction'
import { ComponentArea } from '../Component/ComponentArea'
export function GetDeprecatedFeatures(): FeatureDescription[] {
const bold: EditorFeatureDescription = FillEditorComponentDefaults({
export function GetDeprecatedFeatures(): AnyFeatureDescription[] {
const bold: EditorFeatureDescription = FillIframeEditorDefaults({
name: 'Alternative Rich Text',
identifier: FeatureIdentifier.DeprecatedBoldEditor,
note_type: NoteType.RichText,
@@ -39,7 +37,7 @@ export function GetDeprecatedFeatures(): FeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
})
const markdownBasic: EditorFeatureDescription = FillEditorComponentDefaults({
const markdownBasic: EditorFeatureDescription = FillIframeEditorDefaults({
name: 'Basic Markdown',
identifier: FeatureIdentifier.DeprecatedMarkdownBasicEditor,
note_type: NoteType.Markdown,
@@ -52,7 +50,7 @@ export function GetDeprecatedFeatures(): FeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
})
const markdownAlt: EditorFeatureDescription = FillEditorComponentDefaults({
const markdownAlt: EditorFeatureDescription = FillIframeEditorDefaults({
name: 'Markdown Alternative',
identifier: FeatureIdentifier.DeprecatedMarkdownVisualEditor,
note_type: NoteType.Markdown,
@@ -66,7 +64,7 @@ export function GetDeprecatedFeatures(): FeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
})
const markdownMinimist: EditorFeatureDescription = FillEditorComponentDefaults({
const markdownMinimist: EditorFeatureDescription = FillIframeEditorDefaults({
name: 'Minimal Markdown',
identifier: FeatureIdentifier.DeprecatedMarkdownMinimistEditor,
note_type: NoteType.Markdown,
@@ -80,7 +78,7 @@ export function GetDeprecatedFeatures(): FeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
})
const markdownMath: EditorFeatureDescription = FillEditorComponentDefaults({
const markdownMath: EditorFeatureDescription = FillIframeEditorDefaults({
name: 'Markdown with Math',
identifier: FeatureIdentifier.DeprecatedMarkdownMathEditor,
spellcheckControl: true,
@@ -94,7 +92,7 @@ export function GetDeprecatedFeatures(): FeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
})
const filesafe: IframeComponentFeatureDescription = FillEditorComponentDefaults({
const filesafe: IframeComponentFeatureDescription = FillIframeEditorDefaults({
name: 'FileSafe',
identifier: FeatureIdentifier.DeprecatedFileSafe,
component_permissions: [

View File

@@ -1,5 +1,5 @@
import { FeatureDescription } from '../Feature/FeatureDescription'
import { AnyFeatureDescription } from '../Feature/AnyFeatureDescription'
export function experimentalFeatures(): FeatureDescription[] {
export function experimentalFeatures(): AnyFeatureDescription[] {
return []
}

View File

@@ -1,12 +1,12 @@
import { EditorFeatureDescription } from '../Feature/FeatureDescription'
import { PermissionName } from '../Permission/PermissionName'
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
import { NoteType } from '../Component/NoteType'
import { FillEditorComponentDefaults } from './Utilities/FillEditorComponentDefaults'
import { FillIframeEditorDefaults } from './Utilities/FillEditorComponentDefaults'
import { RoleName } from '@standardnotes/domain-core'
import { IframeComponentFeatureDescription } from '../Feature/IframeComponentFeatureDescription'
export function editors(): EditorFeatureDescription[] {
const code: EditorFeatureDescription = FillEditorComponentDefaults({
export function IframeEditors(): IframeComponentFeatureDescription[] {
const code = FillIframeEditorDefaults({
name: 'Code',
spellcheckControl: true,
identifier: FeatureIdentifier.CodeEditor,
@@ -22,7 +22,7 @@ export function editors(): EditorFeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
})
const plus: EditorFeatureDescription = FillEditorComponentDefaults({
const plus = FillIframeEditorDefaults({
name: 'Rich Text',
note_type: NoteType.RichText,
file_type: 'html',
@@ -35,7 +35,7 @@ export function editors(): EditorFeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
})
const markdown: EditorFeatureDescription = FillEditorComponentDefaults({
const markdown = FillIframeEditorDefaults({
name: 'Markdown',
identifier: FeatureIdentifier.MarkdownProEditor,
note_type: NoteType.Markdown,
@@ -48,7 +48,7 @@ export function editors(): EditorFeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
})
const task: EditorFeatureDescription = FillEditorComponentDefaults({
const task = FillIframeEditorDefaults({
name: 'Checklist',
identifier: FeatureIdentifier.TaskEditor,
note_type: NoteType.Task,
@@ -62,7 +62,7 @@ export function editors(): EditorFeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
})
const tokenvault: EditorFeatureDescription = FillEditorComponentDefaults({
const tokenvault = FillIframeEditorDefaults({
name: 'Authenticator',
note_type: NoteType.Authentication,
file_type: 'json',
@@ -75,7 +75,7 @@ export function editors(): EditorFeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
})
const spreadsheets: EditorFeatureDescription = FillEditorComponentDefaults({
const spreadsheets = FillIframeEditorDefaults({
name: 'Spreadsheet',
identifier: FeatureIdentifier.SheetsEditor,
note_type: NoteType.Spreadsheet,

View File

@@ -0,0 +1,32 @@
import { RoleName } from '@standardnotes/domain-core'
import { NoteType } from '../Component/NoteType'
import { EditorFeatureDescription } from '../Feature/EditorFeatureDescription'
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
import { PermissionName } from '../Permission/PermissionName'
export function nativeEditors(): EditorFeatureDescription[] {
return [
{
name: 'Super',
note_type: NoteType.Super,
identifier: FeatureIdentifier.SuperEditor,
spellcheckControl: true,
file_type: 'json',
interchangeable: false,
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
permission_name: PermissionName.SuperEditor,
description:
'The best way to edit notes. Type / to bring up the block selection menu, or @ to embed images or link other tags and notes. Type - then space to start a list, or [] then space to start a checklist. Drag and drop an image or file to embed it in your note. Cmd/Ctrl + F to bring up search and replace.',
},
{
name: 'Plain Text',
note_type: NoteType.Plain,
spellcheckControl: true,
file_type: 'txt',
interchangeable: true,
identifier: FeatureIdentifier.PlainEditor,
availableInRoles: [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
permission_name: PermissionName.PlainEditor,
},
]
}

View File

@@ -1,4 +1,4 @@
import { ServerFeatureDescription } from '../Feature/FeatureDescription'
import { ServerFeatureDescription } from '../Feature/ServerFeatureDescription'
import { PermissionName } from '../Permission/PermissionName'
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
import { RoleName } from '@standardnotes/domain-core'
@@ -42,16 +42,19 @@ export function serverFeatures(): ServerFeatureDescription[] {
availableInRoles: [RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
},
{
name: 'Files maximum storage tier',
identifier: FeatureIdentifier.FilesMaximumStorageTier,
permission_name: PermissionName.FilesMaximumStorageTier,
availableInRoles: [RoleName.NAMES.ProUser],
},
{
name: 'Files low storage tier',
identifier: FeatureIdentifier.FilesLowStorageTier,
permission_name: PermissionName.FilesLowStorageTier,
availableInRoles: [RoleName.NAMES.PlusUser],
},
{
name: 'Files medium storage tier',
identifier: FeatureIdentifier.SubscriptionSharing,
permission_name: PermissionName.SubscriptionSharing,
availableInRoles: [RoleName.NAMES.ProUser],

View File

@@ -1,4 +1,4 @@
import { ThemeFeatureDescription } from '../Feature/FeatureDescription'
import { ThemeFeatureDescription } from '../Feature/ThemeFeatureDescription'
import { PermissionName } from '../Permission/PermissionName'
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
import { FillThemeComponentDefaults } from './Utilities/FillThemeComponentDefaults'

View File

@@ -1,14 +1,15 @@
import { ContentType } from '@standardnotes/domain-core'
import { ComponentAction } from '../../Component/ComponentAction'
import { EditorFeatureDescription } from '../../Feature/FeatureDescription'
import { EditorFeatureDescription } from '../../Feature/EditorFeatureDescription'
import { IframeComponentFeatureDescription } from '../../Feature/IframeComponentFeatureDescription'
import { ComponentArea } from '../../Component/ComponentArea'
export type RequiredEditorFields = Pick<EditorFeatureDescription, 'availableInRoles'>
export function FillEditorComponentDefaults(
component: Partial<EditorFeatureDescription> & RequiredEditorFields,
): EditorFeatureDescription {
export function FillIframeEditorDefaults(
component: Partial<IframeComponentFeatureDescription> & RequiredEditorFields,
): IframeComponentFeatureDescription {
if (!component.index_path) {
component.index_path = 'dist/index.html'
}
@@ -31,5 +32,5 @@ export function FillEditorComponentDefaults(
component.interchangeable = true
}
return component as EditorFeatureDescription
return component as IframeComponentFeatureDescription
}

View File

@@ -1,6 +1,5 @@
import { ContentType } from '@standardnotes/domain-core'
import { ThemeFeatureDescription } from '../../Feature/FeatureDescription'
import { ThemeFeatureDescription } from '../../Feature/ThemeFeatureDescription'
import { ComponentArea } from '../../Component/ComponentArea'
type RequiredThemeFields = Pick<ThemeFeatureDescription, 'availableInRoles'>

View File

@@ -22,6 +22,7 @@ export enum PermissionName {
NoteHistory30Days = 'server:note-history-30-days',
NoteHistory365Days = 'server:note-history-365-days',
NoteHistoryUnlimited = 'server:note-history-unlimited',
PlainEditor = 'editor:plain',
PlusEditor = 'editor:plus',
SheetsEditor = 'editor:sheets',
SignInAlerts = 'server:sign-in-alerts',

View File

@@ -1,6 +1,17 @@
export * from './Feature/FeatureDescription'
export * from './Feature/AnyFeatureDescription'
export * from './Feature/FeatureIdentifier'
export * from './Feature/Features'
export * from './Feature/TypeGuards'
export * from './Feature/ThirdPartyFeatureDescription'
export * from './Feature/ClientFeatureDescription'
export * from './Feature/ServerFeatureDescription'
export * from './Feature/IframeComponentFeatureDescription'
export * from './Feature/ComponentFeatureDescription'
export * from './Feature/BaseFeatureDescription'
export * from './Feature/EditorFeatureDescription'
export * from './Feature/ThemeFeatureDescription'
export * from './Feature/UIFeatureDescription'
export * from './Permission/Permission'
export * from './Permission/PermissionName'