feat: add features package
This commit is contained in:
25
packages/features/src/Domain/Component/ComponentAction.ts
Normal file
25
packages/features/src/Domain/Component/ComponentAction.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export enum ComponentAction {
|
||||
SetSize = 'set-size',
|
||||
StreamItems = 'stream-items',
|
||||
StreamContextItem = 'stream-context-item',
|
||||
SaveItems = 'save-items',
|
||||
SelectItem = 'select-item',
|
||||
AssociateItem = 'associate-item',
|
||||
DeassociateItem = 'deassociate-item',
|
||||
ClearSelection = 'clear-selection',
|
||||
CreateItem = 'create-item',
|
||||
CreateItems = 'create-items',
|
||||
DeleteItems = 'delete-items',
|
||||
SetComponentData = 'set-component-data',
|
||||
ToggleActivateComponent = 'toggle-activate-component',
|
||||
RequestPermissions = 'request-permissions',
|
||||
PresentConflictResolution = 'present-conflict-resolution',
|
||||
DuplicateItem = 'duplicate-item',
|
||||
ComponentRegistered = 'component-registered',
|
||||
ActivateThemes = 'themes',
|
||||
Reply = 'reply',
|
||||
ThemesActivated = 'themes-activated',
|
||||
KeyDown = 'key-down',
|
||||
KeyUp = 'key-up',
|
||||
Click = 'click',
|
||||
}
|
||||
5
packages/features/src/Domain/Component/ComponentArea.ts
Normal file
5
packages/features/src/Domain/Component/ComponentArea.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum ComponentArea {
|
||||
Editor = 'editor-editor',
|
||||
Themes = 'themes',
|
||||
EditorStack = 'editor-stack',
|
||||
}
|
||||
4
packages/features/src/Domain/Component/ComponentFlag.ts
Normal file
4
packages/features/src/Domain/Component/ComponentFlag.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum ComponentFlag {
|
||||
New = 'New',
|
||||
Deprecated = 'Deprecated',
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { ComponentAction } from './ComponentAction'
|
||||
|
||||
export type ComponentPermission = {
|
||||
name: ComponentAction
|
||||
content_types?: ContentType[]
|
||||
}
|
||||
8
packages/features/src/Domain/Component/NoteType.ts
Normal file
8
packages/features/src/Domain/Component/NoteType.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export enum NoteType {
|
||||
Authentication = 'authentication',
|
||||
Code = 'code',
|
||||
Markdown = 'markdown',
|
||||
RichText = 'rich-text',
|
||||
Spreadsheet = 'spreadsheet',
|
||||
Task = 'task',
|
||||
}
|
||||
7
packages/features/src/Domain/Component/ThemeDockIcon.ts
Normal file
7
packages/features/src/Domain/Component/ThemeDockIcon.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export type ThemeDockIcon = {
|
||||
type: 'svg' | 'circle'
|
||||
background_color: string
|
||||
foreground_color: string
|
||||
border_color: string
|
||||
source?: string
|
||||
}
|
||||
78
packages/features/src/Domain/Feature/FeatureDescription.ts
Normal file
78
packages/features/src/Domain/Feature/FeatureDescription.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { ComponentPermission } from '../Component/ComponentPermission'
|
||||
import { ContentType, RoleName, SubscriptionName } from '@standardnotes/common'
|
||||
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?: RoleName
|
||||
|
||||
/** Statically populated. Non-influencing; used as a reference by other static consumers (such as email service) */
|
||||
availableInSubscriptions: SubscriptionName[]
|
||||
}
|
||||
|
||||
export type BaseFeatureDescription = RoleFields & {
|
||||
deletion_warning?: string
|
||||
deprecated?: boolean
|
||||
deprecation_message?: string
|
||||
description?: string
|
||||
expires_at?: number
|
||||
|
||||
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: ContentType
|
||||
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
|
||||
}
|
||||
|
||||
export type FeatureDescription = BaseFeatureDescription &
|
||||
Partial<ComponentFeatureDescription & EditorFeatureDescription & ThemeFeatureDescription>
|
||||
53
packages/features/src/Domain/Feature/FeatureIdentifier.ts
Normal file
53
packages/features/src/Domain/Feature/FeatureIdentifier.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
export enum FeatureIdentifier {
|
||||
AccountSwitcher = 'com.standardnotes.account-switcher',
|
||||
CloudLink = 'org.standardnotes.cloudlink',
|
||||
DailyDropboxBackup = 'org.standardnotes.daily-dropbox-backup',
|
||||
DailyEmailBackup = 'org.standardnotes.daily-email-backup',
|
||||
DailyGDriveBackup = 'org.standardnotes.daily-gdrive-backup',
|
||||
DailyOneDriveBackup = 'org.standardnotes.daily-onedrive-backup',
|
||||
Files = 'org.standardnotes.files',
|
||||
FilesBeta = 'org.standardnotes.files-beta',
|
||||
FilesLowStorageTier = 'org.standardnotes.files-low-storage-tier',
|
||||
FilesMaximumStorageTier = 'org.standardnotes.files-max-storage-tier',
|
||||
ListedCustomDomain = 'org.standardnotes.listed-custom-domain',
|
||||
NoteHistory30Days = 'org.standardnotes.note-history-30',
|
||||
NoteHistory365Days = 'org.standardnotes.note-history-365',
|
||||
NoteHistoryUnlimited = 'org.standardnotes.note-history-unlimited',
|
||||
SignInAlerts = 'com.standardnotes.sign-in-alerts',
|
||||
SmartFilters = 'org.standardnotes.smart-filters',
|
||||
TagNesting = 'org.standardnotes.tag-nesting',
|
||||
TwoFactorAuth = 'org.standardnotes.two-factor-auth',
|
||||
|
||||
AutobiographyTheme = 'org.standardnotes.theme-autobiography',
|
||||
DynamicTheme = 'org.standardnotes.theme-dynamic',
|
||||
FocusedTheme = 'org.standardnotes.theme-focus',
|
||||
FocusMode = 'org.standardnotes.focus-mode',
|
||||
FuturaTheme = 'org.standardnotes.theme-futura',
|
||||
MidnightTheme = 'org.standardnotes.theme-midnight',
|
||||
SolarizedDarkTheme = 'org.standardnotes.theme-solarized-dark',
|
||||
TitaniumTheme = 'org.standardnotes.theme-titanium',
|
||||
|
||||
AdvancedChecklist = 'org.standardnotes.advanced-checklist',
|
||||
CodeEditor = 'org.standardnotes.code-editor',
|
||||
MarkdownProEditor = 'org.standardnotes.advanced-markdown-editor',
|
||||
MarkdownVisualEditor = 'org.standardnotes.markdown-visual-editor',
|
||||
PlainTextEditor = 'org.standardnotes.plain-text-editor',
|
||||
PlusEditor = 'org.standardnotes.plus-editor',
|
||||
SheetsEditor = 'org.standardnotes.standard-sheets',
|
||||
TaskEditor = 'org.standardnotes.simple-task-editor',
|
||||
TokenVaultEditor = 'org.standardnotes.token-vault',
|
||||
|
||||
DeprecatedBoldEditor = 'org.standardnotes.bold-editor',
|
||||
DeprecatedMarkdownBasicEditor = 'org.standardnotes.simple-markdown-editor',
|
||||
DeprecatedMarkdownMathEditor = 'org.standardnotes.fancy-markdown-editor',
|
||||
DeprecatedMarkdownMinimistEditor = 'org.standardnotes.minimal-markdown-editor',
|
||||
DeprecatedFoldersComponent = 'org.standardnotes.folders',
|
||||
DeprecatedFileSafe = 'org.standardnotes.file-safe',
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifier for standalone filesafe instance offered as legacy installable via extensions-server
|
||||
*/
|
||||
export const LegacyFileSafeIdentifier = 'org.standardnotes.legacy.file-safe'
|
||||
|
||||
export const ExperimentalFeatures = [FeatureIdentifier.AdvancedChecklist]
|
||||
19
packages/features/src/Domain/Feature/Features.spec.ts
Normal file
19
packages/features/src/Domain/Feature/Features.spec.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { SubscriptionName } from '@standardnotes/common'
|
||||
import { GetFeatures, GetFeaturesForSubscription } from './Features'
|
||||
|
||||
describe('features', () => {
|
||||
it('all features should have availableInSubscriptions populated', () => {
|
||||
const features = GetFeatures()
|
||||
|
||||
for (const feature of features) {
|
||||
expect(feature.availableInSubscriptions.length).toBeGreaterThan(0)
|
||||
}
|
||||
})
|
||||
it('gets features for plus plan', () => {
|
||||
const features = GetFeaturesForSubscription(SubscriptionName.PlusPlan)
|
||||
|
||||
for (const feature of features) {
|
||||
expect(feature.availableInSubscriptions.includes(SubscriptionName.PlusPlan))
|
||||
}
|
||||
})
|
||||
})
|
||||
28
packages/features/src/Domain/Feature/Features.ts
Normal file
28
packages/features/src/Domain/Feature/Features.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { FeatureDescription } from './FeatureDescription'
|
||||
import { FeatureIdentifier } from './FeatureIdentifier'
|
||||
import { editors } from '../Lists/Editors'
|
||||
import { themes } from '../Lists/Themes'
|
||||
import { serverFeatures } from '../Lists/ServerFeatures'
|
||||
import { clientFeatures } from '../Lists/ClientFeatures'
|
||||
import { GetDeprecatedFeatures } from '../Lists/DeprecatedFeatures'
|
||||
import { experimentalFeatures } from '../Lists/ExperimentalFeatures'
|
||||
import { SubscriptionName } from '@standardnotes/common'
|
||||
|
||||
export function GetFeatures(): FeatureDescription[] {
|
||||
return [
|
||||
...themes(),
|
||||
...editors(),
|
||||
...serverFeatures(),
|
||||
...clientFeatures(),
|
||||
...experimentalFeatures(),
|
||||
...GetDeprecatedFeatures(),
|
||||
]
|
||||
}
|
||||
|
||||
export function GetFeaturesForSubscription(subscription: SubscriptionName): FeatureDescription[] {
|
||||
return GetFeatures().filter((feature) => feature.availableInSubscriptions.includes(subscription))
|
||||
}
|
||||
|
||||
export function FindNativeFeature(identifier: FeatureIdentifier): FeatureDescription | undefined {
|
||||
return GetFeatures().find((f) => f.identifier === identifier)
|
||||
}
|
||||
58
packages/features/src/Domain/Lists/ClientFeatures.ts
Normal file
58
packages/features/src/Domain/Lists/ClientFeatures.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { ClientFeatureDescription } from '../Feature/FeatureDescription'
|
||||
import { PermissionName } from '../Permission/PermissionName'
|
||||
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
|
||||
import { SubscriptionName } from '@standardnotes/common'
|
||||
|
||||
export function clientFeatures(): ClientFeatureDescription[] {
|
||||
return [
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Tag Nesting',
|
||||
identifier: FeatureIdentifier.TagNesting,
|
||||
permission_name: PermissionName.TagNesting,
|
||||
description: 'Organize your tags into folders.',
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Smart Filters',
|
||||
identifier: FeatureIdentifier.SmartFilters,
|
||||
permission_name: PermissionName.SmartFilters,
|
||||
description: 'Create smart filters for viewing notes matching specific criteria.',
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Encrypted files (coming soon)',
|
||||
identifier: FeatureIdentifier.Files,
|
||||
permission_name: PermissionName.Files,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Encrypted files beta',
|
||||
identifier: FeatureIdentifier.FilesBeta,
|
||||
permission_name: PermissionName.FilesBeta,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Focus Mode',
|
||||
identifier: FeatureIdentifier.FocusMode,
|
||||
permission_name: PermissionName.FocusMode,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Listed Custom Domain',
|
||||
identifier: FeatureIdentifier.ListedCustomDomain,
|
||||
permission_name: PermissionName.ListedCustomDomain,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Multiple accounts',
|
||||
identifier: FeatureIdentifier.AccountSwitcher,
|
||||
permission_name: PermissionName.AccountSwitcher,
|
||||
description: '',
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { GetDeprecatedFeatures } from './DeprecatedFeatures'
|
||||
|
||||
it('all deprecated features should have deprecated flag true', async () => {
|
||||
for (const feature of GetDeprecatedFeatures()) {
|
||||
expect(feature.deprecated).toEqual(true)
|
||||
}
|
||||
})
|
||||
110
packages/features/src/Domain/Lists/DeprecatedFeatures.ts
Normal file
110
packages/features/src/Domain/Lists/DeprecatedFeatures.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { ContentType, SubscriptionName } from '@standardnotes/common'
|
||||
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 { ComponentAction } from '../Component/ComponentAction'
|
||||
import { ComponentArea } from '../Component/ComponentArea'
|
||||
|
||||
export function GetDeprecatedFeatures(): FeatureDescription[] {
|
||||
const bold: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Alternative Rich Text',
|
||||
identifier: FeatureIdentifier.DeprecatedBoldEditor,
|
||||
note_type: NoteType.RichText,
|
||||
file_type: 'html',
|
||||
component_permissions: [
|
||||
{
|
||||
name: ComponentAction.StreamContextItem,
|
||||
content_types: [ContentType.Note],
|
||||
},
|
||||
{
|
||||
name: ComponentAction.StreamItems,
|
||||
content_types: [
|
||||
ContentType.FilesafeCredentials,
|
||||
ContentType.FilesafeFileMetadata,
|
||||
ContentType.FilesafeIntegration,
|
||||
],
|
||||
},
|
||||
],
|
||||
spellcheckControl: true,
|
||||
deprecated: true,
|
||||
permission_name: PermissionName.BoldEditor,
|
||||
description: 'A simple and peaceful rich editor that helps you write and think clearly.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/editors/bold.jpg',
|
||||
})
|
||||
|
||||
const markdownBasic: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Basic Markdown',
|
||||
identifier: FeatureIdentifier.DeprecatedMarkdownBasicEditor,
|
||||
note_type: NoteType.Markdown,
|
||||
spellcheckControl: true,
|
||||
file_type: 'md',
|
||||
deprecated: true,
|
||||
permission_name: PermissionName.MarkdownBasicEditor,
|
||||
description: 'A Markdown editor with dynamic split-pane preview.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/editors/simple-markdown.jpg',
|
||||
})
|
||||
|
||||
const markdownMinimist: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Minimal Markdown',
|
||||
identifier: FeatureIdentifier.DeprecatedMarkdownMinimistEditor,
|
||||
note_type: NoteType.Markdown,
|
||||
file_type: 'md',
|
||||
index_path: 'index.html',
|
||||
permission_name: PermissionName.MarkdownMinimistEditor,
|
||||
spellcheckControl: true,
|
||||
deprecated: true,
|
||||
description: 'A minimal Markdown editor with live rendering and in-text search via Ctrl/Cmd + F',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/editors/min-markdown.jpg',
|
||||
})
|
||||
|
||||
const markdownMath: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Markdown with Math',
|
||||
identifier: FeatureIdentifier.DeprecatedMarkdownMathEditor,
|
||||
spellcheckControl: true,
|
||||
permission_name: PermissionName.MarkdownMathEditor,
|
||||
note_type: NoteType.Markdown,
|
||||
file_type: 'md',
|
||||
deprecated: true,
|
||||
index_path: 'index.html',
|
||||
description: 'A beautiful split-pane Markdown editor with synced-scroll, LaTeX support, and colorful syntax.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/editors/fancy-markdown.jpg',
|
||||
})
|
||||
|
||||
const filesafe: IframeComponentFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'FileSafe',
|
||||
identifier: FeatureIdentifier.DeprecatedFileSafe,
|
||||
component_permissions: [
|
||||
{
|
||||
name: ComponentAction.StreamContextItem,
|
||||
content_types: [ContentType.Note],
|
||||
},
|
||||
{
|
||||
name: ComponentAction.StreamItems,
|
||||
content_types: [
|
||||
ContentType.FilesafeCredentials,
|
||||
ContentType.FilesafeFileMetadata,
|
||||
ContentType.FilesafeIntegration,
|
||||
],
|
||||
},
|
||||
],
|
||||
permission_name: PermissionName.ComponentFilesafe,
|
||||
area: ComponentArea.EditorStack,
|
||||
deprecated: true,
|
||||
description:
|
||||
'Encrypted attachments for your notes using your Dropbox, Google Drive, or WebDAV server. Limited to 50MB per file.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/FileSafe-banner.png',
|
||||
})
|
||||
|
||||
return [bold, markdownBasic, markdownMinimist, markdownMath, filesafe]
|
||||
}
|
||||
105
packages/features/src/Domain/Lists/Editors.ts
Normal file
105
packages/features/src/Domain/Lists/Editors.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { SubscriptionName } from '@standardnotes/common'
|
||||
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'
|
||||
|
||||
export function editors(): EditorFeatureDescription[] {
|
||||
const code: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Code',
|
||||
spellcheckControl: true,
|
||||
identifier: FeatureIdentifier.CodeEditor,
|
||||
permission_name: PermissionName.CodeEditor,
|
||||
note_type: NoteType.Code,
|
||||
file_type: 'txt',
|
||||
interchangeable: true,
|
||||
index_path: 'index.html',
|
||||
description:
|
||||
'Syntax highlighting and convenient keyboard shortcuts for over 120 programming' +
|
||||
' languages. Ideal for code snippets and procedures.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/editors/code.jpg',
|
||||
})
|
||||
|
||||
const plus: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Rich Text',
|
||||
note_type: NoteType.RichText,
|
||||
file_type: 'html',
|
||||
identifier: FeatureIdentifier.PlusEditor,
|
||||
permission_name: PermissionName.PlusEditor,
|
||||
spellcheckControl: true,
|
||||
description:
|
||||
'From highlighting to custom font sizes and colors, to tables and lists, this editor is perfect for crafting any document.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/editors/plus-editor.jpg',
|
||||
})
|
||||
|
||||
const markdown: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Markdown',
|
||||
identifier: FeatureIdentifier.MarkdownProEditor,
|
||||
note_type: NoteType.Markdown,
|
||||
file_type: 'md',
|
||||
permission_name: PermissionName.MarkdownProEditor,
|
||||
spellcheckControl: true,
|
||||
description:
|
||||
'A fully featured Markdown editor that supports live preview, a styling toolbar, and split pane support.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/editors/adv-markdown.jpg',
|
||||
})
|
||||
|
||||
const markdownAlt: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Markdown Alternative',
|
||||
identifier: FeatureIdentifier.MarkdownVisualEditor,
|
||||
note_type: NoteType.Markdown,
|
||||
file_type: 'md',
|
||||
permission_name: PermissionName.MarkdownVisualEditor,
|
||||
spellcheckControl: true,
|
||||
description:
|
||||
'A WYSIWYG-style Markdown editor that renders Markdown in preview-mode while you type without displaying any syntax.',
|
||||
index_path: 'build/index.html',
|
||||
})
|
||||
|
||||
const task: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Checklist',
|
||||
identifier: FeatureIdentifier.TaskEditor,
|
||||
note_type: NoteType.Task,
|
||||
spellcheckControl: true,
|
||||
file_type: 'md',
|
||||
interchangeable: false,
|
||||
permission_name: PermissionName.TaskEditor,
|
||||
description:
|
||||
'A great way to manage short-term and long-term to-do"s. You can mark tasks as completed, change their order, and edit the text naturally in place.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/editors/task-editor.jpg',
|
||||
})
|
||||
|
||||
const tokenvault: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Authenticator',
|
||||
note_type: NoteType.Authentication,
|
||||
file_type: 'json',
|
||||
interchangeable: false,
|
||||
identifier: FeatureIdentifier.TokenVaultEditor,
|
||||
permission_name: PermissionName.TokenVaultEditor,
|
||||
description:
|
||||
'Encrypt and protect your 2FA secrets for all your internet accounts. Authenticator handles your 2FA secrets so that you never lose them again, or have to start over when you get a new device.',
|
||||
thumbnail_url: 'https://standard-notes.s3.amazonaws.com/screenshots/models/editors/token-vault.png',
|
||||
})
|
||||
|
||||
const spreadsheets: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Spreadsheet',
|
||||
identifier: FeatureIdentifier.SheetsEditor,
|
||||
note_type: NoteType.Spreadsheet,
|
||||
file_type: 'json',
|
||||
interchangeable: false,
|
||||
permission_name: PermissionName.SheetsEditor,
|
||||
description:
|
||||
'A powerful spreadsheet editor with formatting and formula support. Not recommended for large data sets, as encryption of such data may decrease editor performance.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/editors/spreadsheets.png',
|
||||
})
|
||||
|
||||
return [code, plus, markdown, markdownAlt, task, tokenvault, spreadsheets]
|
||||
}
|
||||
23
packages/features/src/Domain/Lists/ExperimentalFeatures.ts
Normal file
23
packages/features/src/Domain/Lists/ExperimentalFeatures.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { SubscriptionName } from '@standardnotes/common'
|
||||
import { NoteType } from '../Component/NoteType'
|
||||
import { PermissionName } from '../Permission/PermissionName'
|
||||
import { EditorFeatureDescription, FeatureDescription } from '../Feature/FeatureDescription'
|
||||
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
|
||||
import { FillEditorComponentDefaults } from './Utilities/FillEditorComponentDefaults'
|
||||
|
||||
export function experimentalFeatures(): FeatureDescription[] {
|
||||
const advancedChecklist: EditorFeatureDescription = FillEditorComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Advanced Checklist [Alpha]',
|
||||
identifier: FeatureIdentifier.AdvancedChecklist,
|
||||
note_type: NoteType.Task,
|
||||
spellcheckControl: true,
|
||||
file_type: 'json',
|
||||
interchangeable: false,
|
||||
permission_name: PermissionName.AdvancedChecklist,
|
||||
description: 'A task editor with grouping functionality.',
|
||||
index_path: 'build/index.html',
|
||||
})
|
||||
|
||||
return [advancedChecklist]
|
||||
}
|
||||
64
packages/features/src/Domain/Lists/ServerFeatures.ts
Normal file
64
packages/features/src/Domain/Lists/ServerFeatures.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { ServerFeatureDescription } from '../Feature/FeatureDescription'
|
||||
import { PermissionName } from '../Permission/PermissionName'
|
||||
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
|
||||
import { SubscriptionName } from '@standardnotes/common'
|
||||
|
||||
export function serverFeatures(): ServerFeatureDescription[] {
|
||||
return [
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Two factor authentication',
|
||||
identifier: FeatureIdentifier.TwoFactorAuth,
|
||||
permission_name: PermissionName.TwoFactorAuth,
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.ProPlan],
|
||||
name: 'Unlimited note history',
|
||||
identifier: FeatureIdentifier.NoteHistoryUnlimited,
|
||||
permission_name: PermissionName.NoteHistoryUnlimited,
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan],
|
||||
name: '365 days note history',
|
||||
identifier: FeatureIdentifier.NoteHistory365Days,
|
||||
permission_name: PermissionName.NoteHistory365Days,
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Email backups',
|
||||
identifier: FeatureIdentifier.DailyEmailBackup,
|
||||
permission_name: PermissionName.DailyEmailBackup,
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Sign-in email alerts',
|
||||
identifier: FeatureIdentifier.SignInAlerts,
|
||||
permission_name: PermissionName.SignInAlerts,
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
identifier: FeatureIdentifier.DailyDropboxBackup,
|
||||
permission_name: PermissionName.DailyDropboxBackup,
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
identifier: FeatureIdentifier.DailyGDriveBackup,
|
||||
permission_name: PermissionName.DailyGDriveBackup,
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
identifier: FeatureIdentifier.DailyOneDriveBackup,
|
||||
permission_name: PermissionName.DailyOneDriveBackup,
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.ProPlan],
|
||||
identifier: FeatureIdentifier.FilesMaximumStorageTier,
|
||||
permission_name: PermissionName.FilesMaximumStorageTier,
|
||||
},
|
||||
{
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan],
|
||||
identifier: FeatureIdentifier.FilesLowStorageTier,
|
||||
permission_name: PermissionName.FilesLowStorageTier,
|
||||
},
|
||||
]
|
||||
}
|
||||
109
packages/features/src/Domain/Lists/Themes.ts
Normal file
109
packages/features/src/Domain/Lists/Themes.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { ThemeFeatureDescription } from '../Feature/FeatureDescription'
|
||||
import { PermissionName } from '../Permission/PermissionName'
|
||||
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
|
||||
import { FillThemeComponentDefaults } from './Utilities/FillThemeComponentDefaults'
|
||||
import { SubscriptionName } from '@standardnotes/common'
|
||||
|
||||
export function themes(): ThemeFeatureDescription[] {
|
||||
const midnight: ThemeFeatureDescription = FillThemeComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Midnight',
|
||||
identifier: FeatureIdentifier.MidnightTheme,
|
||||
permission_name: PermissionName.MidnightTheme,
|
||||
description: 'Elegant utilitarianism.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/midnight-with-mobile.jpg',
|
||||
dock_icon: {
|
||||
type: 'circle',
|
||||
background_color: '#086DD6',
|
||||
foreground_color: '#ffffff',
|
||||
border_color: '#086DD6',
|
||||
},
|
||||
})
|
||||
|
||||
const futura: ThemeFeatureDescription = FillThemeComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Futura',
|
||||
identifier: FeatureIdentifier.FuturaTheme,
|
||||
permission_name: PermissionName.FuturaTheme,
|
||||
description: 'Calm and relaxed. Take some time off.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/futura-with-mobile.jpg',
|
||||
dock_icon: {
|
||||
type: 'circle',
|
||||
background_color: '#fca429',
|
||||
foreground_color: '#ffffff',
|
||||
border_color: '#fca429',
|
||||
},
|
||||
})
|
||||
|
||||
const solarizedDark: ThemeFeatureDescription = FillThemeComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Solarized Dark',
|
||||
identifier: FeatureIdentifier.SolarizedDarkTheme,
|
||||
permission_name: PermissionName.SolarizedDarkTheme,
|
||||
description: 'The perfect theme for any time.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/solarized-dark.jpg',
|
||||
dock_icon: {
|
||||
type: 'circle',
|
||||
background_color: '#2AA198',
|
||||
foreground_color: '#ffffff',
|
||||
border_color: '#2AA198',
|
||||
},
|
||||
})
|
||||
|
||||
const autobiography: ThemeFeatureDescription = FillThemeComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Autobiography',
|
||||
identifier: FeatureIdentifier.AutobiographyTheme,
|
||||
permission_name: PermissionName.AutobiographyTheme,
|
||||
description: 'A theme for writers and readers.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/autobiography.jpg',
|
||||
dock_icon: {
|
||||
type: 'circle',
|
||||
background_color: '#9D7441',
|
||||
foreground_color: '#ECE4DB',
|
||||
border_color: '#9D7441',
|
||||
},
|
||||
})
|
||||
|
||||
const focus: ThemeFeatureDescription = FillThemeComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Focus',
|
||||
identifier: FeatureIdentifier.FocusedTheme,
|
||||
permission_name: PermissionName.FocusedTheme,
|
||||
description: 'For when you need to go in.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/focus-with-mobile.jpg',
|
||||
dock_icon: {
|
||||
type: 'circle',
|
||||
background_color: '#a464c2',
|
||||
foreground_color: '#ffffff',
|
||||
border_color: '#a464c2',
|
||||
},
|
||||
})
|
||||
|
||||
const titanium: ThemeFeatureDescription = FillThemeComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Titanium',
|
||||
identifier: FeatureIdentifier.TitaniumTheme,
|
||||
permission_name: PermissionName.TitaniumTheme,
|
||||
description: 'Light on the eyes, heavy on the spirit.',
|
||||
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/titanium-with-mobile.jpg',
|
||||
dock_icon: {
|
||||
type: 'circle',
|
||||
background_color: '#6e2b9e',
|
||||
foreground_color: '#ffffff',
|
||||
border_color: '#6e2b9e',
|
||||
},
|
||||
})
|
||||
|
||||
const dynamic: ThemeFeatureDescription = FillThemeComponentDefaults({
|
||||
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
|
||||
name: 'Dynamic Panels',
|
||||
identifier: FeatureIdentifier.DynamicTheme,
|
||||
permission_name: PermissionName.ThemeDynamic,
|
||||
layerable: true,
|
||||
no_mobile: true,
|
||||
description: 'A smart theme that minimizes the tags and notes panels when they are not in use.',
|
||||
})
|
||||
|
||||
return [midnight, futura, solarizedDark, autobiography, focus, titanium, dynamic]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ComponentAction } from '../../Component/ComponentAction'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { EditorFeatureDescription } from '../../Feature/FeatureDescription'
|
||||
import { ComponentArea } from '../../Component/ComponentArea'
|
||||
|
||||
export type RequiredEditorFields = Pick<EditorFeatureDescription, 'availableInSubscriptions'>
|
||||
|
||||
export function FillEditorComponentDefaults(
|
||||
component: Partial<EditorFeatureDescription> & RequiredEditorFields,
|
||||
): EditorFeatureDescription {
|
||||
if (!component.index_path) {
|
||||
component.index_path = 'dist/index.html'
|
||||
}
|
||||
|
||||
if (!component.component_permissions) {
|
||||
component.component_permissions = [
|
||||
{
|
||||
name: ComponentAction.StreamContextItem,
|
||||
content_types: [ContentType.Note],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
component.content_type = ContentType.Component
|
||||
if (!component.area) {
|
||||
component.area = ComponentArea.Editor
|
||||
}
|
||||
|
||||
if (component.interchangeable == undefined) {
|
||||
component.interchangeable = true
|
||||
}
|
||||
|
||||
return component as EditorFeatureDescription
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { ThemeFeatureDescription } from '../../Feature/FeatureDescription'
|
||||
import { ComponentArea } from '../../Component/ComponentArea'
|
||||
|
||||
type RequiredThemeFields = Pick<ThemeFeatureDescription, 'availableInSubscriptions'>
|
||||
|
||||
export function FillThemeComponentDefaults(
|
||||
theme: Partial<ThemeFeatureDescription> & RequiredThemeFields,
|
||||
): ThemeFeatureDescription {
|
||||
if (!theme.index_path) {
|
||||
theme.index_path = 'dist/dist.css'
|
||||
}
|
||||
|
||||
theme.content_type = ContentType.Theme
|
||||
|
||||
if (!theme.area) {
|
||||
theme.area = ComponentArea.Themes
|
||||
}
|
||||
|
||||
return theme as ThemeFeatureDescription
|
||||
}
|
||||
8
packages/features/src/Domain/Permission/Permission.ts
Normal file
8
packages/features/src/Domain/Permission/Permission.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
import { PermissionName } from './PermissionName'
|
||||
|
||||
export type Permission = {
|
||||
uuid: Uuid
|
||||
name: PermissionName
|
||||
}
|
||||
42
packages/features/src/Domain/Permission/PermissionName.ts
Normal file
42
packages/features/src/Domain/Permission/PermissionName.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
export enum PermissionName {
|
||||
AccountSwitcher = 'app:account-switcher',
|
||||
AdvancedChecklist = 'editor:advanced-checklist',
|
||||
AutobiographyTheme = 'theme:autobiography',
|
||||
BoldEditor = 'editor:bold',
|
||||
CloudLink = 'component:cloud-link',
|
||||
CodeEditor = 'editor:code-editor',
|
||||
ComponentFilesafe = 'component:filesafe',
|
||||
ComponentFolders = 'component:folders',
|
||||
DailyDropboxBackup = 'server:daily-dropbox-backup',
|
||||
DailyEmailBackup = 'server:daily-email-backup',
|
||||
DailyGDriveBackup = 'server:daily-gdrive-backup',
|
||||
DailyOneDriveBackup = 'server:daily-onedrive-backup',
|
||||
FilesBeta = 'app:files-beta',
|
||||
Files = 'app:files',
|
||||
FilesMaximumStorageTier = 'server:files-max-storage-tier',
|
||||
FilesLowStorageTier = 'server:files-low-storage-tier',
|
||||
FocusedTheme = 'theme:focused',
|
||||
FocusMode = 'app:focus-mode',
|
||||
FuturaTheme = 'theme:futura',
|
||||
ListedCustomDomain = 'listed:custom-domain',
|
||||
MarkdownBasicEditor = 'editor:markdown-basic',
|
||||
MarkdownMathEditor = 'editor:markdown-math',
|
||||
MarkdownMinimistEditor = 'editor:markdown-minimist',
|
||||
MarkdownProEditor = 'editor:markdown-pro',
|
||||
MarkdownVisualEditor = 'editor:markdown-visual',
|
||||
MidnightTheme = 'theme:midnight',
|
||||
NoteHistory30Days = 'server:note-history-30-days',
|
||||
NoteHistory365Days = 'server:note-history-365-days',
|
||||
NoteHistoryUnlimited = 'server:note-history-unlimited',
|
||||
PlusEditor = 'editor:plus',
|
||||
SheetsEditor = 'editor:sheets',
|
||||
SignInAlerts = 'server:sign-in-alerts',
|
||||
SmartFilters = 'app:smart-filters',
|
||||
SolarizedDarkTheme = 'theme:solarized-dark',
|
||||
TagNesting = 'app:tag-nesting',
|
||||
TaskEditor = 'editor:task-editor',
|
||||
ThemeDynamic = 'theme:dynamic',
|
||||
TitaniumTheme = 'theme:titanium',
|
||||
TokenVaultEditor = 'editor:token-vault',
|
||||
TwoFactorAuth = 'server:two-factor-auth',
|
||||
}
|
||||
13
packages/features/src/Domain/index.ts
Normal file
13
packages/features/src/Domain/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export * from './Feature/FeatureDescription'
|
||||
export * from './Feature/FeatureIdentifier'
|
||||
export * from './Feature/Features'
|
||||
|
||||
export * from './Permission/Permission'
|
||||
export * from './Permission/PermissionName'
|
||||
|
||||
export * from './Component/ComponentAction'
|
||||
export * from './Component/ComponentArea'
|
||||
export * from './Component/ComponentFlag'
|
||||
export * from './Component/ComponentPermission'
|
||||
export * from './Component/NoteType'
|
||||
export * from './Component/ThemeDockIcon'
|
||||
1
packages/features/src/index.ts
Normal file
1
packages/features/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Domain'
|
||||
Reference in New Issue
Block a user