Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2 changes: 2 additions & 0 deletions packages/services/src/Domain/Item/ItemManagerInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ export interface ItemManagerInterface extends AbstractService {
getDisplayableFiles(): FileItem[]
setVaultDisplayOptions(options: VaultDisplayOptions): void
numberOfNotesWithConflicts(): number
/** Returns all notes, files, tags and views */
getInteractableItems(): DecryptedItemInterface[]
getDisplayableNotes(): SNNote[]
getDisplayableNotesAndFiles(): (SNNote | FileItem)[]
setPrimaryItemDisplayOptions(options: NotesAndFilesDisplayControllerOptions): void
Expand Down
11 changes: 11 additions & 0 deletions packages/snjs/lib/Services/Items/ItemManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,17 @@ export class ItemManager extends Services.AbstractService implements Services.It
this.itemCounter.setVaultDisplayOptions(options)
}

public getInteractableItems(): Models.DecryptedItemInterface[] {
return (this.systemSmartViews as Models.DecryptedItemInterface[]).concat(
this.collection.all([
ContentType.TYPES.Note,
ContentType.TYPES.File,
ContentType.TYPES.Tag,
ContentType.TYPES.SmartView,
]) as Models.DecryptedItemInterface[],
)
}

public getDisplayableNotes(): Models.SNNote[] {
assert(this.navigationDisplayController.contentTypes.length === 2)

Expand Down
1 change: 1 addition & 0 deletions packages/ui-services/src/Keyboard/KeyboardCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ export const OPEN_PREFERENCES_COMMAND = createKeyboardCommand('OPEN_PREFERENCES_
export const CHANGE_EDITOR_WIDTH_COMMAND = createKeyboardCommand('CHANGE_EDITOR_WIDTH_COMMAND')

export const TOGGLE_KEYBOARD_SHORTCUTS_MODAL = createKeyboardCommand('TOGGLE_KEYBOARD_SHORTCUTS_MODAL')
export const TOGGLE_COMMAND_PALETTE = createKeyboardCommand('TOGGLE_COMMAND_PALETTE')
29 changes: 24 additions & 5 deletions packages/ui-services/src/Keyboard/KeyboardService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Environment, Platform } from '@standardnotes/snjs'
import { Environment, Platform, UuidGenerator } from '@standardnotes/snjs'
import { eventMatchesKeyAndModifiers } from './eventMatchesKeyAndModifiers'
import { KeyboardCommand } from './KeyboardCommands'
import { KeyboardKeyEvent } from './KeyboardKeyEvent'
Expand Down Expand Up @@ -28,6 +28,20 @@ export class KeyboardService {
}
}

private isDisabled = false
/**
* When called, the service will stop triggering command handlers
* on keydown/keyup events. Useful when you need to handle events
* yourself while keeping the rest of behaviours inert.
* Make sure to call {@link enableEventHandling} once done.
*/
public disableEventHandling() {
this.isDisabled = true
}
public enableEventHandling() {
this.isDisabled = false
}

get isMac() {
return this.platform === Platform.MacDesktop || this.platform === Platform.MacWeb
}
Expand Down Expand Up @@ -116,6 +130,9 @@ export class KeyboardService {
}

private handleKeyboardEvent(event: KeyboardEvent, keyEvent: KeyboardKeyEvent): void {
if (this.isDisabled) {
return
}
for (const command of this.commandMap.keys()) {
const shortcut = this.commandMap.get(command)
if (!shortcut) {
Expand Down Expand Up @@ -243,26 +260,28 @@ export class KeyboardService {
...shortcut,
category: handler.category,
description: handler.description,
id: UuidGenerator.GenerateUuid(),
}
}

/**
* Register help item for a keyboard shortcut that is handled outside of the KeyboardService,
* for example by a library like Lexical.
*/
registerExternalKeyboardShortcutHelpItem(item: KeyboardShortcutHelpItem): () => void {
this.keyboardShortcutHelpItems.add(item)
registerExternalKeyboardShortcutHelpItem(item: Omit<KeyboardShortcutHelpItem, 'id'>): () => void {
const itemWithId = { ...item, id: UuidGenerator.GenerateUuid() }
this.keyboardShortcutHelpItems.add(itemWithId)

return () => {
this.keyboardShortcutHelpItems.delete(item)
this.keyboardShortcutHelpItems.delete(itemWithId)
}
}

/**
* Register help item for a keyboard shortcut that is handled outside of the KeyboardService,
* for example by a library like Lexical.
*/
registerExternalKeyboardShortcutHelpItems(items: KeyboardShortcutHelpItem[]): () => void {
registerExternalKeyboardShortcutHelpItems(items: Omit<KeyboardShortcutHelpItem, 'id'>[]): () => void {
const disposers = items.map((item) => this.registerExternalKeyboardShortcutHelpItem(item))

return () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/ui-services/src/Keyboard/KeyboardShortcut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ export type PlatformedKeyboardShortcut = KeyboardShortcut & {

export type KeyboardShortcutCategory = 'General' | 'Notes list' | 'Current note' | 'Super notes' | 'Formatting'

export type KeyboardShortcutHelpItem = Omit<PlatformedKeyboardShortcut, 'command'> & {
export interface KeyboardShortcutHelpItem extends Omit<PlatformedKeyboardShortcut, 'command'> {
command?: KeyboardCommand
category: KeyboardShortcutCategory
description: string
id: string
}
8 changes: 7 additions & 1 deletion packages/ui-services/src/Keyboard/getKeyboardShortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
CHANGE_EDITOR_WIDTH_COMMAND,
SUPER_TOGGLE_TOOLBAR,
TOGGLE_KEYBOARD_SHORTCUTS_MODAL,
TOGGLE_COMMAND_PALETTE,
} from './KeyboardCommands'
import { KeyboardKey } from './KeyboardKey'
import { KeyboardModifier, getPrimaryModifier } from './KeyboardModifier'
Expand Down Expand Up @@ -108,7 +109,7 @@ export function getKeyboardShortcuts(platform: Platform, _environment: Environme
},
{
command: CHANGE_EDITOR_COMMAND,
key: '/',
key: '?',
modifiers: [primaryModifier, KeyboardModifier.Shift],
preventDefault: true,
},
Expand Down Expand Up @@ -200,5 +201,10 @@ export function getKeyboardShortcuts(platform: Platform, _environment: Environme
key: '/',
modifiers: [primaryModifier],
},
{
command: TOGGLE_COMMAND_PALETTE,
code: 'Semicolon',
modifiers: [primaryModifier, KeyboardModifier.Shift],
},
]
}
4 changes: 3 additions & 1 deletion packages/ui-services/src/Keyboard/keyboardCharacterForKey.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function keyboardCharacterForKeyOrCode(keyOrCode: string) {
export function keyboardCharacterForKeyOrCode(keyOrCode: string, shiftKey = false) {
if (keyOrCode.startsWith('Digit')) {
return keyOrCode.replace('Digit', '')
}
Expand All @@ -14,6 +14,8 @@ export function keyboardCharacterForKeyOrCode(keyOrCode: string) {
return '←'
case 'ArrowRight':
return '→'
case 'Semicolon':
return shiftKey ? ':' : ';'
default:
return keyOrCode
}
Expand Down
2 changes: 1 addition & 1 deletion packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
"app/**/*.{js,ts,jsx,tsx,css,md}": "prettier --write"
},
"dependencies": {
"@ariakit/react": "^0.3.9",
"@ariakit/react": "^0.4.18",
"@lexical/clipboard": "0.32.1",
"@lexical/headless": "0.32.1",
"@lexical/link": "0.32.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const Web_TYPES = {
Importer: Symbol.for('Importer'),
ItemGroupController: Symbol.for('ItemGroupController'),
KeyboardService: Symbol.for('KeyboardService'),
CommandService: Symbol.for('CommandService'),
MobileWebReceiver: Symbol.for('MobileWebReceiver'),
MomentsService: Symbol.for('MomentsService'),
PersistenceService: Symbol.for('PersistenceService'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
ThemeManager,
ToastService,
VaultDisplayService,
WebApplicationInterface,
} from '@standardnotes/ui-services'
import { DependencyContainer } from '@standardnotes/utils'
import { Web_TYPES } from './Types'
Expand Down Expand Up @@ -50,9 +49,11 @@ import { LoadPurchaseFlowUrl } from '../UseCase/LoadPurchaseFlowUrl'
import { GetPurchaseFlowUrl } from '../UseCase/GetPurchaseFlowUrl'
import { OpenSubscriptionDashboard } from '../UseCase/OpenSubscriptionDashboard'
import { HeadlessSuperConverter } from '@/Components/SuperEditor/Tools/HeadlessSuperConverter'
import { WebApplication } from '../WebApplication'
import { CommandService } from '../../Components/CommandPalette/CommandService'

export class WebDependencies extends DependencyContainer {
constructor(private application: WebApplicationInterface) {
constructor(private application: WebApplication) {
super()

this.bind(Web_TYPES.SuperConverter, () => {
Expand Down Expand Up @@ -124,6 +125,9 @@ export class WebDependencies extends DependencyContainer {
this.bind(Web_TYPES.KeyboardService, () => {
return new KeyboardService(application.platform, application.environment)
})
this.bind(Web_TYPES.CommandService, () => {
return new CommandService(this.get<KeyboardService>(Web_TYPES.KeyboardService), application.generateUuid)
})

this.bind(Web_TYPES.ArchiveManager, () => {
return new ArchiveManager(application)
Expand Down Expand Up @@ -199,6 +203,7 @@ export class WebDependencies extends DependencyContainer {
return new PaneController(
application.preferences,
this.get<KeyboardService>(Web_TYPES.KeyboardService),
application.commands,
this.get<IsTabletOrMobileScreen>(Web_TYPES.IsTabletOrMobileScreen),
this.get<PanesForLayout>(Web_TYPES.PanesForLayout),
application.events,
Expand Down Expand Up @@ -233,33 +238,24 @@ export class WebDependencies extends DependencyContainer {
return new NavigationController(
this.get<FeaturesController>(Web_TYPES.FeaturesController),
this.get<VaultDisplayService>(Web_TYPES.VaultDisplayService),
this.get<KeyboardService>(Web_TYPES.KeyboardService),
this.get<CommandService>(Web_TYPES.CommandService),
this.get<PaneController>(Web_TYPES.PaneController),
application.sync,
application.mutator,
application.items,
application.preferences,
application.alerts,
application.changeAndSaveItem,
application.recents,
application.events,
)
})

this.bind(Web_TYPES.NotesController, () => {
return new NotesController(
this.get<ItemListController>(Web_TYPES.ItemListController),
this.get<NavigationController>(Web_TYPES.NavigationController),
this.get<ItemGroupController>(Web_TYPES.ItemGroupController),
this.get<KeyboardService>(Web_TYPES.KeyboardService),
application.preferences,
application.items,
application.mutator,
application.sync,
application.protections,
application.alerts,
application,
this.get<IsGlobalSpellcheckEnabled>(Web_TYPES.IsGlobalSpellcheckEnabled),
this.get<GetItemTags>(Web_TYPES.GetItemTags),
application.events,
)
})

Expand Down Expand Up @@ -304,6 +300,7 @@ export class WebDependencies extends DependencyContainer {
application.options,
this.get<IsNativeMobileWeb>(Web_TYPES.IsNativeMobileWeb),
application.changeAndSaveItem,
application.recents,
application.events,
)
})
Expand Down Expand Up @@ -374,14 +371,15 @@ export class WebDependencies extends DependencyContainer {
application.platform,
application.mobileDevice,
this.get<IsNativeMobileWeb>(Web_TYPES.IsNativeMobileWeb),
application.recents,
application.events,
)
})

this.bind(Web_TYPES.HistoryModalController, () => {
return new HistoryModalController(
this.get<NotesController>(Web_TYPES.NotesController),
this.get<KeyboardService>(Web_TYPES.KeyboardService),
this.get<CommandService>(Web_TYPES.CommandService),
application.events,
)
})
Expand Down
33 changes: 33 additions & 0 deletions packages/web/src/javascripts/Application/Recents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const MaxCommands = 5
const MaxItems = 10

export class RecentActionsState {
#commandUuids: string[] = []
#itemUuids: string[] = []

/**
* Recently used commands, most recent at the start
*/
get commandUuids() {
return this.#commandUuids
}
/**
* Recently opened items, most recent at the start
*/
get itemUuids() {
return this.#itemUuids
}

add(id: string, action_type: 'item' | 'command' = 'item') {
const action_array = action_type === 'item' ? this.#itemUuids : this.#commandUuids
const existing = action_array.findIndex((uuid) => uuid === id)
if (existing !== -1) {
action_array.splice(existing, 1)
}
const max = action_type === 'item' ? MaxItems : MaxCommands
if (action_array.length == max) {
action_array.pop()
}
action_array.unshift(id)
}
}
13 changes: 13 additions & 0 deletions packages/web/src/javascripts/Application/WebApplication.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ import { Environment, namespacedKey, Platform, RawStorageKey, SNLog } from '@sta
import { WebApplication } from '@/Application/WebApplication'
import { WebOrDesktopDevice } from './Device/WebOrDesktopDevice'

jest.mock('@standardnotes/sncrypto-web', () => {
return {
SNWebCrypto: class {
initialize() {
return Promise.resolve()
}
generateUUID() {
return 'mock-uuid'
}
},
}
})

describe('web application', () => {
let application: WebApplication

Expand Down
7 changes: 7 additions & 0 deletions packages/web/src/javascripts/Application/WebApplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ import { SearchOptionsController } from '@/Controllers/SearchOptionsController'
import { PersistenceService } from '@/Controllers/Abstract/PersistenceService'
import { removeFromArray } from '@standardnotes/utils'
import { FileItemActionType } from '@/Components/AttachedFilesPopover/PopoverFileItemAction'
import { RecentActionsState } from './Recents'
import { CommandService } from '../Components/CommandPalette/CommandService'

export type WebEventObserver = (event: WebAppEvent, data?: unknown) => void

Expand All @@ -95,6 +97,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
public isSessionsModalVisible = false

public devMode?: DevMode
public recents = new RecentActionsState()

constructor(
deviceInterface: WebOrDesktopDevice,
Expand Down Expand Up @@ -597,6 +600,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
return this.deps.get<KeyboardService>(Web_TYPES.KeyboardService)
}

get commands(): CommandService {
return this.deps.get<CommandService>(Web_TYPES.CommandService)
}

get featuresController(): FeaturesController {
return this.deps.get<FeaturesController>(Web_TYPES.FeaturesController)
}
Expand Down
Loading
Loading