mirror of
https://github.com/standardnotes/app.git
synced 2026-01-16 23:01:30 +00:00
feat(web): extract ui-services package
This commit is contained in:
parent
c72a407095
commit
7e251262d7
161 changed files with 1105 additions and 824 deletions
BIN
.yarn/cache/@standardnotes-common-npm-1.30.0-a603c54867-833b6e7f3e.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-common-npm-1.30.0-a603c54867-833b6e7f3e.zip
vendored
Normal file
Binary file not shown.
|
|
@ -28,6 +28,7 @@
|
|||
"build:desktop": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/desktop --exclude @standardnotes/components-meta run build",
|
||||
"build:mobile": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/mobile --exclude @standardnotes/components-meta run build",
|
||||
"build:snjs": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/snjs --exclude @standardnotes/components-meta run build",
|
||||
"build:services": "yarn workspaces foreach -pt --verbose -R --from @standardnotes/services --exclude @standardnotes/components-meta run build",
|
||||
"start:server:web": "lerna run start --scope=@standardnotes/web",
|
||||
"start:server:e2e": "lerna run start:test-server --scope=@standardnotes/snjs",
|
||||
"prepare": "husky install",
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
import { AnyKeyParamsContent, ProtocolVersion } from '@standardnotes/common'
|
||||
import { BackupFileDecryptedContextualPayload, BackupFileEncryptedContextualPayload } from '@standardnotes/models'
|
||||
|
||||
export type BackupFile = {
|
||||
version?: ProtocolVersion
|
||||
keyParams?: AnyKeyParamsContent
|
||||
auth_params?: AnyKeyParamsContent
|
||||
items: (BackupFileDecryptedContextualPayload | BackupFileEncryptedContextualPayload)[]
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import {
|
|||
ProtocolVersion,
|
||||
} from '@standardnotes/common'
|
||||
import {
|
||||
BackupFile,
|
||||
CreateDecryptedItemFromPayload,
|
||||
CreatePayloadSplit,
|
||||
DecryptedPayload,
|
||||
|
|
@ -28,7 +29,6 @@ import { CreateAnyKeyParams } from '../Keys/RootKey/KeyParamsFunctions'
|
|||
import { SNRootKey } from '../Keys/RootKey/RootKey'
|
||||
import { SNRootKeyParams } from '../Keys/RootKey/RootKeyParams'
|
||||
import { EncryptionService } from '../Service/Encryption/EncryptionService'
|
||||
import { BackupFile } from './BackupFile'
|
||||
import { BackupFileType } from './BackupFileType'
|
||||
|
||||
export async function DecryptBackupFile(
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import {
|
||||
BackupFile,
|
||||
DecryptedPayloadInterface,
|
||||
EncryptedPayloadInterface,
|
||||
ItemContent,
|
||||
RootKeyInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
import { BackupFile } from '../../Backups/BackupFile'
|
||||
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
||||
import { KeyedDecryptionSplit } from '../../Split/KeyedDecryptionSplit'
|
||||
import { KeyedEncryptionSplit } from '../../Split/KeyedEncryptionSplit'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import * as Common from '@standardnotes/common'
|
||||
import * as Models from '@standardnotes/models'
|
||||
import {
|
||||
BackupFile,
|
||||
CreateDecryptedBackupFileContextPayload,
|
||||
CreateEncryptedBackupFileContextPayload,
|
||||
EncryptedPayload,
|
||||
|
|
@ -15,7 +16,6 @@ import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
|||
import * as Utils from '@standardnotes/utils'
|
||||
import { isNotUndefined } from '@standardnotes/utils'
|
||||
import { V001Algorithm, V002Algorithm } from '../../Algorithm'
|
||||
import { BackupFile } from '../../Backups/BackupFile'
|
||||
import { DecryptBackupFile } from '../../Backups/BackupFileDecryptor'
|
||||
import { CreateAnyKeyParams } from '../../Keys/RootKey/KeyParamsFunctions'
|
||||
import { SNRootKey } from '../../Keys/RootKey/RootKey'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
export * from './Algorithm'
|
||||
export * from './Backups/BackupFile'
|
||||
export * from './Backups/BackupFileDecryptor'
|
||||
export * from './Backups/BackupFileType'
|
||||
export * from './Keys/ItemsKey/ItemsKey'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
import { ComponentAction } from '@standardnotes/features'
|
||||
import { MessageData } from './MessageData'
|
||||
|
||||
export type ActionObserver = (action: ComponentAction, messageData: MessageData) => void
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import { ComponentViewerEvent } from './ComponentViewerEvent'
|
||||
|
||||
export type ComponentEventObserver = (event: ComponentViewerEvent) => void
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { ComponentAction } from '@standardnotes/features'
|
||||
import { MessageData } from './MessageData'
|
||||
|
||||
export type ComponentMessage = {
|
||||
action: ComponentAction
|
||||
sessionKey?: string
|
||||
componentData?: Record<string, unknown>
|
||||
data: MessageData
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export enum ComponentViewerEvent {
|
||||
FeatureStatusUpdated = 'FeatureStatusUpdated',
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { DecryptedTransferPayload } from '../TransferPayload/Interfaces/DecryptedTransferPayload'
|
||||
|
||||
export type IncomingComponentItemPayload = DecryptedTransferPayload & {
|
||||
clientData: Record<string, unknown>
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
export enum KeyboardModifier {
|
||||
Shift = 'Shift',
|
||||
Ctrl = 'Control',
|
||||
Meta = 'Meta',
|
||||
}
|
||||
31
packages/models/src/Domain/Abstract/Component/MessageData.ts
Normal file
31
packages/models/src/Domain/Abstract/Component/MessageData.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { ContentType, Uuid } from '@standardnotes/common'
|
||||
import { ComponentPermission } from '@standardnotes/features'
|
||||
|
||||
import { IncomingComponentItemPayload } from './IncomingComponentItemPayload'
|
||||
import { KeyboardModifier } from './KeyboardModifier'
|
||||
|
||||
export type MessageData = Partial<{
|
||||
/** Related to the stream-item-context action */
|
||||
item?: IncomingComponentItemPayload
|
||||
/** Related to the stream-items action */
|
||||
content_types?: ContentType[]
|
||||
items?: IncomingComponentItemPayload[]
|
||||
/** Related to the request-permission action */
|
||||
permissions?: ComponentPermission[]
|
||||
/** Related to the component-registered action */
|
||||
componentData?: Record<string, unknown>
|
||||
uuid?: Uuid
|
||||
environment?: string
|
||||
platform?: string
|
||||
activeThemeUrls?: string[]
|
||||
/** Related to set-size action */
|
||||
width?: string | number
|
||||
height?: string | number
|
||||
type?: string
|
||||
/** Related to themes action */
|
||||
themes?: string[]
|
||||
/** Related to clear-selection action */
|
||||
content_type?: ContentType
|
||||
/** Related to key-pressed action */
|
||||
keyboardModifier?: KeyboardModifier
|
||||
}>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { ComponentPermission } from '@standardnotes/features'
|
||||
import { SNComponent } from '../../Syncable/Component'
|
||||
|
||||
export type PermissionDialog = {
|
||||
component: SNComponent
|
||||
permissions: ComponentPermission[]
|
||||
permissionsString: string
|
||||
actionBlock: (approved: boolean) => void
|
||||
callback: (approved: boolean) => void
|
||||
}
|
||||
|
|
@ -1,60 +1,10 @@
|
|||
import { Uuid } from '@standardnotes/common'
|
||||
import { ContextPayload } from './ContextPayload'
|
||||
import { ItemContent } from '../Content/ItemContent'
|
||||
import { DecryptedTransferPayload, EncryptedTransferPayload } from '../TransferPayload'
|
||||
import { AnyKeyParamsContent, ProtocolVersion } from '@standardnotes/common'
|
||||
import { BackupFileDecryptedContextualPayload } from './BackupFileDecryptedContextualPayload'
|
||||
import { BackupFileEncryptedContextualPayload } from './BackupFileEncryptedContextualPayload'
|
||||
|
||||
export interface BackupFileEncryptedContextualPayload extends ContextPayload {
|
||||
auth_hash?: string
|
||||
content: string
|
||||
created_at_timestamp: number
|
||||
created_at: Date
|
||||
duplicate_of?: Uuid
|
||||
enc_item_key: string
|
||||
items_key_id: string | undefined
|
||||
updated_at: Date
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
|
||||
export interface BackupFileDecryptedContextualPayload<C extends ItemContent = ItemContent> extends ContextPayload {
|
||||
content: C
|
||||
created_at_timestamp: number
|
||||
created_at: Date
|
||||
duplicate_of?: Uuid
|
||||
updated_at: Date
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
|
||||
export function CreateEncryptedBackupFileContextPayload(
|
||||
fromPayload: EncryptedTransferPayload,
|
||||
): BackupFileEncryptedContextualPayload {
|
||||
return {
|
||||
auth_hash: fromPayload.auth_hash,
|
||||
content_type: fromPayload.content_type,
|
||||
content: fromPayload.content,
|
||||
created_at_timestamp: fromPayload.created_at_timestamp,
|
||||
created_at: fromPayload.created_at,
|
||||
deleted: false,
|
||||
duplicate_of: fromPayload.duplicate_of,
|
||||
enc_item_key: fromPayload.enc_item_key,
|
||||
items_key_id: fromPayload.items_key_id,
|
||||
updated_at_timestamp: fromPayload.updated_at_timestamp,
|
||||
updated_at: fromPayload.updated_at,
|
||||
uuid: fromPayload.uuid,
|
||||
}
|
||||
}
|
||||
|
||||
export function CreateDecryptedBackupFileContextPayload(
|
||||
fromPayload: DecryptedTransferPayload,
|
||||
): BackupFileDecryptedContextualPayload {
|
||||
return {
|
||||
content_type: fromPayload.content_type,
|
||||
content: fromPayload.content,
|
||||
created_at_timestamp: fromPayload.created_at_timestamp,
|
||||
created_at: fromPayload.created_at,
|
||||
deleted: false,
|
||||
duplicate_of: fromPayload.duplicate_of,
|
||||
updated_at_timestamp: fromPayload.updated_at_timestamp,
|
||||
updated_at: fromPayload.updated_at,
|
||||
uuid: fromPayload.uuid,
|
||||
}
|
||||
export type BackupFile = {
|
||||
version?: ProtocolVersion
|
||||
keyParams?: AnyKeyParamsContent
|
||||
auth_params?: AnyKeyParamsContent
|
||||
items: (BackupFileDecryptedContextualPayload | BackupFileEncryptedContextualPayload)[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
import { Uuid } from '@standardnotes/common'
|
||||
import { ItemContent } from '../Content/ItemContent'
|
||||
import { ContextPayload } from './ContextPayload'
|
||||
|
||||
export interface BackupFileDecryptedContextualPayload<C extends ItemContent = ItemContent> extends ContextPayload {
|
||||
content: C
|
||||
created_at_timestamp: number
|
||||
created_at: Date
|
||||
duplicate_of?: Uuid
|
||||
updated_at: Date
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { Uuid } from '@standardnotes/common'
|
||||
import { ContextPayload } from './ContextPayload'
|
||||
|
||||
export interface BackupFileEncryptedContextualPayload extends ContextPayload {
|
||||
auth_hash?: string
|
||||
content: string
|
||||
created_at_timestamp: number
|
||||
created_at: Date
|
||||
duplicate_of?: Uuid
|
||||
enc_item_key: string
|
||||
items_key_id: string | undefined
|
||||
updated_at: Date
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
39
packages/models/src/Domain/Abstract/Contextual/Functions.ts
Normal file
39
packages/models/src/Domain/Abstract/Contextual/Functions.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { DecryptedTransferPayload, EncryptedTransferPayload } from '../TransferPayload'
|
||||
|
||||
import { BackupFileDecryptedContextualPayload } from './BackupFileDecryptedContextualPayload'
|
||||
import { BackupFileEncryptedContextualPayload } from './BackupFileEncryptedContextualPayload'
|
||||
|
||||
export function CreateEncryptedBackupFileContextPayload(
|
||||
fromPayload: EncryptedTransferPayload,
|
||||
): BackupFileEncryptedContextualPayload {
|
||||
return {
|
||||
auth_hash: fromPayload.auth_hash,
|
||||
content_type: fromPayload.content_type,
|
||||
content: fromPayload.content,
|
||||
created_at_timestamp: fromPayload.created_at_timestamp,
|
||||
created_at: fromPayload.created_at,
|
||||
deleted: false,
|
||||
duplicate_of: fromPayload.duplicate_of,
|
||||
enc_item_key: fromPayload.enc_item_key,
|
||||
items_key_id: fromPayload.items_key_id,
|
||||
updated_at_timestamp: fromPayload.updated_at_timestamp,
|
||||
updated_at: fromPayload.updated_at,
|
||||
uuid: fromPayload.uuid,
|
||||
}
|
||||
}
|
||||
|
||||
export function CreateDecryptedBackupFileContextPayload(
|
||||
fromPayload: DecryptedTransferPayload,
|
||||
): BackupFileDecryptedContextualPayload {
|
||||
return {
|
||||
content_type: fromPayload.content_type,
|
||||
content: fromPayload.content,
|
||||
created_at_timestamp: fromPayload.created_at_timestamp,
|
||||
created_at: fromPayload.created_at,
|
||||
deleted: false,
|
||||
duplicate_of: fromPayload.duplicate_of,
|
||||
updated_at_timestamp: fromPayload.updated_at_timestamp,
|
||||
updated_at: fromPayload.updated_at,
|
||||
uuid: fromPayload.uuid,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
export * from './ComponentCreate'
|
||||
export * from './ComponentRetrieved'
|
||||
export * from './BackupFile'
|
||||
export * from './LocalStorage'
|
||||
export * from './OfflineSyncPush'
|
||||
export * from './OfflineSyncSaved'
|
||||
export * from './ServerSyncPush'
|
||||
export * from './SessionHistory'
|
||||
export * from './ServerSyncSaved'
|
||||
export * from './FilteredServerItem'
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
import { MutationType } from '../Types/MutationType'
|
||||
|
||||
import { ItemMutator } from './ItemMutator'
|
||||
|
||||
export type TransactionalMutation = {
|
||||
itemUuid: Uuid
|
||||
mutate: (mutator: ItemMutator) => void
|
||||
mutationType?: MutationType
|
||||
}
|
||||
11
packages/models/src/Domain/Abstract/Item/Types/ItemStream.ts
Normal file
11
packages/models/src/Domain/Abstract/Item/Types/ItemStream.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { PayloadEmitSource } from '../../Payload'
|
||||
import { DecryptedItemInterface } from '../Interfaces/DecryptedItem'
|
||||
import { DeletedItemInterface } from '../Interfaces/DeletedItem'
|
||||
import { EncryptedItemInterface } from '../Interfaces/EncryptedItem'
|
||||
|
||||
export type ItemStream<I extends DecryptedItemInterface> = (data: {
|
||||
changed: I[]
|
||||
inserted: I[]
|
||||
removed: (DeletedItemInterface | EncryptedItemInterface)[]
|
||||
source: PayloadEmitSource
|
||||
}) => void
|
||||
|
|
@ -20,10 +20,10 @@ export * from './Interfaces/TypeCheck'
|
|||
export * from './Mutator/DecryptedItemMutator'
|
||||
export * from './Mutator/DeleteMutator'
|
||||
export * from './Mutator/ItemMutator'
|
||||
export * from './Types/AppDataField'
|
||||
export * from './Mutator/TransactionalMutation'
|
||||
export * from './Types/AppDataField'
|
||||
export * from './Types/ConflictStrategy'
|
||||
export * from './Types/DefaultAppDomain'
|
||||
export * from './Types/DefaultAppDomain'
|
||||
export * from './Types/ItemStream'
|
||||
export * from './Types/MutationType'
|
||||
export * from './Types/SingletonStrategy'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,26 @@
|
|||
export * from './Abstract/Component/ActionObserver'
|
||||
export * from './Abstract/Component/ComponentViewerEvent'
|
||||
export * from './Abstract/Component/ComponentMessage'
|
||||
export * from './Abstract/Component/ComponentEventObserver'
|
||||
export * from './Abstract/Component/IncomingComponentItemPayload'
|
||||
export * from './Abstract/Component/KeyboardModifier'
|
||||
export * from './Abstract/Component/MessageData'
|
||||
export * from './Abstract/Component/PermissionDialog'
|
||||
export * from './Abstract/Content/ItemContent'
|
||||
export * from './Abstract/Contextual'
|
||||
export * from './Abstract/Contextual/BackupFile'
|
||||
export * from './Abstract/Contextual/BackupFileDecryptedContextualPayload'
|
||||
export * from './Abstract/Contextual/BackupFileEncryptedContextualPayload'
|
||||
export * from './Abstract/Contextual/ComponentCreate'
|
||||
export * from './Abstract/Contextual/ComponentRetrieved'
|
||||
export * from './Abstract/Contextual/ContextPayload'
|
||||
export * from './Abstract/Contextual/FilteredServerItem'
|
||||
export * from './Abstract/Contextual/Functions'
|
||||
export * from './Abstract/Contextual/LocalStorage'
|
||||
export * from './Abstract/Contextual/OfflineSyncPush'
|
||||
export * from './Abstract/Contextual/OfflineSyncSaved'
|
||||
export * from './Abstract/Contextual/ServerSyncPush'
|
||||
export * from './Abstract/Contextual/ServerSyncSaved'
|
||||
export * from './Abstract/Contextual/SessionHistory'
|
||||
export * from './Abstract/Item'
|
||||
export * from './Abstract/Payload'
|
||||
export * from './Abstract/TransferPayload'
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/auth": "^3.19.4",
|
||||
"@standardnotes/common": "^1.23.1",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/common": "^1.30.0",
|
||||
"@standardnotes/models": "workspace:^",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/security": "^1.2.0",
|
||||
"@standardnotes/utils": "workspace:*",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
import { ApplicationInterface } from './ApplicationInterface'
|
||||
import { DeinitCallback } from './DeinitCallback'
|
||||
|
||||
export interface AppGroupManagedApplication extends ApplicationInterface {
|
||||
onDeinit: DeinitCallback
|
||||
setOnDeinit(onDeinit: DeinitCallback): void
|
||||
}
|
||||
|
|
@ -1,22 +1,44 @@
|
|||
import { ApplicationIdentifier } from '@standardnotes/common'
|
||||
import { ApplicationIdentifier, ContentType } from '@standardnotes/common'
|
||||
import { BackupFile, DecryptedItemInterface, ItemStream, PrefKey, PrefValue } from '@standardnotes/models'
|
||||
|
||||
import { ComponentManagerInterface } from '../Component/ComponentManagerInterface'
|
||||
import { ApplicationEvent } from '../Event/ApplicationEvent'
|
||||
import { ApplicationEventCallback } from '../Event/ApplicationEventCallback'
|
||||
import { FeaturesClientInterface } from '../Feature/FeaturesClientInterface'
|
||||
import { ItemsClientInterface } from '../Item/ItemsClientInterface'
|
||||
import { MutatorClientInterface } from '../Mutator/MutatorClientInterface'
|
||||
import { StorageValueModes } from '../Storage/StorageTypes'
|
||||
|
||||
import { DeinitCallback } from './DeinitCallback'
|
||||
import { DeinitMode } from './DeinitMode'
|
||||
import { DeinitSource } from './DeinitSource'
|
||||
import { UserClientInterface } from './UserClientInterface'
|
||||
|
||||
export interface ApplicationInterface {
|
||||
deinit(mode: DeinitMode, source: DeinitSource): void
|
||||
|
||||
getDeinitMode(): DeinitMode
|
||||
|
||||
isStarted(): boolean
|
||||
isLaunched(): boolean
|
||||
addEventObserver(callback: ApplicationEventCallback, singleEvent?: ApplicationEvent): () => void
|
||||
hasProtectionSources(): boolean
|
||||
createEncryptedBackupFileForAutomatedDesktopBackups(): Promise<BackupFile | undefined>
|
||||
createDecryptedBackupFile(): Promise<BackupFile | undefined>
|
||||
hasPasscode(): boolean
|
||||
lock(): Promise<void>
|
||||
setValue(key: string, value: unknown, mode?: StorageValueModes): void
|
||||
getValue(key: string, mode?: StorageValueModes): unknown
|
||||
removeValue(key: string, mode?: StorageValueModes): Promise<void>
|
||||
isLocked(): Promise<boolean>
|
||||
getPreference<K extends PrefKey>(key: K): PrefValue[K] | undefined
|
||||
getPreference<K extends PrefKey>(key: K, defaultValue: PrefValue[K]): PrefValue[K]
|
||||
getPreference<K extends PrefKey>(key: K, defaultValue?: PrefValue[K]): PrefValue[K] | undefined
|
||||
streamItems<I extends DecryptedItemInterface = DecryptedItemInterface>(
|
||||
contentType: ContentType | ContentType[],
|
||||
stream: ItemStream<I>,
|
||||
): () => void
|
||||
get features(): FeaturesClientInterface
|
||||
get componentManager(): ComponentManagerInterface
|
||||
get items(): ItemsClientInterface
|
||||
get mutator(): MutatorClientInterface
|
||||
get user(): UserClientInterface
|
||||
|
||||
readonly identifier: ApplicationIdentifier
|
||||
}
|
||||
|
||||
export interface AppGroupManagedApplication extends ApplicationInterface {
|
||||
onDeinit: DeinitCallback
|
||||
|
||||
setOnDeinit(onDeinit: DeinitCallback): void
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { DeinitSource } from './DeinitSource'
|
||||
import { DeinitMode } from './DeinitMode'
|
||||
import { AppGroupManagedApplication } from './ApplicationInterface'
|
||||
import { AppGroupManagedApplication } from './AppGroupManagedApplication'
|
||||
|
||||
export type DeinitCallback = (application: AppGroupManagedApplication, mode: DeinitMode, source: DeinitSource) => void
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import { DesktopManagerInterface } from '../Device/DesktopManagerInterface'
|
||||
import { WebAppEvent } from '../Event/WebAppEvent'
|
||||
import { ApplicationInterface } from './ApplicationInterface'
|
||||
|
||||
export interface WebApplicationInterface extends ApplicationInterface {
|
||||
notifyWebEvent(event: WebAppEvent, data?: unknown): void
|
||||
getDesktopService(): DesktopManagerInterface | undefined
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { Uuid } from '@standardnotes/common'
|
||||
import { ComponentArea } from '@standardnotes/features'
|
||||
import { ActionObserver, PermissionDialog, SNComponent, SNNote } from '@standardnotes/models'
|
||||
|
||||
import { DesktopManagerInterface } from '../Device/DesktopManagerInterface'
|
||||
import { ComponentViewerInterface } from './ComponentViewerInterface'
|
||||
|
||||
export interface ComponentManagerInterface {
|
||||
urlForComponent(component: SNComponent): string | undefined
|
||||
setDesktopManager(desktopManager: DesktopManagerInterface): void
|
||||
componentsForArea(area: ComponentArea): SNComponent[]
|
||||
editorForNote(note: SNNote): SNComponent | undefined
|
||||
doesEditorChangeRequireAlert(from: SNComponent | undefined, to: SNComponent | undefined): boolean
|
||||
showEditorChangeAlert(): Promise<boolean>
|
||||
destroyComponentViewer(viewer: ComponentViewerInterface): void
|
||||
createComponentViewer(
|
||||
component: SNComponent,
|
||||
contextItem?: Uuid,
|
||||
actionObserver?: ActionObserver,
|
||||
urlOverride?: string,
|
||||
): ComponentViewerInterface
|
||||
presentPermissionsDialog(_dialog: PermissionDialog): void
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export enum ComponentViewerError {
|
||||
OfflineRestricted = 'OfflineRestricted',
|
||||
MissingUrl = 'MissingUrl',
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import {
|
||||
ActionObserver,
|
||||
ComponentEventObserver,
|
||||
ComponentMessage,
|
||||
DecryptedItemInterface,
|
||||
SNComponent,
|
||||
} from '@standardnotes/models'
|
||||
import { FeatureStatus } from '../Feature/FeatureStatus'
|
||||
import { ComponentViewerError } from './ComponentViewerError'
|
||||
|
||||
export interface ComponentViewerInterface {
|
||||
readonly component: SNComponent
|
||||
readonly url?: string
|
||||
identifier: string
|
||||
lockReadonly: boolean
|
||||
sessionKey?: string
|
||||
overrideContextItem?: DecryptedItemInterface
|
||||
get componentUuid(): string
|
||||
destroy(): void
|
||||
setReadonly(readonly: boolean): void
|
||||
getFeatureStatus(): FeatureStatus
|
||||
shouldRender(): boolean
|
||||
getError(): ComponentViewerError | undefined
|
||||
setWindow(window: Window): void
|
||||
addEventObserver(observer: ComponentEventObserver): () => void
|
||||
addActionObserver(observer: ActionObserver): () => void
|
||||
postActiveThemes(): void
|
||||
handleMessage(message: ComponentMessage): void
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { SNComponent } from '@standardnotes/models'
|
||||
|
||||
export interface DesktopManagerInterface {
|
||||
syncComponentsInstallation(components: SNComponent[]): void
|
||||
registerUpdateObserver(callback: (component: SNComponent) => void): () => void
|
||||
getExtServerHost(): string
|
||||
}
|
||||
65
packages/services/src/Domain/Event/ApplicationEvent.ts
Normal file
65
packages/services/src/Domain/Event/ApplicationEvent.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
export enum ApplicationEvent {
|
||||
SignedIn = 2,
|
||||
SignedOut = 3,
|
||||
|
||||
/** When a full, potentially multi-page sync completes */
|
||||
CompletedFullSync = 5,
|
||||
|
||||
FailedSync = 6,
|
||||
HighLatencySync = 7,
|
||||
EnteredOutOfSync = 8,
|
||||
ExitedOutOfSync = 9,
|
||||
|
||||
/**
|
||||
* The application has finished it `prepareForLaunch` state and is now ready for unlock
|
||||
* Called when the application has initialized and is ready for launch, but before
|
||||
* the application has been unlocked, if applicable. Use this to do pre-launch
|
||||
* configuration, but do not attempt to access user data like notes or tags.
|
||||
*/
|
||||
Started = 10,
|
||||
|
||||
/**
|
||||
* The applicaiton is fully unlocked and ready for i/o
|
||||
* Called when the application has been fully decrypted and unlocked. Use this to
|
||||
* to begin streaming data like notes and tags.
|
||||
*/
|
||||
Launched = 11,
|
||||
LocalDataLoaded = 12,
|
||||
|
||||
/**
|
||||
* When the root key or root key wrapper changes. Includes events like account state
|
||||
* changes (registering, signing in, changing pw, logging out) and passcode state
|
||||
* changes (adding, removing, changing).
|
||||
*/
|
||||
KeyStatusChanged = 13,
|
||||
|
||||
MajorDataChange = 14,
|
||||
CompletedRestart = 15,
|
||||
LocalDataIncrementalLoad = 16,
|
||||
SyncStatusChanged = 17,
|
||||
WillSync = 18,
|
||||
InvalidSyncSession = 19,
|
||||
LocalDatabaseReadError = 20,
|
||||
LocalDatabaseWriteError = 21,
|
||||
|
||||
/** When a single roundtrip completes with sync, in a potentially multi-page sync request.
|
||||
* If just a single roundtrip, this event will be triggered, along with CompletedFullSync */
|
||||
CompletedIncrementalSync = 22,
|
||||
|
||||
/**
|
||||
* The application has loaded all pending migrations (but not run any, except for the base one),
|
||||
* and consumers may now call `hasPendingMigrations`
|
||||
*/
|
||||
MigrationsLoaded = 23,
|
||||
|
||||
/** When StorageService is ready to start servicing read/write requests */
|
||||
StorageReady = 24,
|
||||
|
||||
PreferencesChanged = 25,
|
||||
UnprotectedSessionBegan = 26,
|
||||
UserRolesChanged = 27,
|
||||
FeaturesUpdated = 28,
|
||||
UnprotectedSessionExpired = 29,
|
||||
/** Called when the app first launches and after first sync request made after sign in */
|
||||
CompletedInitialSync = 30,
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import { ApplicationEvent } from './ApplicationEvent'
|
||||
|
||||
export type ApplicationEventCallback = (event: ApplicationEvent, data?: unknown) => Promise<void>
|
||||
6
packages/services/src/Domain/Feature/FeatureStatus.ts
Normal file
6
packages/services/src/Domain/Feature/FeatureStatus.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export enum FeatureStatus {
|
||||
NoUserSubscription = 'NoUserSubscription',
|
||||
NotInCurrentPlan = 'NotInCurrentPlan',
|
||||
InCurrentPlanButExpired = 'InCurrentPlanButExpired',
|
||||
Entitled = 'Entitled',
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import { FeatureStatus, SetOfflineFeaturesFunctionResponse } from './Types'
|
||||
import { FeatureDescription, FeatureIdentifier } from '@standardnotes/features'
|
||||
import { SNComponent } from '@standardnotes/models'
|
||||
import { RoleName } from '@standardnotes/common'
|
||||
|
||||
import { FeatureStatus } from './FeatureStatus'
|
||||
import { SetOfflineFeaturesFunctionResponse } from './SetOfflineFeaturesFunctionResponse'
|
||||
|
||||
export interface FeaturesClientInterface {
|
||||
downloadExternalFeature(urlOrCode: string): Promise<SNComponent | undefined>
|
||||
|
||||
4
packages/services/src/Domain/Feature/FeaturesEvent.ts
Normal file
4
packages/services/src/Domain/Feature/FeaturesEvent.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export enum FeaturesEvent {
|
||||
UserRolesChanged = 'UserRolesChanged',
|
||||
FeaturesUpdated = 'FeaturesUpdated',
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export type OfflineSubscriptionEntitlements = {
|
||||
featuresUrl: string
|
||||
extensionKey: string
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
|
||||
export type SetOfflineFeaturesFunctionResponse = ClientDisplayableError | undefined
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { SNItemsKey } from '@standardnotes/encryption'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { ContentType, Uuid } from '@standardnotes/common'
|
||||
import {
|
||||
SNNote,
|
||||
FileItem,
|
||||
|
|
@ -14,8 +13,8 @@ import {
|
|||
SNComponent,
|
||||
SNTheme,
|
||||
DisplayOptions,
|
||||
ItemsKeyInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { UuidString } from '@Lib/Types'
|
||||
|
||||
export interface ItemsClientInterface {
|
||||
get invalidItems(): EncryptedItemInterface[]
|
||||
|
|
@ -43,7 +42,7 @@ export interface ItemsClientInterface {
|
|||
|
||||
getDisplayableTags(): SNTag[]
|
||||
|
||||
getDisplayableItemsKeys(): SNItemsKey[]
|
||||
getDisplayableItemsKeys(): ItemsKeyInterface[]
|
||||
|
||||
getDisplayableFiles(): FileItem[]
|
||||
|
||||
|
|
@ -116,14 +115,14 @@ export interface ItemsClientInterface {
|
|||
/**
|
||||
* Finds an item by UUID.
|
||||
*/
|
||||
findItem<T extends DecryptedItemInterface = DecryptedItemInterface>(uuid: UuidString): T | undefined
|
||||
findItem<T extends DecryptedItemInterface = DecryptedItemInterface>(uuid: Uuid): T | undefined
|
||||
|
||||
/**
|
||||
* Finds an item by predicate.
|
||||
*/
|
||||
findItems<T extends DecryptedItemInterface>(uuids: UuidString[]): T[]
|
||||
findItems<T extends DecryptedItemInterface>(uuids: Uuid[]): T[]
|
||||
|
||||
findSureItem<T extends DecryptedItemInterface = DecryptedItemInterface>(uuid: UuidString): T
|
||||
findSureItem<T extends DecryptedItemInterface = DecryptedItemInterface>(uuid: Uuid): T
|
||||
|
||||
/**
|
||||
* Finds an item by predicate.
|
||||
184
packages/services/src/Domain/Mutator/MutatorClientInterface.ts
Normal file
184
packages/services/src/Domain/Mutator/MutatorClientInterface.ts
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
import { ContentType } from '@standardnotes/common'
|
||||
import {
|
||||
BackupFile,
|
||||
DecryptedItemInterface,
|
||||
DecryptedItemMutator,
|
||||
EncryptedItemInterface,
|
||||
FileItem,
|
||||
ItemContent,
|
||||
PayloadEmitSource,
|
||||
SmartView,
|
||||
SNComponent,
|
||||
SNNote,
|
||||
SNTag,
|
||||
TransactionalMutation,
|
||||
} from '@standardnotes/models'
|
||||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
|
||||
import { ChallengeReason } from '../Challenge/Types/ChallengeReason'
|
||||
import { SyncOptions } from '../Sync/SyncOptions'
|
||||
|
||||
export interface MutatorClientInterface {
|
||||
/**
|
||||
* Inserts the input item by its payload properties, and marks the item as dirty.
|
||||
* A sync is not performed after an item is inserted. This must be handled by the caller.
|
||||
*/
|
||||
insertItem(item: DecryptedItemInterface): Promise<DecryptedItemInterface>
|
||||
|
||||
/**
|
||||
* Mutates a pre-existing item, marks it as dirty, and syncs it
|
||||
*/
|
||||
changeAndSaveItem<M extends DecryptedItemMutator = DecryptedItemMutator>(
|
||||
itemToLookupUuidFor: DecryptedItemInterface,
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps?: boolean,
|
||||
emitSource?: PayloadEmitSource,
|
||||
syncOptions?: SyncOptions,
|
||||
): Promise<DecryptedItemInterface | undefined>
|
||||
|
||||
/**
|
||||
* Mutates pre-existing items, marks them as dirty, and syncs
|
||||
*/
|
||||
changeAndSaveItems<M extends DecryptedItemMutator = DecryptedItemMutator>(
|
||||
itemsToLookupUuidsFor: DecryptedItemInterface[],
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps?: boolean,
|
||||
emitSource?: PayloadEmitSource,
|
||||
syncOptions?: SyncOptions,
|
||||
): Promise<void>
|
||||
|
||||
/**
|
||||
* Mutates a pre-existing item and marks it as dirty. Does not sync changes.
|
||||
*/
|
||||
changeItem<M extends DecryptedItemMutator>(
|
||||
itemToLookupUuidFor: DecryptedItemInterface,
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps?: boolean,
|
||||
): Promise<DecryptedItemInterface | undefined>
|
||||
|
||||
/**
|
||||
* Mutates a pre-existing items and marks them as dirty. Does not sync changes.
|
||||
*/
|
||||
changeItems<M extends DecryptedItemMutator = DecryptedItemMutator>(
|
||||
itemsToLookupUuidsFor: DecryptedItemInterface[],
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps?: boolean,
|
||||
): Promise<(DecryptedItemInterface | undefined)[]>
|
||||
|
||||
/**
|
||||
* Run unique mutations per each item in the array, then only propagate all changes
|
||||
* once all mutations have been run. This differs from `changeItems` in that changeItems
|
||||
* runs the same mutation on all items.
|
||||
*/
|
||||
runTransactionalMutations(
|
||||
transactions: TransactionalMutation[],
|
||||
emitSource?: PayloadEmitSource,
|
||||
payloadSourceKey?: string,
|
||||
): Promise<(DecryptedItemInterface | undefined)[]>
|
||||
|
||||
runTransactionalMutation(
|
||||
transaction: TransactionalMutation,
|
||||
emitSource?: PayloadEmitSource,
|
||||
payloadSourceKey?: string,
|
||||
): Promise<DecryptedItemInterface | undefined>
|
||||
|
||||
protectItems<_M extends DecryptedItemMutator<ItemContent>, I extends DecryptedItemInterface<ItemContent>>(
|
||||
items: I[],
|
||||
): Promise<I[]>
|
||||
|
||||
unprotectItems<_M extends DecryptedItemMutator<ItemContent>, I extends DecryptedItemInterface<ItemContent>>(
|
||||
items: I[],
|
||||
reason: ChallengeReason,
|
||||
): Promise<I[] | undefined>
|
||||
|
||||
protectNote(note: SNNote): Promise<SNNote>
|
||||
|
||||
unprotectNote(note: SNNote): Promise<SNNote | undefined>
|
||||
|
||||
protectNotes(notes: SNNote[]): Promise<SNNote[]>
|
||||
|
||||
unprotectNotes(notes: SNNote[]): Promise<SNNote[]>
|
||||
|
||||
protectFile(file: FileItem): Promise<FileItem>
|
||||
|
||||
unprotectFile(file: FileItem): Promise<FileItem | undefined>
|
||||
|
||||
/**
|
||||
* Takes the values of the input item and emits it onto global state.
|
||||
*/
|
||||
mergeItem(item: DecryptedItemInterface, source: PayloadEmitSource): Promise<DecryptedItemInterface>
|
||||
|
||||
/**
|
||||
* Creates an unmanaged item that can be added later.
|
||||
*/
|
||||
createTemplateItem<
|
||||
C extends ItemContent = ItemContent,
|
||||
I extends DecryptedItemInterface<C> = DecryptedItemInterface<C>,
|
||||
>(
|
||||
contentType: ContentType,
|
||||
content?: C,
|
||||
): I
|
||||
|
||||
/**
|
||||
* @param isUserModified Whether to change the modified date the user
|
||||
* sees of the item.
|
||||
*/
|
||||
setItemNeedsSync(item: DecryptedItemInterface, isUserModified?: boolean): Promise<DecryptedItemInterface | undefined>
|
||||
|
||||
setItemsNeedsSync(items: DecryptedItemInterface[]): Promise<(DecryptedItemInterface | undefined)[]>
|
||||
|
||||
deleteItem(item: DecryptedItemInterface | EncryptedItemInterface): Promise<void>
|
||||
|
||||
deleteItems(items: (DecryptedItemInterface | EncryptedItemInterface)[]): Promise<void>
|
||||
|
||||
emptyTrash(): Promise<void>
|
||||
|
||||
duplicateItem<T extends DecryptedItemInterface>(item: T, additionalContent?: Partial<T['content']>): Promise<T>
|
||||
|
||||
/**
|
||||
* Migrates any tags containing a '.' character to sa chema-based heirarchy, removing
|
||||
* the dot from the tag's title.
|
||||
*/
|
||||
migrateTagsToFolders(): Promise<unknown>
|
||||
|
||||
/**
|
||||
* Establishes a hierarchical relationship between two tags.
|
||||
*/
|
||||
setTagParent(parentTag: SNTag, childTag: SNTag): Promise<void>
|
||||
|
||||
/**
|
||||
* Remove the tag parent.
|
||||
*/
|
||||
unsetTagParent(childTag: SNTag): Promise<void>
|
||||
|
||||
findOrCreateTag(title: string): Promise<SNTag>
|
||||
|
||||
/** Creates and returns the tag but does not run sync. Callers must perform sync. */
|
||||
createTagOrSmartView(title: string): Promise<SNTag | SmartView>
|
||||
|
||||
/**
|
||||
* Activates or deactivates a component, depending on its
|
||||
* current state, and syncs.
|
||||
*/
|
||||
toggleComponent(component: SNComponent): Promise<void>
|
||||
|
||||
toggleTheme(theme: SNComponent): Promise<void>
|
||||
|
||||
/**
|
||||
* @returns
|
||||
* .affectedItems: Items that were either created or dirtied by this import
|
||||
* .errorCount: The number of items that were not imported due to failure to decrypt.
|
||||
*/
|
||||
importData(
|
||||
data: BackupFile,
|
||||
awaitSync?: boolean,
|
||||
): Promise<
|
||||
| {
|
||||
affectedItems: DecryptedItemInterface[]
|
||||
errorCount: number
|
||||
}
|
||||
| {
|
||||
error: ClientDisplayableError
|
||||
}
|
||||
>
|
||||
}
|
||||
|
|
@ -1,13 +1,19 @@
|
|||
export * from './Alert/AlertService'
|
||||
export * from './Api/ApiServiceInterface'
|
||||
export * from './Application/AppGroupManagedApplication'
|
||||
export * from './Application/ApplicationInterface'
|
||||
export * from './Application/ApplicationStage'
|
||||
export * from './Application/DeinitCallback'
|
||||
export * from './Application/DeinitSource'
|
||||
export * from './Application/DeinitMode'
|
||||
export * from './Application/UserClientInterface'
|
||||
export * from './Application/ApplicationInterface'
|
||||
export * from './Application/WebApplicationInterface'
|
||||
export * from './Challenge'
|
||||
export * from './Component/ComponentManagerInterface'
|
||||
export * from './Component/ComponentViewerError'
|
||||
export * from './Component/ComponentViewerInterface'
|
||||
export * from './Device/DesktopDeviceInterface'
|
||||
export * from './Device/DesktopManagerInterface'
|
||||
export * from './Device/DesktopWebCommunication'
|
||||
export * from './Device/DeviceInterface'
|
||||
export * from './Device/Environments'
|
||||
|
|
@ -16,9 +22,17 @@ export * from './Device/MobileDeviceInterface'
|
|||
export * from './Device/TypeCheck'
|
||||
export * from './Device/WebOrDesktopDeviceInterface'
|
||||
export * from './Diagnostics/ServiceDiagnostics'
|
||||
export * from './Event/ApplicationEvent'
|
||||
export * from './Event/ApplicationEventCallback'
|
||||
export * from './Event/EventObserver'
|
||||
export * from './Event/SyncEvent'
|
||||
export * from './Event/SyncEventReceiver'
|
||||
export * from './Event/WebAppEvent'
|
||||
export * from './Feature/FeatureStatus'
|
||||
export * from './Feature/FeaturesClientInterface'
|
||||
export * from './Feature/FeaturesEvent'
|
||||
export * from './Feature/OfflineSubscriptionEntitlements'
|
||||
export * from './Feature/SetOfflineFeaturesFunctionResponse'
|
||||
export * from './Files/FilesApiInterface'
|
||||
export * from './FileSystem/FileSystemApi'
|
||||
export * from './Integrity/IntegrityApiInterface'
|
||||
|
|
@ -32,7 +46,9 @@ export * from './Internal/InternalEventInterface'
|
|||
export * from './Internal/InternalEventPublishStrategy'
|
||||
export * from './Internal/InternalEventType'
|
||||
export * from './Item/ItemManagerInterface'
|
||||
export * from './Item/ItemsClientInterface'
|
||||
export * from './Item/ItemsServerInterface'
|
||||
export * from './Mutator/MutatorClientInterface'
|
||||
export * from './Payloads/PayloadManagerInterface'
|
||||
export * from './Preferences/PreferenceServiceInterface'
|
||||
export * from './Service/AbstractService'
|
||||
|
|
|
|||
|
|
@ -19,9 +19,12 @@ import * as Settings from '@standardnotes/settings'
|
|||
import * as Files from '@standardnotes/files'
|
||||
import { Subscription } from '@standardnotes/security'
|
||||
import { UuidString, ApplicationEventPayload } from '../Types'
|
||||
import { ApplicationEvent, applicationEventForSyncEvent } from '@Lib/Application/Event'
|
||||
import { applicationEventForSyncEvent } from '@Lib/Application/Event'
|
||||
import {
|
||||
ApplicationEvent,
|
||||
ApplicationEventCallback,
|
||||
ChallengeValidation,
|
||||
ComponentManagerInterface,
|
||||
DiagnosticInfo,
|
||||
Environment,
|
||||
isDesktopDevice,
|
||||
|
|
@ -36,7 +39,7 @@ import {
|
|||
} from '@standardnotes/services'
|
||||
import { SNLog } from '../Log'
|
||||
import { useBoolean } from '@standardnotes/utils'
|
||||
import { DecryptedItemInterface, EncryptedItemInterface } from '@standardnotes/models'
|
||||
import { BackupFile, DecryptedItemInterface, EncryptedItemInterface, ItemStream } from '@standardnotes/models'
|
||||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
import { Challenge, ChallengeResponse } from '../Services'
|
||||
import { ApplicationConstructorOptions, FullyResolvedApplicationOptions } from './Options/ApplicationOptions'
|
||||
|
|
@ -49,20 +52,11 @@ type LaunchCallback = {
|
|||
receiveChallenge: (challenge: Challenge) => void
|
||||
}
|
||||
|
||||
type ApplicationEventCallback = (event: ApplicationEvent, data?: unknown) => Promise<void>
|
||||
|
||||
type ApplicationObserver = {
|
||||
singleEvent?: ApplicationEvent
|
||||
callback: ApplicationEventCallback
|
||||
}
|
||||
|
||||
type ItemStream<I extends DecryptedItemInterface> = (data: {
|
||||
changed: I[]
|
||||
inserted: I[]
|
||||
removed: (Models.DeletedItemInterface | Models.EncryptedItemInterface)[]
|
||||
source: Models.PayloadEmitSource
|
||||
}) => void
|
||||
|
||||
type ObserverRemover = () => void
|
||||
|
||||
export class SNApplication
|
||||
|
|
@ -97,7 +91,7 @@ export class SNApplication
|
|||
private syncService!: InternalServices.SNSyncService
|
||||
private challengeService!: InternalServices.ChallengeService
|
||||
public singletonManager!: InternalServices.SNSingletonManager
|
||||
public componentManager!: InternalServices.SNComponentManager
|
||||
public componentManagerService!: InternalServices.SNComponentManager
|
||||
public protectionService!: InternalServices.SNProtectionService
|
||||
public actionsManager!: InternalServices.SNActionsService
|
||||
public historyManager!: InternalServices.SNHistoryManager
|
||||
|
|
@ -192,11 +186,11 @@ export class SNApplication
|
|||
return this.fileService
|
||||
}
|
||||
|
||||
public get features(): InternalServices.FeaturesClientInterface {
|
||||
public get features(): ExternalServices.FeaturesClientInterface {
|
||||
return this.featuresService
|
||||
}
|
||||
|
||||
public get items(): InternalServices.ItemsClientInterface {
|
||||
public get items(): ExternalServices.ItemsClientInterface {
|
||||
return this.itemManager
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +210,7 @@ export class SNApplication
|
|||
return this.settingsService
|
||||
}
|
||||
|
||||
public get mutator(): InternalServices.MutatorClientInterface {
|
||||
public get mutator(): ExternalServices.MutatorClientInterface {
|
||||
return this.mutatorService
|
||||
}
|
||||
|
||||
|
|
@ -232,6 +226,10 @@ export class SNApplication
|
|||
return this.filesBackupService
|
||||
}
|
||||
|
||||
public get componentManager(): ComponentManagerInterface {
|
||||
return this.componentManagerService
|
||||
}
|
||||
|
||||
public computePrivateWorkspaceIdentifier(userphrase: string, name: string): Promise<string | undefined> {
|
||||
return Encryption.ComputePrivateWorkspaceIdentifier(this.options.crypto, userphrase, name)
|
||||
}
|
||||
|
|
@ -659,11 +657,11 @@ export class SNApplication
|
|||
return this.listedService.getListedAccountInfo(account, inContextOfItem)
|
||||
}
|
||||
|
||||
public async createEncryptedBackupFileForAutomatedDesktopBackups(): Promise<Encryption.BackupFile | undefined> {
|
||||
public async createEncryptedBackupFileForAutomatedDesktopBackups(): Promise<BackupFile | undefined> {
|
||||
return this.protocolService.createEncryptedBackupFile()
|
||||
}
|
||||
|
||||
public async createEncryptedBackupFile(): Promise<Encryption.BackupFile | undefined> {
|
||||
public async createEncryptedBackupFile(): Promise<BackupFile | undefined> {
|
||||
if (!(await this.protectionService.authorizeBackupCreation())) {
|
||||
return
|
||||
}
|
||||
|
|
@ -671,7 +669,7 @@ export class SNApplication
|
|||
return this.protocolService.createEncryptedBackupFile()
|
||||
}
|
||||
|
||||
public async createDecryptedBackupFile(): Promise<Encryption.BackupFile | undefined> {
|
||||
public async createDecryptedBackupFile(): Promise<BackupFile | undefined> {
|
||||
if (!(await this.protectionService.authorizeBackupCreation())) {
|
||||
return
|
||||
}
|
||||
|
|
@ -1070,7 +1068,7 @@ export class SNApplication
|
|||
;(this.syncService as unknown) = undefined
|
||||
;(this.challengeService as unknown) = undefined
|
||||
;(this.singletonManager as unknown) = undefined
|
||||
;(this.componentManager as unknown) = undefined
|
||||
;(this.componentManagerService as unknown) = undefined
|
||||
;(this.protectionService as unknown) = undefined
|
||||
;(this.actionsManager as unknown) = undefined
|
||||
;(this.historyManager as unknown) = undefined
|
||||
|
|
@ -1161,11 +1159,11 @@ export class SNApplication
|
|||
this.serviceObservers.push(
|
||||
this.featuresService.addEventObserver((event) => {
|
||||
switch (event) {
|
||||
case InternalServices.FeaturesEvent.UserRolesChanged: {
|
||||
case ExternalServices.FeaturesEvent.UserRolesChanged: {
|
||||
void this.notifyEvent(ApplicationEvent.UserRolesChanged)
|
||||
break
|
||||
}
|
||||
case InternalServices.FeaturesEvent.FeaturesUpdated: {
|
||||
case ExternalServices.FeaturesEvent.FeaturesUpdated: {
|
||||
void this.notifyEvent(ApplicationEvent.FeaturesUpdated)
|
||||
break
|
||||
}
|
||||
|
|
@ -1268,7 +1266,7 @@ export class SNApplication
|
|||
const MaybeSwappedComponentManager = this.getClass<typeof InternalServices.SNComponentManager>(
|
||||
InternalServices.SNComponentManager,
|
||||
)
|
||||
this.componentManager = new MaybeSwappedComponentManager(
|
||||
this.componentManagerService = new MaybeSwappedComponentManager(
|
||||
this.itemManager,
|
||||
this.syncService,
|
||||
this.featuresService,
|
||||
|
|
@ -1278,7 +1276,7 @@ export class SNApplication
|
|||
this.platform,
|
||||
this.internalEventBus,
|
||||
)
|
||||
this.services.push(this.componentManager)
|
||||
this.services.push(this.componentManagerService)
|
||||
}
|
||||
|
||||
private createHttpManager() {
|
||||
|
|
@ -1533,7 +1531,7 @@ export class SNApplication
|
|||
this.protocolService,
|
||||
this.payloadManager,
|
||||
this.challengeService,
|
||||
this.componentManager,
|
||||
this.componentManagerService,
|
||||
this.historyManager,
|
||||
this.internalEventBus,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,72 +1,6 @@
|
|||
import { SyncEvent } from '@standardnotes/services'
|
||||
import { ApplicationEvent, SyncEvent } from '@standardnotes/services'
|
||||
export { SyncEvent }
|
||||
|
||||
export enum ApplicationEvent {
|
||||
SignedIn = 2,
|
||||
SignedOut = 3,
|
||||
|
||||
/** When a full, potentially multi-page sync completes */
|
||||
CompletedFullSync = 5,
|
||||
|
||||
FailedSync = 6,
|
||||
HighLatencySync = 7,
|
||||
EnteredOutOfSync = 8,
|
||||
ExitedOutOfSync = 9,
|
||||
|
||||
/**
|
||||
* The application has finished it `prepareForLaunch` state and is now ready for unlock
|
||||
* Called when the application has initialized and is ready for launch, but before
|
||||
* the application has been unlocked, if applicable. Use this to do pre-launch
|
||||
* configuration, but do not attempt to access user data like notes or tags.
|
||||
*/
|
||||
Started = 10,
|
||||
|
||||
/**
|
||||
* The applicaiton is fully unlocked and ready for i/o
|
||||
* Called when the application has been fully decrypted and unlocked. Use this to
|
||||
* to begin streaming data like notes and tags.
|
||||
*/
|
||||
Launched = 11,
|
||||
LocalDataLoaded = 12,
|
||||
|
||||
/**
|
||||
* When the root key or root key wrapper changes. Includes events like account state
|
||||
* changes (registering, signing in, changing pw, logging out) and passcode state
|
||||
* changes (adding, removing, changing).
|
||||
*/
|
||||
KeyStatusChanged = 13,
|
||||
|
||||
MajorDataChange = 14,
|
||||
CompletedRestart = 15,
|
||||
LocalDataIncrementalLoad = 16,
|
||||
SyncStatusChanged = 17,
|
||||
WillSync = 18,
|
||||
InvalidSyncSession = 19,
|
||||
LocalDatabaseReadError = 20,
|
||||
LocalDatabaseWriteError = 21,
|
||||
|
||||
/** When a single roundtrip completes with sync, in a potentially multi-page sync request.
|
||||
* If just a single roundtrip, this event will be triggered, along with CompletedFullSync */
|
||||
CompletedIncrementalSync = 22,
|
||||
|
||||
/**
|
||||
* The application has loaded all pending migrations (but not run any, except for the base one),
|
||||
* and consumers may now call `hasPendingMigrations`
|
||||
*/
|
||||
MigrationsLoaded = 23,
|
||||
|
||||
/** When StorageService is ready to start servicing read/write requests */
|
||||
StorageReady = 24,
|
||||
|
||||
PreferencesChanged = 25,
|
||||
UnprotectedSessionBegan = 26,
|
||||
UserRolesChanged = 27,
|
||||
FeaturesUpdated = 28,
|
||||
UnprotectedSessionExpired = 29,
|
||||
/** Called when the app first launches and after first sync request made after sign in */
|
||||
CompletedInitialSync = 30,
|
||||
}
|
||||
|
||||
export function applicationEventForSyncEvent(syncEvent: SyncEvent) {
|
||||
return (
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { ApplicationEvent } from '../Application/Event'
|
||||
import { FileItem, PrefKey, SNNote } from '@standardnotes/models'
|
||||
import { removeFromArray } from '@standardnotes/utils'
|
||||
import { ApplicationEvent } from '@standardnotes/services'
|
||||
|
||||
import { SNApplication } from '../Application/Application'
|
||||
|
||||
import { NoteViewController } from './NoteViewController'
|
||||
import { FileViewController } from './FileViewController'
|
||||
import { TemplateNoteViewControllerOptions } from './TemplateNoteViewControllerOptions'
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
import { ApplicationEvent } from '@Lib/Application/Event'
|
||||
import { AbstractService, InternalEventBusInterface } from '@standardnotes/services'
|
||||
import { SNApplication } from '../../Application/Application'
|
||||
import {
|
||||
AbstractService,
|
||||
ApplicationEvent,
|
||||
ApplicationInterface,
|
||||
InternalEventBusInterface,
|
||||
} from '@standardnotes/services'
|
||||
|
||||
export class ApplicationService extends AbstractService {
|
||||
private unsubApp!: () => void
|
||||
|
||||
constructor(protected application: SNApplication, protected override internalEventBus: InternalEventBusInterface) {
|
||||
constructor(
|
||||
protected application: ApplicationInterface,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
this.addAppEventObserverAfterSubclassesFinishConstructing()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,15 @@ import {
|
|||
FindNativeFeature,
|
||||
FeatureIdentifier,
|
||||
} from '@standardnotes/features'
|
||||
import { DesktopManagerInterface } from '@Lib/Services/ComponentManager/Types'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { GenericItem, SNComponent } from '@standardnotes/models'
|
||||
import { InternalEventBusInterface, Environment, Platform, AlertService } from '@standardnotes/services'
|
||||
import {
|
||||
DesktopManagerInterface,
|
||||
InternalEventBusInterface,
|
||||
Environment,
|
||||
Platform,
|
||||
AlertService,
|
||||
} from '@standardnotes/services'
|
||||
import { ItemManager } from '@Lib/Services/Items/ItemManager'
|
||||
import { SNFeaturesService } from '@Lib/Services/Features/FeaturesService'
|
||||
import { SNComponentManager } from './ComponentManager'
|
||||
|
|
@ -34,7 +39,10 @@ describe('featuresService', () => {
|
|||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
syncComponentsInstallation() {},
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
registerUpdateObserver() {},
|
||||
registerUpdateObserver(_callback: (component: SNComponent) => void) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
return () => {}
|
||||
},
|
||||
getExtServerHost() {
|
||||
return desktopExtHost
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,21 +3,28 @@ import { SNPreferencesService } from '../Preferences/PreferencesService'
|
|||
import { SNFeaturesService } from '@Lib/Services/Features/FeaturesService'
|
||||
import { ContentType, DisplayStringForContentType } from '@standardnotes/common'
|
||||
import { ItemManager } from '@Lib/Services/Items/ItemManager'
|
||||
import { SNNote, SNTheme, SNComponent, ComponentMutator, PayloadEmitSource } from '@standardnotes/models'
|
||||
import {
|
||||
ActionObserver,
|
||||
SNNote,
|
||||
SNTheme,
|
||||
SNComponent,
|
||||
ComponentMutator,
|
||||
PayloadEmitSource,
|
||||
PermissionDialog,
|
||||
} from '@standardnotes/models'
|
||||
import { SNSyncService } from '@Lib/Services/Sync/SyncService'
|
||||
import find from 'lodash/find'
|
||||
import uniq from 'lodash/uniq'
|
||||
import { ComponentArea, ComponentAction, ComponentPermission, FindNativeFeature } from '@standardnotes/features'
|
||||
import { Copy, filterFromArray, removeFromArray, sleep, assert } from '@standardnotes/utils'
|
||||
import { UuidString } from '@Lib/Types/UuidString'
|
||||
import {
|
||||
PermissionDialog,
|
||||
DesktopManagerInterface,
|
||||
AllowedBatchContentTypes,
|
||||
} from '@Lib/Services/ComponentManager/Types'
|
||||
import { ActionObserver, ComponentViewer } from '@Lib/Services/ComponentManager/ComponentViewer'
|
||||
import { AllowedBatchContentTypes } from '@Lib/Services/ComponentManager/Types'
|
||||
import { ComponentViewer } from '@Lib/Services/ComponentManager/ComponentViewer'
|
||||
import {
|
||||
AbstractService,
|
||||
ComponentManagerInterface,
|
||||
ComponentViewerInterface,
|
||||
DesktopManagerInterface,
|
||||
InternalEventBusInterface,
|
||||
Environment,
|
||||
Platform,
|
||||
|
|
@ -42,7 +49,7 @@ export enum ComponentManagerEvent {
|
|||
}
|
||||
|
||||
export type EventData = {
|
||||
componentViewer?: ComponentViewer
|
||||
componentViewer?: ComponentViewerInterface
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -50,9 +57,12 @@ export type EventData = {
|
|||
* and other components. The component manager primarily deals with iframes, and orchestrates
|
||||
* sending and receiving messages to and from frames via the postMessage API.
|
||||
*/
|
||||
export class SNComponentManager extends AbstractService<ComponentManagerEvent, EventData> {
|
||||
export class SNComponentManager
|
||||
extends AbstractService<ComponentManagerEvent, EventData>
|
||||
implements ComponentManagerInterface
|
||||
{
|
||||
private desktopManager?: DesktopManagerInterface
|
||||
private viewers: ComponentViewer[] = []
|
||||
private viewers: ComponentViewerInterface[] = []
|
||||
private removeItemObserver!: () => void
|
||||
private permissionDialogs: PermissionDialog[] = []
|
||||
|
||||
|
|
@ -137,7 +147,7 @@ export class SNComponentManager extends AbstractService<ComponentManagerEvent, E
|
|||
contextItem?: UuidString,
|
||||
actionObserver?: ActionObserver,
|
||||
urlOverride?: string,
|
||||
): ComponentViewer {
|
||||
): ComponentViewerInterface {
|
||||
const viewer = new ComponentViewer(
|
||||
component,
|
||||
this.itemManager,
|
||||
|
|
@ -159,7 +169,7 @@ export class SNComponentManager extends AbstractService<ComponentManagerEvent, E
|
|||
return viewer
|
||||
}
|
||||
|
||||
public destroyComponentViewer(viewer: ComponentViewer): void {
|
||||
public destroyComponentViewer(viewer: ComponentViewerInterface): void {
|
||||
viewer.destroy()
|
||||
removeFromArray(this.viewers, viewer)
|
||||
}
|
||||
|
|
@ -316,11 +326,11 @@ export class SNComponentManager extends AbstractService<ComponentManagerEvent, E
|
|||
return this.itemManager.findItem<SNComponent>(uuid)
|
||||
}
|
||||
|
||||
findComponentViewer(identifier: string): ComponentViewer | undefined {
|
||||
findComponentViewer(identifier: string): ComponentViewerInterface | undefined {
|
||||
return this.viewers.find((viewer) => viewer.identifier === identifier)
|
||||
}
|
||||
|
||||
componentViewerForSessionKey(key: string): ComponentViewer | undefined {
|
||||
componentViewerForSessionKey(key: string): ComponentViewerInterface | undefined {
|
||||
return this.viewers.find((viewer) => viewer.sessionKey === key)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,19 @@
|
|||
import { SNPreferencesService } from '../Preferences/PreferencesService'
|
||||
import { FeatureStatus, FeaturesEvent } from '@Lib/Services/Features'
|
||||
import { Environment, Platform, AlertService } from '@standardnotes/services'
|
||||
import {
|
||||
ComponentViewerInterface,
|
||||
ComponentViewerError,
|
||||
Environment,
|
||||
FeatureStatus,
|
||||
FeaturesEvent,
|
||||
Platform,
|
||||
AlertService,
|
||||
} from '@standardnotes/services'
|
||||
import { SNFeaturesService } from '@Lib/Services'
|
||||
import {
|
||||
ActionObserver,
|
||||
ComponentEventObserver,
|
||||
ComponentViewerEvent,
|
||||
ComponentMessage,
|
||||
SNComponent,
|
||||
PrefKey,
|
||||
NoteContent,
|
||||
|
|
@ -21,6 +32,8 @@ import {
|
|||
ComponentDataDomain,
|
||||
PayloadEmitSource,
|
||||
PayloadTimestampDefaults,
|
||||
IncomingComponentItemPayload,
|
||||
MessageData,
|
||||
} from '@standardnotes/models'
|
||||
import find from 'lodash/find'
|
||||
import uniq from 'lodash/uniq'
|
||||
|
|
@ -28,12 +41,10 @@ import remove from 'lodash/remove'
|
|||
import { SNSyncService } from '@Lib/Services/Sync/SyncService'
|
||||
import { environmentToString, platformToString } from '@Lib/Application/Platforms'
|
||||
import {
|
||||
ComponentMessage,
|
||||
OutgoingItemMessagePayload,
|
||||
MessageReply,
|
||||
StreamItemsMessageData,
|
||||
AllowedBatchContentTypes,
|
||||
IncomingComponentItemPayload,
|
||||
DeleteItemsMessageData,
|
||||
MessageReplyData,
|
||||
} from './Types'
|
||||
|
|
@ -53,7 +64,6 @@ import {
|
|||
sureSearchArray,
|
||||
isNotUndefined,
|
||||
} from '@standardnotes/utils'
|
||||
import { MessageData } from '..'
|
||||
|
||||
type RunWithPermissionsCallback = (
|
||||
componentUuid: UuidString,
|
||||
|
|
@ -76,21 +86,9 @@ const ReadwriteActions = [
|
|||
ComponentAction.SetComponentData,
|
||||
]
|
||||
|
||||
export type ActionObserver = (action: ComponentAction, messageData: MessageData) => void
|
||||
|
||||
export enum ComponentViewerEvent {
|
||||
FeatureStatusUpdated = 'FeatureStatusUpdated',
|
||||
}
|
||||
type EventObserver = (event: ComponentViewerEvent) => void
|
||||
|
||||
export enum ComponentViewerError {
|
||||
OfflineRestricted = 'OfflineRestricted',
|
||||
MissingUrl = 'MissingUrl',
|
||||
}
|
||||
|
||||
type Writeable<T> = { -readonly [P in keyof T]: T[P] }
|
||||
|
||||
export class ComponentViewer {
|
||||
export class ComponentViewer implements ComponentViewerInterface {
|
||||
private streamItems?: ContentType[]
|
||||
private streamContextItemOriginalMessage?: ComponentMessage
|
||||
private streamItemsOriginalMessage?: ComponentMessage
|
||||
|
|
@ -101,7 +99,7 @@ export class ComponentViewer {
|
|||
public overrideContextItem?: DecryptedItemInterface
|
||||
private featureStatus: FeatureStatus
|
||||
private removeFeaturesObserver: () => void
|
||||
private eventObservers: EventObserver[] = []
|
||||
private eventObservers: ComponentEventObserver[] = []
|
||||
private dealloced = false
|
||||
|
||||
private window?: Window
|
||||
|
|
@ -189,7 +187,7 @@ export class ComponentViewer {
|
|||
;(this.removeItemObserver as unknown) = undefined
|
||||
}
|
||||
|
||||
public addEventObserver(observer: EventObserver): () => void {
|
||||
public addEventObserver(observer: ComponentEventObserver): () => void {
|
||||
this.eventObservers.push(observer)
|
||||
|
||||
const thislessChangeObservers = this.eventObservers
|
||||
|
|
|
|||
|
|
@ -1,24 +1,8 @@
|
|||
import {
|
||||
ComponentArea,
|
||||
ComponentAction,
|
||||
ComponentPermission,
|
||||
FeatureIdentifier,
|
||||
LegacyFileSafeIdentifier,
|
||||
} from '@standardnotes/features'
|
||||
import { ItemContent, SNComponent, DecryptedTransferPayload } from '@standardnotes/models'
|
||||
import { ComponentArea, ComponentAction, FeatureIdentifier, LegacyFileSafeIdentifier } from '@standardnotes/features'
|
||||
import { ComponentMessage, ItemContent, MessageData } from '@standardnotes/models'
|
||||
import { UuidString } from '@Lib/Types/UuidString'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
export interface DesktopManagerInterface {
|
||||
syncComponentsInstallation(components: SNComponent[]): void
|
||||
registerUpdateObserver(callback: (component: SNComponent) => void): void
|
||||
getExtServerHost(): string
|
||||
}
|
||||
|
||||
export type IncomingComponentItemPayload = DecryptedTransferPayload & {
|
||||
clientData: Record<string, unknown>
|
||||
}
|
||||
|
||||
export type OutgoingItemMessagePayload = {
|
||||
uuid: string
|
||||
content_type: ContentType
|
||||
|
|
@ -63,46 +47,6 @@ export type StreamObserver = {
|
|||
contentTypes?: ContentType[]
|
||||
}
|
||||
|
||||
export type PermissionDialog = {
|
||||
component: SNComponent
|
||||
permissions: ComponentPermission[]
|
||||
permissionsString: string
|
||||
actionBlock: (approved: boolean) => void
|
||||
callback: (approved: boolean) => void
|
||||
}
|
||||
|
||||
export enum KeyboardModifier {
|
||||
Shift = 'Shift',
|
||||
Ctrl = 'Control',
|
||||
Meta = 'Meta',
|
||||
}
|
||||
|
||||
export type MessageData = Partial<{
|
||||
/** Related to the stream-item-context action */
|
||||
item?: IncomingComponentItemPayload
|
||||
/** Related to the stream-items action */
|
||||
content_types?: ContentType[]
|
||||
items?: IncomingComponentItemPayload[]
|
||||
/** Related to the request-permission action */
|
||||
permissions?: ComponentPermission[]
|
||||
/** Related to the component-registered action */
|
||||
componentData?: Record<string, unknown>
|
||||
uuid?: UuidString
|
||||
environment?: string
|
||||
platform?: string
|
||||
activeThemeUrls?: string[]
|
||||
/** Related to set-size action */
|
||||
width?: string | number
|
||||
height?: string | number
|
||||
type?: string
|
||||
/** Related to themes action */
|
||||
themes?: string[]
|
||||
/** Related to clear-selection action */
|
||||
content_type?: ContentType
|
||||
/** Related to key-pressed action */
|
||||
keyboardModifier?: KeyboardModifier
|
||||
}>
|
||||
|
||||
export type MessageReplyData = {
|
||||
approved?: boolean
|
||||
deleted?: boolean
|
||||
|
|
@ -120,13 +64,6 @@ export type DeleteItemsMessageData = MessageData & {
|
|||
items: OutgoingItemMessagePayload[]
|
||||
}
|
||||
|
||||
export type ComponentMessage = {
|
||||
action: ComponentAction
|
||||
sessionKey?: string
|
||||
componentData?: Record<string, unknown>
|
||||
data: MessageData
|
||||
}
|
||||
|
||||
export type MessageReply = {
|
||||
action: ComponentAction
|
||||
original: ComponentMessage
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ import {
|
|||
DiskStorageService,
|
||||
StorageKey,
|
||||
} from '@Lib/index'
|
||||
import { FeatureStatus, SNFeaturesService } from '@Lib/Services/Features'
|
||||
import { SNFeaturesService } from '@Lib/Services/Features'
|
||||
import { ContentType, RoleName } from '@standardnotes/common'
|
||||
import { FeatureDescription, FeatureIdentifier, GetFeatures } from '@standardnotes/features'
|
||||
import { SNWebSocketsService } from '../Api/WebsocketsService'
|
||||
import { SNSettingsService } from '../Settings'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { convertTimestampToMilliseconds } from '@standardnotes/utils'
|
||||
import { InternalEventBusInterface } from '@standardnotes/services'
|
||||
import { FeatureStatus, InternalEventBusInterface } from '@standardnotes/services'
|
||||
|
||||
describe('featuresService', () => {
|
||||
let storageService: DiskStorageService
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import {
|
|||
} from '@standardnotes/utils'
|
||||
import { ClientDisplayableError, UserFeaturesResponse } from '@standardnotes/responses'
|
||||
import { ContentType, RoleName } from '@standardnotes/common'
|
||||
import { FeaturesClientInterface } from './ClientInterface'
|
||||
import { FillItemContent, PayloadEmitSource } from '@standardnotes/models'
|
||||
import { ItemManager } from '../Items/ItemManager'
|
||||
import { LEGACY_PROD_EXT_ORIGIN, PROD_OFFLINE_FEATURES_URL } from '../../Hosts'
|
||||
|
|
@ -27,20 +26,30 @@ import { UuidString } from '@Lib/Types/UuidString'
|
|||
import * as FeaturesImports from '@standardnotes/features'
|
||||
import * as Messages from '@Lib/Services/Api/Messages'
|
||||
import * as Models from '@standardnotes/models'
|
||||
import * as Services from '@standardnotes/services'
|
||||
import {
|
||||
AbstractService,
|
||||
AlertService,
|
||||
ApiServiceEvent,
|
||||
ApplicationStage,
|
||||
ButtonType,
|
||||
DiagnosticInfo,
|
||||
FeaturesClientInterface,
|
||||
FeaturesEvent,
|
||||
FeatureStatus,
|
||||
InternalEventBusInterface,
|
||||
InternalEventHandlerInterface,
|
||||
InternalEventInterface,
|
||||
MetaReceivedData,
|
||||
OfflineSubscriptionEntitlements,
|
||||
SetOfflineFeaturesFunctionResponse,
|
||||
} from './Types'
|
||||
import { DiagnosticInfo } from '@standardnotes/services'
|
||||
StorageKey,
|
||||
} from '@standardnotes/services'
|
||||
|
||||
type GetOfflineSubscriptionDetailsResponse = OfflineSubscriptionEntitlements | ClientDisplayableError
|
||||
|
||||
export class SNFeaturesService
|
||||
extends Services.AbstractService<FeaturesEvent>
|
||||
implements FeaturesClientInterface, Services.InternalEventHandlerInterface
|
||||
extends AbstractService<FeaturesEvent>
|
||||
implements FeaturesClientInterface, InternalEventHandlerInterface
|
||||
{
|
||||
private deinited = false
|
||||
private roles: RoleName[] = []
|
||||
|
|
@ -60,10 +69,10 @@ export class SNFeaturesService
|
|||
private settingsService: SNSettingsService,
|
||||
private userService: UserService,
|
||||
private syncService: SNSyncService,
|
||||
private alertService: Services.AlertService,
|
||||
private alertService: AlertService,
|
||||
private sessionManager: SNSessionManager,
|
||||
private crypto: PureCryptoInterface,
|
||||
protected override internalEventBus: Services.InternalEventBusInterface,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
|
||||
|
|
@ -109,8 +118,8 @@ export class SNFeaturesService
|
|||
})
|
||||
}
|
||||
|
||||
async handleEvent(event: Services.InternalEventInterface): Promise<void> {
|
||||
if (event.type === Services.ApiServiceEvent.MetaReceived) {
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === ApiServiceEvent.MetaReceived) {
|
||||
if (!this.syncService) {
|
||||
this.log('[Features Service] Handling events interrupted. Sync service is not yet initialized.', event)
|
||||
|
||||
|
|
@ -126,7 +135,7 @@ export class SNFeaturesService
|
|||
return
|
||||
}
|
||||
|
||||
const { userUuid, userRoles } = event.payload as Services.MetaReceivedData
|
||||
const { userUuid, userRoles } = event.payload as MetaReceivedData
|
||||
await this.updateRolesAndFetchFeatures(
|
||||
userUuid,
|
||||
userRoles.map((role) => role.name),
|
||||
|
|
@ -134,9 +143,9 @@ export class SNFeaturesService
|
|||
}
|
||||
}
|
||||
|
||||
override async handleApplicationStage(stage: Services.ApplicationStage): Promise<void> {
|
||||
override async handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
await super.handleApplicationStage(stage)
|
||||
if (stage === Services.ApplicationStage.FullSyncCompleted_13) {
|
||||
if (stage === ApplicationStage.FullSyncCompleted_13) {
|
||||
if (!this.hasOnlineSubscription()) {
|
||||
const offlineRepo = this.getOfflineRepo()
|
||||
if (offlineRepo) {
|
||||
|
|
@ -154,7 +163,7 @@ export class SNFeaturesService
|
|||
|
||||
this.enabledExperimentalFeatures.push(identifier)
|
||||
|
||||
void this.storageService.setValue(Services.StorageKey.ExperimentalFeatures, this.enabledExperimentalFeatures)
|
||||
void this.storageService.setValue(StorageKey.ExperimentalFeatures, this.enabledExperimentalFeatures)
|
||||
|
||||
void this.mapRemoteNativeFeaturesToItems([feature])
|
||||
void this.notifyEvent(FeaturesEvent.FeaturesUpdated)
|
||||
|
|
@ -168,7 +177,7 @@ export class SNFeaturesService
|
|||
|
||||
removeFromArray(this.enabledExperimentalFeatures, identifier)
|
||||
|
||||
void this.storageService.setValue(Services.StorageKey.ExperimentalFeatures, this.enabledExperimentalFeatures)
|
||||
void this.storageService.setValue(StorageKey.ExperimentalFeatures, this.enabledExperimentalFeatures)
|
||||
|
||||
const component = this.itemManager
|
||||
.getItems<Models.SNComponent | Models.SNTheme>([ContentType.Component, ContentType.Theme])
|
||||
|
|
@ -248,7 +257,7 @@ export class SNFeaturesService
|
|||
await this.itemManager.setItemToBeDeleted(repo)
|
||||
void this.syncService.sync()
|
||||
}
|
||||
await this.storageService.removeValue(Services.StorageKey.UserFeatures)
|
||||
await this.storageService.removeValue(StorageKey.UserFeatures)
|
||||
}
|
||||
|
||||
private parseOfflineEntitlementsCode(code: string): GetOfflineSubscriptionDetailsResponse | ClientDisplayableError {
|
||||
|
|
@ -322,15 +331,11 @@ export class SNFeaturesService
|
|||
}
|
||||
|
||||
public initializeFromDisk(): void {
|
||||
this.roles = this.storageService.getValue<RoleName[]>(Services.StorageKey.UserRoles, undefined, [])
|
||||
this.roles = this.storageService.getValue<RoleName[]>(StorageKey.UserRoles, undefined, [])
|
||||
|
||||
this.features = this.storageService.getValue(Services.StorageKey.UserFeatures, undefined, [])
|
||||
this.features = this.storageService.getValue(StorageKey.UserFeatures, undefined, [])
|
||||
|
||||
this.enabledExperimentalFeatures = this.storageService.getValue(
|
||||
Services.StorageKey.ExperimentalFeatures,
|
||||
undefined,
|
||||
[],
|
||||
)
|
||||
this.enabledExperimentalFeatures = this.storageService.getValue(StorageKey.ExperimentalFeatures, undefined, [])
|
||||
}
|
||||
|
||||
public async updateRolesAndFetchFeatures(userUuid: UuidString, roles: RoleName[]): Promise<void> {
|
||||
|
|
@ -361,7 +366,7 @@ export class SNFeaturesService
|
|||
if (!arraysEqual(this.roles, roles)) {
|
||||
void this.notifyEvent(FeaturesEvent.UserRolesChanged)
|
||||
}
|
||||
await this.storageService.setValue(Services.StorageKey.UserRoles, this.roles)
|
||||
await this.storageService.setValue(StorageKey.UserRoles, this.roles)
|
||||
}
|
||||
|
||||
public async didDownloadFeatures(features: FeaturesImports.FeatureDescription[]): Promise<void> {
|
||||
|
|
@ -372,7 +377,7 @@ export class SNFeaturesService
|
|||
this.features = features
|
||||
this.completedSuccessfulFeaturesRetrieval = true
|
||||
void this.notifyEvent(FeaturesEvent.FeaturesUpdated)
|
||||
void this.storageService.setValue(Services.StorageKey.UserFeatures, this.features)
|
||||
void this.storageService.setValue(StorageKey.UserFeatures, this.features)
|
||||
|
||||
await this.mapRemoteNativeFeaturesToItems(features)
|
||||
}
|
||||
|
|
@ -607,7 +612,7 @@ export class SNFeaturesService
|
|||
Messages.API_MESSAGE_UNTRUSTED_EXTENSIONS_WARNING,
|
||||
'Install extension from an untrusted source?',
|
||||
'Proceed to install',
|
||||
Services.ButtonType.Danger,
|
||||
ButtonType.Danger,
|
||||
'Cancel',
|
||||
)
|
||||
if (didConfirm) {
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
|
||||
export type SetOfflineFeaturesFunctionResponse = ClientDisplayableError | undefined
|
||||
|
||||
export type OfflineSubscriptionEntitlements = {
|
||||
featuresUrl: string
|
||||
extensionKey: string
|
||||
}
|
||||
|
||||
export enum FeaturesEvent {
|
||||
UserRolesChanged = 'UserRolesChanged',
|
||||
FeaturesUpdated = 'FeaturesUpdated',
|
||||
}
|
||||
|
||||
export enum FeatureStatus {
|
||||
NoUserSubscription = 'NoUserSubscription',
|
||||
NotInCurrentPlan = 'NotInCurrentPlan',
|
||||
InCurrentPlanButExpired = 'InCurrentPlanButExpired',
|
||||
Entitled = 'Entitled',
|
||||
}
|
||||
|
|
@ -1,3 +1 @@
|
|||
export * from './ClientInterface'
|
||||
export * from './FeaturesService'
|
||||
export * from './Types'
|
||||
|
|
|
|||
|
|
@ -3,13 +3,11 @@ import { assert, naturalSort, removeFromArray, UuidGenerator, Uuids } from '@sta
|
|||
import { ItemsKeyMutator, SNItemsKey } from '@standardnotes/encryption'
|
||||
import { PayloadManager } from '../Payloads/PayloadManager'
|
||||
import { TagsToFoldersMigrationApplicator } from '../../Migrations/Applicators/TagsToFolders'
|
||||
import { TransactionalMutation } from './TransactionalMutation'
|
||||
import { UuidString } from '../../Types/UuidString'
|
||||
import * as Models from '@standardnotes/models'
|
||||
import * as Services from '@standardnotes/services'
|
||||
import { ItemsClientInterface } from './ItemsClientInterface'
|
||||
import { PayloadManagerChangeData } from '../Payloads'
|
||||
import { DiagnosticInfo } from '@standardnotes/services'
|
||||
import { DiagnosticInfo, ItemsClientInterface } from '@standardnotes/services'
|
||||
import { ApplicationDisplayOptions } from '@Lib/Application/Options/OptionalOptions'
|
||||
import { CollectionSort } from '@standardnotes/models'
|
||||
|
||||
|
|
@ -578,7 +576,7 @@ export class ItemManager
|
|||
* runs the same mutation on all items.
|
||||
*/
|
||||
public async runTransactionalMutations(
|
||||
transactions: TransactionalMutation[],
|
||||
transactions: Models.TransactionalMutation[],
|
||||
emitSource = Models.PayloadEmitSource.LocalChanged,
|
||||
payloadSourceKey?: string,
|
||||
): Promise<(Models.DecryptedItemInterface | undefined)[]> {
|
||||
|
|
@ -607,7 +605,7 @@ export class ItemManager
|
|||
}
|
||||
|
||||
public async runTransactionalMutation(
|
||||
transaction: TransactionalMutation,
|
||||
transaction: Models.TransactionalMutation,
|
||||
emitSource = Models.PayloadEmitSource.LocalChanged,
|
||||
payloadSourceKey?: string,
|
||||
): Promise<Models.DecryptedItemInterface | undefined> {
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
import * as Models from '@standardnotes/models'
|
||||
import { UuidString } from '../../Types/UuidString'
|
||||
|
||||
export type TransactionalMutation = {
|
||||
itemUuid: UuidString
|
||||
mutate: (mutator: Models.ItemMutator) => void
|
||||
mutationType?: Models.MutationType
|
||||
}
|
||||
|
|
@ -1,3 +1 @@
|
|||
export * from './ItemsClientInterface'
|
||||
export * from './ItemManager'
|
||||
export * from './TransactionalMutation'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { ApplicationEvent } from '../../Application/Event'
|
||||
import { BaseMigration } from '@Lib/Migrations/Base'
|
||||
import { compareSemVersions } from '@Lib/Version'
|
||||
import { lastElement } from '@standardnotes/utils'
|
||||
|
|
@ -7,6 +6,7 @@ import { MigrationServices } from '../../Migrations/MigrationServices'
|
|||
import {
|
||||
RawStorageKey,
|
||||
namespacedKey,
|
||||
ApplicationEvent,
|
||||
ApplicationStage,
|
||||
AbstractService,
|
||||
DiagnosticInfo,
|
||||
|
|
|
|||
|
|
@ -1,183 +0,0 @@
|
|||
import { ContentType } from '@standardnotes/common'
|
||||
import { ChallengeReason, SyncOptions } from '@standardnotes/services'
|
||||
import { TransactionalMutation } from '../Items'
|
||||
import * as Models from '@standardnotes/models'
|
||||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
import { BackupFile } from '@standardnotes/encryption'
|
||||
|
||||
export interface MutatorClientInterface {
|
||||
/**
|
||||
* Inserts the input item by its payload properties, and marks the item as dirty.
|
||||
* A sync is not performed after an item is inserted. This must be handled by the caller.
|
||||
*/
|
||||
insertItem(item: Models.DecryptedItemInterface): Promise<Models.DecryptedItemInterface>
|
||||
|
||||
/**
|
||||
* Mutates a pre-existing item, marks it as dirty, and syncs it
|
||||
*/
|
||||
changeAndSaveItem<M extends Models.DecryptedItemMutator = Models.DecryptedItemMutator>(
|
||||
itemToLookupUuidFor: Models.DecryptedItemInterface,
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps?: boolean,
|
||||
emitSource?: Models.PayloadEmitSource,
|
||||
syncOptions?: SyncOptions,
|
||||
): Promise<Models.DecryptedItemInterface | undefined>
|
||||
|
||||
/**
|
||||
* Mutates pre-existing items, marks them as dirty, and syncs
|
||||
*/
|
||||
changeAndSaveItems<M extends Models.DecryptedItemMutator = Models.DecryptedItemMutator>(
|
||||
itemsToLookupUuidsFor: Models.DecryptedItemInterface[],
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps?: boolean,
|
||||
emitSource?: Models.PayloadEmitSource,
|
||||
syncOptions?: SyncOptions,
|
||||
): Promise<void>
|
||||
|
||||
/**
|
||||
* Mutates a pre-existing item and marks it as dirty. Does not sync changes.
|
||||
*/
|
||||
changeItem<M extends Models.DecryptedItemMutator>(
|
||||
itemToLookupUuidFor: Models.DecryptedItemInterface,
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps?: boolean,
|
||||
): Promise<Models.DecryptedItemInterface | undefined>
|
||||
|
||||
/**
|
||||
* Mutates a pre-existing items and marks them as dirty. Does not sync changes.
|
||||
*/
|
||||
changeItems<M extends Models.DecryptedItemMutator = Models.DecryptedItemMutator>(
|
||||
itemsToLookupUuidsFor: Models.DecryptedItemInterface[],
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps?: boolean,
|
||||
): Promise<(Models.DecryptedItemInterface | undefined)[]>
|
||||
|
||||
/**
|
||||
* Run unique mutations per each item in the array, then only propagate all changes
|
||||
* once all mutations have been run. This differs from `changeItems` in that changeItems
|
||||
* runs the same mutation on all items.
|
||||
*/
|
||||
runTransactionalMutations(
|
||||
transactions: TransactionalMutation[],
|
||||
emitSource?: Models.PayloadEmitSource,
|
||||
payloadSourceKey?: string,
|
||||
): Promise<(Models.DecryptedItemInterface | undefined)[]>
|
||||
|
||||
runTransactionalMutation(
|
||||
transaction: TransactionalMutation,
|
||||
emitSource?: Models.PayloadEmitSource,
|
||||
payloadSourceKey?: string,
|
||||
): Promise<Models.DecryptedItemInterface | undefined>
|
||||
|
||||
protectItems<
|
||||
_M extends Models.DecryptedItemMutator<Models.ItemContent>,
|
||||
I extends Models.DecryptedItemInterface<Models.ItemContent>,
|
||||
>(
|
||||
items: I[],
|
||||
): Promise<I[]>
|
||||
|
||||
unprotectItems<
|
||||
_M extends Models.DecryptedItemMutator<Models.ItemContent>,
|
||||
I extends Models.DecryptedItemInterface<Models.ItemContent>,
|
||||
>(
|
||||
items: I[],
|
||||
reason: ChallengeReason,
|
||||
): Promise<I[] | undefined>
|
||||
|
||||
protectNote(note: Models.SNNote): Promise<Models.SNNote>
|
||||
|
||||
unprotectNote(note: Models.SNNote): Promise<Models.SNNote | undefined>
|
||||
|
||||
protectNotes(notes: Models.SNNote[]): Promise<Models.SNNote[]>
|
||||
|
||||
unprotectNotes(notes: Models.SNNote[]): Promise<Models.SNNote[]>
|
||||
|
||||
protectFile(file: Models.FileItem): Promise<Models.FileItem>
|
||||
|
||||
unprotectFile(file: Models.FileItem): Promise<Models.FileItem | undefined>
|
||||
|
||||
/**
|
||||
* Takes the values of the input item and emits it onto global state.
|
||||
*/
|
||||
mergeItem(
|
||||
item: Models.DecryptedItemInterface,
|
||||
source: Models.PayloadEmitSource,
|
||||
): Promise<Models.DecryptedItemInterface>
|
||||
|
||||
/**
|
||||
* Creates an unmanaged item that can be added later.
|
||||
*/
|
||||
createTemplateItem<
|
||||
C extends Models.ItemContent = Models.ItemContent,
|
||||
I extends Models.DecryptedItemInterface<C> = Models.DecryptedItemInterface<C>,
|
||||
>(
|
||||
contentType: ContentType,
|
||||
content?: C,
|
||||
): I
|
||||
|
||||
/**
|
||||
* @param isUserModified Whether to change the modified date the user
|
||||
* sees of the item.
|
||||
*/
|
||||
setItemNeedsSync(
|
||||
item: Models.DecryptedItemInterface,
|
||||
isUserModified?: boolean,
|
||||
): Promise<Models.DecryptedItemInterface | undefined>
|
||||
|
||||
setItemsNeedsSync(items: Models.DecryptedItemInterface[]): Promise<(Models.DecryptedItemInterface | undefined)[]>
|
||||
|
||||
deleteItem(item: Models.DecryptedItemInterface | Models.EncryptedItemInterface): Promise<void>
|
||||
|
||||
deleteItems(items: (Models.DecryptedItemInterface | Models.EncryptedItemInterface)[]): Promise<void>
|
||||
|
||||
emptyTrash(): Promise<void>
|
||||
|
||||
duplicateItem<T extends Models.DecryptedItemInterface>(item: T, additionalContent?: Partial<T['content']>): Promise<T>
|
||||
|
||||
/**
|
||||
* Migrates any tags containing a '.' character to sa chema-based heirarchy, removing
|
||||
* the dot from the tag's title.
|
||||
*/
|
||||
migrateTagsToFolders(): Promise<unknown>
|
||||
|
||||
/**
|
||||
* Establishes a hierarchical relationship between two tags.
|
||||
*/
|
||||
setTagParent(parentTag: Models.SNTag, childTag: Models.SNTag): Promise<void>
|
||||
|
||||
/**
|
||||
* Remove the tag parent.
|
||||
*/
|
||||
unsetTagParent(childTag: Models.SNTag): Promise<void>
|
||||
|
||||
findOrCreateTag(title: string): Promise<Models.SNTag>
|
||||
|
||||
/** Creates and returns the tag but does not run sync. Callers must perform sync. */
|
||||
createTagOrSmartView(title: string): Promise<Models.SNTag | Models.SmartView>
|
||||
|
||||
/**
|
||||
* Activates or deactivates a component, depending on its
|
||||
* current state, and syncs.
|
||||
*/
|
||||
toggleComponent(component: Models.SNComponent): Promise<void>
|
||||
|
||||
toggleTheme(theme: Models.SNComponent): Promise<void>
|
||||
|
||||
/**
|
||||
* @returns
|
||||
* .affectedItems: Items that were either created or dirtied by this import
|
||||
* .errorCount: The number of items that were not imported due to failure to decrypt.
|
||||
*/
|
||||
importData(
|
||||
data: BackupFile,
|
||||
awaitSync?: boolean,
|
||||
): Promise<
|
||||
| {
|
||||
affectedItems: Models.DecryptedItemInterface[]
|
||||
errorCount: number
|
||||
}
|
||||
| {
|
||||
error: ClientDisplayableError
|
||||
}
|
||||
>
|
||||
}
|
||||
|
|
@ -6,25 +6,42 @@ import {
|
|||
ChallengeValidation,
|
||||
ChallengePrompt,
|
||||
ChallengeReason,
|
||||
MutatorClientInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { BackupFile, EncryptionProvider } from '@standardnotes/encryption'
|
||||
import { EncryptionProvider } from '@standardnotes/encryption'
|
||||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
import { ContentType, ProtocolVersion, compareVersions } from '@standardnotes/common'
|
||||
import { ItemManager, TransactionalMutation } from '../Items'
|
||||
import { MutatorClientInterface } from './MutatorClientInterface'
|
||||
import { ItemManager } from '../Items'
|
||||
import { PayloadManager } from '../Payloads/PayloadManager'
|
||||
import { SNComponentManager } from '../ComponentManager/ComponentManager'
|
||||
import { SNProtectionService } from '../Protection/ProtectionService'
|
||||
import { SNSyncService } from '../Sync'
|
||||
import { Strings } from '../../Strings'
|
||||
import { TagsToFoldersMigrationApplicator } from '@Lib/Migrations/Applicators/TagsToFolders'
|
||||
import * as Models from '@standardnotes/models'
|
||||
import { Challenge, ChallengeService } from '../Challenge'
|
||||
import {
|
||||
BackupFile,
|
||||
BackupFileDecryptedContextualPayload,
|
||||
ComponentContent,
|
||||
CopyPayloadWithContentOverride,
|
||||
CreateDecryptedBackupFileContextPayload,
|
||||
CreateDecryptedMutatorForItem,
|
||||
CreateEncryptedBackupFileContextPayload,
|
||||
DecryptedItemInterface,
|
||||
DecryptedItemMutator,
|
||||
DecryptedPayloadInterface,
|
||||
EncryptedItemInterface,
|
||||
FileItem,
|
||||
isDecryptedPayload,
|
||||
isEncryptedTransferPayload,
|
||||
ItemContent,
|
||||
MutationType,
|
||||
PayloadEmitSource,
|
||||
SmartView,
|
||||
SNComponent,
|
||||
SNNote,
|
||||
SNTag,
|
||||
TransactionalMutation,
|
||||
} from '@standardnotes/models'
|
||||
|
||||
export class MutatorService extends AbstractService implements MutatorClientInterface {
|
||||
|
|
@ -54,106 +71,101 @@ export class MutatorService extends AbstractService implements MutatorClientInte
|
|||
;(this.historyService as unknown) = undefined
|
||||
}
|
||||
|
||||
public async insertItem(item: Models.DecryptedItemInterface): Promise<Models.DecryptedItemInterface> {
|
||||
const mutator = Models.CreateDecryptedMutatorForItem(item, Models.MutationType.UpdateUserTimestamps)
|
||||
public async insertItem(item: DecryptedItemInterface): Promise<DecryptedItemInterface> {
|
||||
const mutator = CreateDecryptedMutatorForItem(item, MutationType.UpdateUserTimestamps)
|
||||
const dirtiedPayload = mutator.getResult()
|
||||
const insertedItem = await this.itemManager.emitItemFromPayload(
|
||||
dirtiedPayload,
|
||||
Models.PayloadEmitSource.LocalInserted,
|
||||
)
|
||||
const insertedItem = await this.itemManager.emitItemFromPayload(dirtiedPayload, PayloadEmitSource.LocalInserted)
|
||||
return insertedItem
|
||||
}
|
||||
|
||||
public async changeAndSaveItem<M extends Models.DecryptedItemMutator = Models.DecryptedItemMutator>(
|
||||
itemToLookupUuidFor: Models.DecryptedItemInterface,
|
||||
public async changeAndSaveItem<M extends DecryptedItemMutator = DecryptedItemMutator>(
|
||||
itemToLookupUuidFor: DecryptedItemInterface,
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps = true,
|
||||
emitSource?: Models.PayloadEmitSource,
|
||||
emitSource?: PayloadEmitSource,
|
||||
syncOptions?: SyncOptions,
|
||||
): Promise<Models.DecryptedItemInterface | undefined> {
|
||||
): Promise<DecryptedItemInterface | undefined> {
|
||||
await this.itemManager.changeItems(
|
||||
[itemToLookupUuidFor],
|
||||
mutate,
|
||||
updateTimestamps ? Models.MutationType.UpdateUserTimestamps : Models.MutationType.NoUpdateUserTimestamps,
|
||||
updateTimestamps ? MutationType.UpdateUserTimestamps : MutationType.NoUpdateUserTimestamps,
|
||||
emitSource,
|
||||
)
|
||||
await this.syncService.sync(syncOptions)
|
||||
return this.itemManager.findItem(itemToLookupUuidFor.uuid)
|
||||
}
|
||||
|
||||
public async changeAndSaveItems<M extends Models.DecryptedItemMutator = Models.DecryptedItemMutator>(
|
||||
itemsToLookupUuidsFor: Models.DecryptedItemInterface[],
|
||||
public async changeAndSaveItems<M extends DecryptedItemMutator = DecryptedItemMutator>(
|
||||
itemsToLookupUuidsFor: DecryptedItemInterface[],
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps = true,
|
||||
emitSource?: Models.PayloadEmitSource,
|
||||
emitSource?: PayloadEmitSource,
|
||||
syncOptions?: SyncOptions,
|
||||
): Promise<void> {
|
||||
await this.itemManager.changeItems(
|
||||
itemsToLookupUuidsFor,
|
||||
mutate,
|
||||
updateTimestamps ? Models.MutationType.UpdateUserTimestamps : Models.MutationType.NoUpdateUserTimestamps,
|
||||
updateTimestamps ? MutationType.UpdateUserTimestamps : MutationType.NoUpdateUserTimestamps,
|
||||
emitSource,
|
||||
)
|
||||
await this.syncService.sync(syncOptions)
|
||||
}
|
||||
|
||||
public async changeItem<M extends Models.DecryptedItemMutator>(
|
||||
itemToLookupUuidFor: Models.DecryptedItemInterface,
|
||||
public async changeItem<M extends DecryptedItemMutator>(
|
||||
itemToLookupUuidFor: DecryptedItemInterface,
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps = true,
|
||||
): Promise<Models.DecryptedItemInterface | undefined> {
|
||||
): Promise<DecryptedItemInterface | undefined> {
|
||||
await this.itemManager.changeItems(
|
||||
[itemToLookupUuidFor],
|
||||
mutate,
|
||||
updateTimestamps ? Models.MutationType.UpdateUserTimestamps : Models.MutationType.NoUpdateUserTimestamps,
|
||||
updateTimestamps ? MutationType.UpdateUserTimestamps : MutationType.NoUpdateUserTimestamps,
|
||||
)
|
||||
return this.itemManager.findItem(itemToLookupUuidFor.uuid)
|
||||
}
|
||||
|
||||
public async changeItems<M extends Models.DecryptedItemMutator = Models.DecryptedItemMutator>(
|
||||
itemsToLookupUuidsFor: Models.DecryptedItemInterface[],
|
||||
public async changeItems<M extends DecryptedItemMutator = DecryptedItemMutator>(
|
||||
itemsToLookupUuidsFor: DecryptedItemInterface[],
|
||||
mutate: (mutator: M) => void,
|
||||
updateTimestamps = true,
|
||||
): Promise<(Models.DecryptedItemInterface | undefined)[]> {
|
||||
): Promise<(DecryptedItemInterface | undefined)[]> {
|
||||
return this.itemManager.changeItems(
|
||||
itemsToLookupUuidsFor,
|
||||
mutate,
|
||||
updateTimestamps ? Models.MutationType.UpdateUserTimestamps : Models.MutationType.NoUpdateUserTimestamps,
|
||||
updateTimestamps ? MutationType.UpdateUserTimestamps : MutationType.NoUpdateUserTimestamps,
|
||||
)
|
||||
}
|
||||
|
||||
public async runTransactionalMutations(
|
||||
transactions: TransactionalMutation[],
|
||||
emitSource = Models.PayloadEmitSource.LocalChanged,
|
||||
emitSource = PayloadEmitSource.LocalChanged,
|
||||
payloadSourceKey?: string,
|
||||
): Promise<(Models.DecryptedItemInterface | undefined)[]> {
|
||||
): Promise<(DecryptedItemInterface | undefined)[]> {
|
||||
return this.itemManager.runTransactionalMutations(transactions, emitSource, payloadSourceKey)
|
||||
}
|
||||
|
||||
public async runTransactionalMutation(
|
||||
transaction: TransactionalMutation,
|
||||
emitSource = Models.PayloadEmitSource.LocalChanged,
|
||||
emitSource = PayloadEmitSource.LocalChanged,
|
||||
payloadSourceKey?: string,
|
||||
): Promise<Models.DecryptedItemInterface | undefined> {
|
||||
): Promise<DecryptedItemInterface | undefined> {
|
||||
return this.itemManager.runTransactionalMutation(transaction, emitSource, payloadSourceKey)
|
||||
}
|
||||
|
||||
async protectItems<M extends Models.DecryptedItemMutator, I extends Models.DecryptedItemInterface>(
|
||||
items: I[],
|
||||
): Promise<I[]> {
|
||||
async protectItems<M extends DecryptedItemMutator, I extends DecryptedItemInterface>(items: I[]): Promise<I[]> {
|
||||
const protectedItems = await this.itemManager.changeItems<M, I>(
|
||||
items,
|
||||
(mutator) => {
|
||||
mutator.protected = true
|
||||
},
|
||||
Models.MutationType.NoUpdateUserTimestamps,
|
||||
MutationType.NoUpdateUserTimestamps,
|
||||
)
|
||||
|
||||
void this.syncService.sync()
|
||||
return protectedItems
|
||||
}
|
||||
|
||||
async unprotectItems<M extends Models.DecryptedItemMutator, I extends Models.DecryptedItemInterface>(
|
||||
async unprotectItems<M extends DecryptedItemMutator, I extends DecryptedItemInterface>(
|
||||
items: I[],
|
||||
reason: ChallengeReason,
|
||||
): Promise<I[] | undefined> {
|
||||
|
|
@ -166,74 +178,69 @@ export class MutatorService extends AbstractService implements MutatorClientInte
|
|||
(mutator) => {
|
||||
mutator.protected = false
|
||||
},
|
||||
Models.MutationType.NoUpdateUserTimestamps,
|
||||
MutationType.NoUpdateUserTimestamps,
|
||||
)
|
||||
|
||||
void this.syncService.sync()
|
||||
return unprotectedItems
|
||||
}
|
||||
|
||||
public async protectNote(note: Models.SNNote): Promise<Models.SNNote> {
|
||||
public async protectNote(note: SNNote): Promise<SNNote> {
|
||||
const result = await this.protectItems([note])
|
||||
return result[0]
|
||||
}
|
||||
|
||||
public async unprotectNote(note: Models.SNNote): Promise<Models.SNNote | undefined> {
|
||||
public async unprotectNote(note: SNNote): Promise<SNNote | undefined> {
|
||||
const result = await this.unprotectItems([note], ChallengeReason.UnprotectNote)
|
||||
return result ? result[0] : undefined
|
||||
}
|
||||
|
||||
public async protectNotes(notes: Models.SNNote[]): Promise<Models.SNNote[]> {
|
||||
public async protectNotes(notes: SNNote[]): Promise<SNNote[]> {
|
||||
return this.protectItems(notes)
|
||||
}
|
||||
|
||||
public async unprotectNotes(notes: Models.SNNote[]): Promise<Models.SNNote[]> {
|
||||
public async unprotectNotes(notes: SNNote[]): Promise<SNNote[]> {
|
||||
const results = await this.unprotectItems(notes, ChallengeReason.UnprotectNote)
|
||||
return results || []
|
||||
}
|
||||
|
||||
async protectFile(file: Models.FileItem): Promise<Models.FileItem> {
|
||||
async protectFile(file: FileItem): Promise<FileItem> {
|
||||
const result = await this.protectItems([file])
|
||||
return result[0]
|
||||
}
|
||||
|
||||
async unprotectFile(file: Models.FileItem): Promise<Models.FileItem | undefined> {
|
||||
async unprotectFile(file: FileItem): Promise<FileItem | undefined> {
|
||||
const result = await this.unprotectItems([file], ChallengeReason.UnprotectFile)
|
||||
return result ? result[0] : undefined
|
||||
}
|
||||
|
||||
public async mergeItem(
|
||||
item: Models.DecryptedItemInterface,
|
||||
source: Models.PayloadEmitSource,
|
||||
): Promise<Models.DecryptedItemInterface> {
|
||||
public async mergeItem(item: DecryptedItemInterface, source: PayloadEmitSource): Promise<DecryptedItemInterface> {
|
||||
return this.itemManager.emitItemFromPayload(item.payloadRepresentation(), source)
|
||||
}
|
||||
|
||||
public createTemplateItem<
|
||||
C extends Models.ItemContent = Models.ItemContent,
|
||||
I extends Models.DecryptedItemInterface<C> = Models.DecryptedItemInterface<C>,
|
||||
C extends ItemContent = ItemContent,
|
||||
I extends DecryptedItemInterface<C> = DecryptedItemInterface<C>,
|
||||
>(contentType: ContentType, content?: C): I {
|
||||
return this.itemManager.createTemplateItem(contentType, content)
|
||||
}
|
||||
|
||||
public async setItemNeedsSync(
|
||||
item: Models.DecryptedItemInterface,
|
||||
item: DecryptedItemInterface,
|
||||
updateTimestamps = false,
|
||||
): Promise<Models.DecryptedItemInterface | undefined> {
|
||||
): Promise<DecryptedItemInterface | undefined> {
|
||||
return this.itemManager.setItemDirty(item, updateTimestamps)
|
||||
}
|
||||
|
||||
public async setItemsNeedsSync(
|
||||
items: Models.DecryptedItemInterface[],
|
||||
): Promise<(Models.DecryptedItemInterface | undefined)[]> {
|
||||
public async setItemsNeedsSync(items: DecryptedItemInterface[]): Promise<(DecryptedItemInterface | undefined)[]> {
|
||||
return this.itemManager.setItemsDirty(items)
|
||||
}
|
||||
|
||||
public async deleteItem(item: Models.DecryptedItemInterface | Models.EncryptedItemInterface): Promise<void> {
|
||||
public async deleteItem(item: DecryptedItemInterface | EncryptedItemInterface): Promise<void> {
|
||||
return this.deleteItems([item])
|
||||
}
|
||||
|
||||
public async deleteItems(items: (Models.DecryptedItemInterface | Models.EncryptedItemInterface)[]): Promise<void> {
|
||||
public async deleteItems(items: (DecryptedItemInterface | EncryptedItemInterface)[]): Promise<void> {
|
||||
await this.itemManager.setItemsToBeDeleted(items)
|
||||
await this.syncService.sync()
|
||||
}
|
||||
|
|
@ -243,7 +250,7 @@ export class MutatorService extends AbstractService implements MutatorClientInte
|
|||
await this.syncService.sync()
|
||||
}
|
||||
|
||||
public duplicateItem<T extends Models.DecryptedItemInterface>(
|
||||
public duplicateItem<T extends DecryptedItemInterface>(
|
||||
item: T,
|
||||
additionalContent?: Partial<T['content']>,
|
||||
): Promise<T> {
|
||||
|
|
@ -257,29 +264,29 @@ export class MutatorService extends AbstractService implements MutatorClientInte
|
|||
return this.syncService.sync()
|
||||
}
|
||||
|
||||
public async setTagParent(parentTag: Models.SNTag, childTag: Models.SNTag): Promise<void> {
|
||||
public async setTagParent(parentTag: SNTag, childTag: SNTag): Promise<void> {
|
||||
await this.itemManager.setTagParent(parentTag, childTag)
|
||||
}
|
||||
|
||||
public async unsetTagParent(childTag: Models.SNTag): Promise<void> {
|
||||
public async unsetTagParent(childTag: SNTag): Promise<void> {
|
||||
await this.itemManager.unsetTagParent(childTag)
|
||||
}
|
||||
|
||||
public async findOrCreateTag(title: string): Promise<Models.SNTag> {
|
||||
public async findOrCreateTag(title: string): Promise<SNTag> {
|
||||
return this.itemManager.findOrCreateTagByTitle(title)
|
||||
}
|
||||
|
||||
/** Creates and returns the tag but does not run sync. Callers must perform sync. */
|
||||
public async createTagOrSmartView(title: string): Promise<Models.SNTag | Models.SmartView> {
|
||||
public async createTagOrSmartView(title: string): Promise<SNTag | SmartView> {
|
||||
return this.itemManager.createTagOrSmartView(title)
|
||||
}
|
||||
|
||||
public async toggleComponent(component: Models.SNComponent): Promise<void> {
|
||||
public async toggleComponent(component: SNComponent): Promise<void> {
|
||||
await this.componentManager.toggleComponent(component.uuid)
|
||||
await this.syncService.sync()
|
||||
}
|
||||
|
||||
public async toggleTheme(theme: Models.SNComponent): Promise<void> {
|
||||
public async toggleTheme(theme: SNComponent): Promise<void> {
|
||||
await this.componentManager.toggleTheme(theme.uuid)
|
||||
await this.syncService.sync()
|
||||
}
|
||||
|
|
@ -289,7 +296,7 @@ export class MutatorService extends AbstractService implements MutatorClientInte
|
|||
awaitSync = false,
|
||||
): Promise<
|
||||
| {
|
||||
affectedItems: Models.DecryptedItemInterface[]
|
||||
affectedItems: DecryptedItemInterface[]
|
||||
errorCount: number
|
||||
}
|
||||
| {
|
||||
|
|
@ -342,7 +349,7 @@ export class MutatorService extends AbstractService implements MutatorClientInte
|
|||
if (isEncryptedTransferPayload(item)) {
|
||||
return CreateEncryptedBackupFileContextPayload(item)
|
||||
} else {
|
||||
return CreateDecryptedBackupFileContextPayload(item as Models.BackupFileDecryptedContextualPayload)
|
||||
return CreateDecryptedBackupFileContextPayload(item as BackupFileDecryptedContextualPayload)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -355,9 +362,9 @@ export class MutatorService extends AbstractService implements MutatorClientInte
|
|||
const validPayloads = decryptedPayloadsOrError.filter(isDecryptedPayload).map((payload) => {
|
||||
/* Don't want to activate any components during import process in
|
||||
* case of exceptions breaking up the import proccess */
|
||||
if (payload.content_type === ContentType.Component && (payload.content as Models.ComponentContent).active) {
|
||||
const typedContent = payload as Models.DecryptedPayloadInterface<Models.ComponentContent>
|
||||
return Models.CopyPayloadWithContentOverride(typedContent, {
|
||||
if (payload.content_type === ContentType.Component && (payload.content as ComponentContent).active) {
|
||||
const typedContent = payload as DecryptedPayloadInterface<ComponentContent>
|
||||
return CopyPayloadWithContentOverride(typedContent, {
|
||||
active: false,
|
||||
})
|
||||
} else {
|
||||
|
|
@ -376,7 +383,7 @@ export class MutatorService extends AbstractService implements MutatorClientInte
|
|||
await promise
|
||||
}
|
||||
|
||||
const affectedItems = this.itemManager.findItems(affectedUuids) as Models.DecryptedItemInterface[]
|
||||
const affectedItems = this.itemManager.findItems(affectedUuids) as DecryptedItemInterface[]
|
||||
|
||||
return {
|
||||
affectedItems: affectedItems,
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
export * from './MutatorClientInterface'
|
||||
export * from './MutatorService'
|
||||
|
|
|
|||
3
packages/ui-services/.eslintignore
Normal file
3
packages/ui-services/.eslintignore
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
dist
|
||||
coverage
|
||||
9
packages/ui-services/.eslintrc
Normal file
9
packages/ui-services/.eslintrc
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"extends": "../../.eslintrc",
|
||||
"parserOptions": {
|
||||
"project": "./linter.tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": ["warn", { "ignoreRestArgs": true }]
|
||||
}
|
||||
}
|
||||
1
packages/ui-services/.gitignore
vendored
Normal file
1
packages/ui-services/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
dist
|
||||
11
packages/ui-services/jest.config.js
Normal file
11
packages/ui-services/jest.config.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const base = require('../../node_modules/@standardnotes/config/src/jest.json');
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: 'tsconfig.json',
|
||||
},
|
||||
}
|
||||
};
|
||||
4
packages/ui-services/linter.tsconfig.json
Normal file
4
packages/ui-services/linter.tsconfig.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
41
packages/ui-services/package.json
Normal file
41
packages/ui-services/package.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "@standardnotes/ui-services",
|
||||
"version": "1.0.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
"description": "UI Services for Standard Notes clients",
|
||||
"main": "dist/index.js",
|
||||
"author": "Standard Notes",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
"prestart": "yarn clean",
|
||||
"start": "tsc -p tsconfig.json --watch",
|
||||
"prebuild": "yarn clean",
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"test:unit": "jest spec --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/filepicker": "workspace:^",
|
||||
"@standardnotes/services": "workspace:^",
|
||||
"@standardnotes/snjs": "workspace:^",
|
||||
"@standardnotes/styles": "workspace:^",
|
||||
"@standardnotes/toast": "workspace:^",
|
||||
"@standardnotes/utils": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^28.1.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
||||
"@typescript-eslint/parser": "^5.12.1",
|
||||
"eslint-plugin-prettier": "*",
|
||||
"jest": "^28.1.2",
|
||||
"ts-jest": "^28.0.5"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
import { AlertService, ButtonType } from '@standardnotes/services'
|
||||
import { sanitizeHtmlString } from '@standardnotes/utils'
|
||||
import { SKAlert } from '@standardnotes/styles'
|
||||
|
||||
|
|
@ -65,37 +64,3 @@ export function alertDialog({
|
|||
alert.present()
|
||||
})
|
||||
}
|
||||
|
||||
export class WebAlertService extends AlertService {
|
||||
alert(text: string, title?: string, closeButtonText?: string) {
|
||||
return alertDialog({ text, title, closeButtonText })
|
||||
}
|
||||
|
||||
confirm(
|
||||
text: string,
|
||||
title?: string,
|
||||
confirmButtonText?: string,
|
||||
confirmButtonType?: ButtonType,
|
||||
cancelButtonText?: string,
|
||||
): Promise<boolean> {
|
||||
return confirmDialog({
|
||||
text,
|
||||
title,
|
||||
confirmButtonText,
|
||||
cancelButtonText,
|
||||
confirmButtonStyle: confirmButtonType === ButtonType.Danger ? 'danger' : 'info',
|
||||
})
|
||||
}
|
||||
|
||||
blockingDialog(text: string, title?: string) {
|
||||
const alert = new SKAlert({
|
||||
title: title && sanitizeHtmlString(title),
|
||||
text: sanitizeHtmlString(text),
|
||||
buttons: [],
|
||||
})
|
||||
alert.present()
|
||||
return () => {
|
||||
alert.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
38
packages/ui-services/src/Alert/WebAlertService.ts
Normal file
38
packages/ui-services/src/Alert/WebAlertService.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { AlertService, ButtonType } from '@standardnotes/services'
|
||||
import { sanitizeHtmlString } from '@standardnotes/utils'
|
||||
import { SKAlert } from '@standardnotes/styles'
|
||||
import { alertDialog, confirmDialog } from './Functions'
|
||||
|
||||
export class WebAlertService extends AlertService {
|
||||
alert(text: string, title?: string, closeButtonText?: string) {
|
||||
return alertDialog({ text, title, closeButtonText })
|
||||
}
|
||||
|
||||
confirm(
|
||||
text: string,
|
||||
title?: string,
|
||||
confirmButtonText?: string,
|
||||
confirmButtonType?: ButtonType,
|
||||
cancelButtonText?: string,
|
||||
): Promise<boolean> {
|
||||
return confirmDialog({
|
||||
text,
|
||||
title,
|
||||
confirmButtonText,
|
||||
cancelButtonText,
|
||||
confirmButtonStyle: confirmButtonType === ButtonType.Danger ? 'danger' : 'info',
|
||||
})
|
||||
}
|
||||
|
||||
blockingDialog(text: string, title?: string) {
|
||||
const alert = new SKAlert({
|
||||
title: title && sanitizeHtmlString(title),
|
||||
text: sanitizeHtmlString(text),
|
||||
buttons: [],
|
||||
})
|
||||
alert.present()
|
||||
return () => {
|
||||
alert.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
import { WebApplication } from '@/Application/Application'
|
||||
import { parseFileName } from '@standardnotes/filepicker'
|
||||
import {
|
||||
BackupFile,
|
||||
|
|
@ -6,6 +5,7 @@ import {
|
|||
BackupFileDecryptedContextualPayload,
|
||||
NoteContent,
|
||||
EncryptedItemInterface,
|
||||
SNApplication,
|
||||
} from '@standardnotes/snjs'
|
||||
|
||||
function sanitizeFileName(name: string): string {
|
||||
|
|
@ -27,10 +27,10 @@ type ZippableData = {
|
|||
type ObjectURL = string
|
||||
|
||||
export class ArchiveManager {
|
||||
private readonly application: WebApplication
|
||||
private readonly application: SNApplication
|
||||
private textFile?: string
|
||||
|
||||
constructor(application: WebApplication) {
|
||||
constructor(application: SNApplication) {
|
||||
this.application = application
|
||||
}
|
||||
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
import { useCallback, useState } from 'react'
|
||||
|
||||
export enum StorageKey {
|
||||
AnonymousUserId = 'AnonymousUserId',
|
||||
ShowBetaWarning = 'ShowBetaWarning',
|
||||
|
|
@ -26,19 +24,3 @@ export const storage = {
|
|||
localStorage.removeItem(key)
|
||||
},
|
||||
}
|
||||
|
||||
type LocalStorageHookReturnType<Key extends StorageKey> = [StorageValue[Key] | null, (value: StorageValue[Key]) => void]
|
||||
|
||||
export const useLocalStorageItem = <Key extends StorageKey>(key: Key): LocalStorageHookReturnType<Key> => {
|
||||
const [value, setValue] = useState(() => storage.get(key))
|
||||
|
||||
const set = useCallback(
|
||||
(value: StorageValue[Key]) => {
|
||||
storage.set(key, value)
|
||||
setValue(value)
|
||||
},
|
||||
[key],
|
||||
)
|
||||
|
||||
return [value, set]
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
import { WebApplication } from '@/Application/Application'
|
||||
import {
|
||||
StorageValueModes,
|
||||
ApplicationService,
|
||||
|
|
@ -13,6 +12,7 @@ import {
|
|||
InternalEventBus,
|
||||
PayloadEmitSource,
|
||||
LocalStorageDecryptedContextualPayload,
|
||||
WebApplicationInterface,
|
||||
} from '@standardnotes/snjs'
|
||||
import { dismissToast, ToastType, addTimedToast } from '@standardnotes/toast'
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ export class ThemeManager extends ApplicationService {
|
|||
private unregisterStream!: () => void
|
||||
private lastUseDeviceThemeSettings = false
|
||||
|
||||
constructor(application: WebApplication) {
|
||||
constructor(application: WebApplicationInterface) {
|
||||
super(application, new InternalEventBus())
|
||||
this.colorSchemeEventHandler = this.colorSchemeEventHandler.bind(this)
|
||||
}
|
||||
|
|
@ -80,7 +80,7 @@ export class ThemeManager extends ApplicationService {
|
|||
}
|
||||
|
||||
get webApplication() {
|
||||
return this.application as WebApplication
|
||||
return this.application as WebApplicationInterface
|
||||
}
|
||||
|
||||
override deinit() {
|
||||
7
packages/ui-services/src/index.ts
Normal file
7
packages/ui-services/src/index.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export * from './Alert/Functions'
|
||||
export * from './Alert/WebAlertService'
|
||||
export * from './Archive/ArchiveManager'
|
||||
export * from './IO/IOService'
|
||||
export * from './Security/AutolockService'
|
||||
export * from './Storage/LocalStorage'
|
||||
export * from './Theme/ThemeManager'
|
||||
14
packages/ui-services/tsconfig.json
Normal file
14
packages/ui-services/tsconfig.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"extends": "../../node_modules/@standardnotes/config/src/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"jsx": "react-jsx",
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"references": [],
|
||||
"exclude": ["**/*.spec.ts", "dist", "node_modules"]
|
||||
}
|
||||
|
|
@ -82,6 +82,7 @@
|
|||
"@standardnotes/snjs": "workspace:*",
|
||||
"@standardnotes/styles": "workspace:*",
|
||||
"@standardnotes/toast": "workspace:*",
|
||||
"@standardnotes/ui-services": "workspace:^",
|
||||
"@zip.js/zip.js": "^2.4.10",
|
||||
"mobx": "^6.5.0",
|
||||
"mobx-react-lite": "^3.3.0",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
import { WebCrypto } from '@/Application/Crypto'
|
||||
import { WebAlertService } from '@/Services/AlertService'
|
||||
import { ArchiveManager } from '@/Services/ArchiveManager'
|
||||
import { AutolockService } from '@/Services/AutolockService'
|
||||
import { DesktopManager } from '@/Services/DesktopManager'
|
||||
import { IOService } from '@/Services/IOService'
|
||||
import { ThemeManager } from '@/Services/ThemeManager'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { WebOrDesktopDevice } from '@/Application/Device/WebOrDesktopDevice'
|
||||
import {
|
||||
DeinitSource,
|
||||
|
|
@ -21,11 +15,14 @@ import {
|
|||
SNTag,
|
||||
ContentType,
|
||||
DecryptedItemInterface,
|
||||
WebAppEvent,
|
||||
WebApplicationInterface,
|
||||
} from '@standardnotes/snjs'
|
||||
import { makeObservable, observable } from 'mobx'
|
||||
import { PanelResizedData } from '@/Types/PanelResizedData'
|
||||
import { WebAppEvent } from './WebAppEvent'
|
||||
import { isDesktopApplication } from '@/Utils'
|
||||
import { DesktopManager } from './Device/DesktopManager'
|
||||
import { ArchiveManager, AutolockService, IOService, ThemeManager, WebAlertService } from '@standardnotes/ui-services'
|
||||
|
||||
type WebServices = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
|
|
@ -38,7 +35,7 @@ type WebServices = {
|
|||
|
||||
export type WebEventObserver = (event: WebAppEvent, data?: unknown) => void
|
||||
|
||||
export class WebApplication extends SNApplication {
|
||||
export class WebApplication extends SNApplication implements WebApplicationInterface {
|
||||
private webServices!: WebServices
|
||||
private webEventObservers: WebEventObserver[] = []
|
||||
public itemControllerGroup: ItemGroupController
|
||||
|
|
|
|||
|
|
@ -6,14 +6,12 @@ import {
|
|||
InternalEventBus,
|
||||
isDesktopDevice,
|
||||
} from '@standardnotes/snjs'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ArchiveManager, IOService, AutolockService, ThemeManager } from '@standardnotes/ui-services'
|
||||
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { getPlatform, isDesktopApplication } from '@/Utils'
|
||||
import { ArchiveManager } from '@/Services/ArchiveManager'
|
||||
import { DesktopManager } from '@/Services/DesktopManager'
|
||||
import { IOService } from '@/Services/IOService'
|
||||
import { AutolockService } from '@/Services/AutolockService'
|
||||
import { ThemeManager } from '@/Services/ThemeManager'
|
||||
import { WebOrDesktopDevice } from '@/Application/Device/WebOrDesktopDevice'
|
||||
import { DesktopManager } from './Device/DesktopManager'
|
||||
|
||||
const createApplication = (
|
||||
descriptor: ApplicationDescriptor,
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import {
|
|||
assert,
|
||||
DesktopClientRequiresWebMethods,
|
||||
DesktopDeviceInterface,
|
||||
WebApplicationInterface,
|
||||
WebAppEvent,
|
||||
} from '@standardnotes/snjs'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { WebAppEvent } from '@/Application/WebAppEvent'
|
||||
|
||||
export class DesktopManager
|
||||
extends ApplicationService
|
||||
|
|
@ -27,12 +27,12 @@ export class DesktopManager
|
|||
dataLoaded = false
|
||||
lastSearchedText?: string
|
||||
|
||||
constructor(application: WebApplication, private device: DesktopDeviceInterface) {
|
||||
constructor(application: WebApplicationInterface, private device: DesktopDeviceInterface) {
|
||||
super(application, new InternalEventBus())
|
||||
}
|
||||
|
||||
get webApplication() {
|
||||
return this.application as WebApplication
|
||||
return this.application as WebApplicationInterface
|
||||
}
|
||||
|
||||
override deinit() {
|
||||
|
|
@ -80,7 +80,7 @@ export class DesktopManager
|
|||
.catch(console.error)
|
||||
}
|
||||
|
||||
registerUpdateObserver(callback: (component: SNComponent) => void) {
|
||||
registerUpdateObserver(callback: (component: SNComponent) => void): () => void {
|
||||
const observer = {
|
||||
callback: callback,
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { ApplicationEvent } from '@standardnotes/snjs'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { autorun, IReactionDisposer, IReactionPublic } from 'mobx'
|
||||
import { Component } from 'react'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { useCallback, FunctionComponent, KeyboardEventHandler } from 'react'
|
||||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { WebApplication } from '@/Application/Application'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { ChangeEventHandler, FunctionComponent, useCallback, useEffect, useState } from 'react'
|
||||
import Checkbox from '@/Components/Checkbox/Checkbox'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { STRING_NON_MATCHING_PASSWORDS } from '@/Constants/Strings'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, KeyboardEventHandler, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { AccountMenuPane } from './AccountMenuPane'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { WebApplication } from '@/Application/Application'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, KeyboardEventHandler, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { AccountMenuPane } from './AccountMenuPane'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { WebApplication } from '@/Application/Application'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import { SyncQueueStrategy } from '@standardnotes/snjs'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { WebApplication } from '@/Application/Application'
|
||||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useState } from 'react'
|
||||
import { AccountMenuPane } from './AccountMenuPane'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { WebApplication } from '@/Application/Application'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { isDev } from '@/Utils'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import React, { FunctionComponent, KeyboardEventHandler, useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { User as UserType } from '@standardnotes/snjs'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Icon from '@/Components/Icon/Icon'
|
||||
import MenuItem from '@/Components/Menu/MenuItem'
|
||||
import { MenuItemType } from '@/Components/Menu/MenuItemType'
|
||||
import { KeyboardKey } from '@/Services/IOService'
|
||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
import { ApplicationDescriptor } from '@standardnotes/snjs'
|
||||
import {
|
||||
ChangeEventHandler,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { ApplicationDescriptor, ApplicationGroupEvent, ButtonType } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useEffect, useState } from 'react'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
|
||||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useRef, useState } from 'react'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||
import { getPlatformString, getWindowUrlParams } from '@/Utils'
|
||||
import { ApplicationEvent, Challenge, removeFromArray } from '@standardnotes/snjs'
|
||||
import { ApplicationEvent, Challenge, removeFromArray, WebAppEvent } from '@standardnotes/snjs'
|
||||
import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/Constants/Constants'
|
||||
import { alertDialog } from '@/Services/AlertService'
|
||||
import { alertDialog } from '@standardnotes/ui-services'
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import { WebAppEvent } from '@/Application/WebAppEvent'
|
||||
import Navigation from '@/Components/Navigation/Navigation'
|
||||
import NoteGroupView from '@/Components/NoteGroupView/NoteGroupView'
|
||||
import Footer from '@/Components/Footer/Footer'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/Constants/Constants'
|
||||
import { KeyboardKey } from '@/Services/IOService'
|
||||
import { KeyboardKey } from '@standardnotes/ui-services'
|
||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||
import { FileItem } from '@standardnotes/snjs'
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import Icon from '@/Components/Icon/Icon'
|
|||
import ChallengeModalPrompt from './ChallengePrompt'
|
||||
import LockscreenWorkspaceSwitcher from './LockscreenWorkspaceSwitcher'
|
||||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { ChallengeModalValues } from './ChallengeModalValues'
|
||||
|
||||
type Props = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ApplicationGroup } from '@/Application/ApplicationGroup'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { FunctionComponent, useCallback, useRef, useState } from 'react'
|
||||
import WorkspaceSwitcherMenu from '@/Components/AccountMenu/WorkspaceSwitcher/WorkspaceSwitcherMenu'
|
||||
import Button from '@/Components/Button/Button'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { WebApplication } from '@/Application/Application'
|
||||
import { ViewControllerManager } from '@/Services/ViewControllerManager'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent, useCallback, useRef, useState } from 'react'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
FeatureStatus,
|
||||
SNComponent,
|
||||
dateToLocalizedString,
|
||||
ComponentViewer,
|
||||
ComponentViewerInterface,
|
||||
ComponentViewerEvent,
|
||||
ComponentViewerError,
|
||||
} from '@standardnotes/snjs'
|
||||
|
|
@ -19,8 +19,8 @@ import { openSubscriptionDashboard } from '@/Utils/ManageSubscription'
|
|||
|
||||
interface IProps {
|
||||
application: WebApplication
|
||||
componentViewer: ComponentViewer
|
||||
requestReload?: (viewer: ComponentViewer, force?: boolean) => void
|
||||
componentViewer: ComponentViewerInterface
|
||||
requestReload?: (viewer: ComponentViewerInterface, force?: boolean) => void
|
||||
onLoad?: (component: SNComponent) => void
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue