diff --git a/client/angular.json b/client/angular.json
index 9b069422f..37af94e99 100644
--- a/client/angular.json
+++ b/client/angular.json
@@ -195,7 +195,6 @@
"path-browserify",
"deep-merge",
"escape-string-regexp",
- "mousetrap",
"is-plain-object",
"parse-srcset",
"deepmerge",
diff --git a/client/package.json b/client/package.json
index 5a0481be6..f10f774a8 100644
--- a/client/package.json
+++ b/client/package.json
@@ -84,7 +84,6 @@
"@wdio/mocha-framework": "^8.10.4",
"@wdio/shared-store-service": "^8.10.5",
"@wdio/spec-reporter": "^8.10.5",
- "angular2-hotkeys": "^13.1.0",
"angularx-qrcode": "16.0.0",
"babel-loader": "^9.1.0",
"bootstrap": "^5.1.3",
@@ -126,6 +125,7 @@
"socket.io-client": "^4.5.4",
"stylelint": "^15.1.0",
"stylelint-config-sass-guidelines": "^10.0.0",
+ "tinykeys": "^2.1.0",
"ts-loader": "^9.3.0",
"ts-node": "^10.9.1",
"tslib": "^2.4.0",
diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts
index 40b3b19b7..3125cffc9 100644
--- a/client/src/app/+video-channels/video-channels.component.ts
+++ b/client/src/app/+video-channels/video-channels.component.ts
@@ -1,9 +1,8 @@
-import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { Subscription } from 'rxjs'
import { catchError, distinctUntilChanged, map, switchMap } from 'rxjs/operators'
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
-import { AuthService, MarkdownService, Notifier, RestExtractor, ScreenService } from '@app/core'
+import { AuthService, MarkdownService, Notifier, RestExtractor, ScreenService, Hotkey, HotkeysService } from '@app/core'
import { Account, ListOverflowItem, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
import { BlocklistService } from '@app/shared/shared-moderation'
import { SupportModalComponent } from '@app/shared/shared-support-modal'
@@ -77,12 +76,12 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
})
this.hotkeys = [
- new Hotkey('S', (event: KeyboardEvent): boolean => {
- if (this.subscribeButton.subscribed) this.subscribeButton.unsubscribe()
+ new Hotkey('Shift+s', () => {
+ if (this.subscribeButton.isSubscribedToAll()) this.subscribeButton.unsubscribe()
else this.subscribeButton.subscribe()
return false
- }, undefined, $localize`Subscribe to the account`)
+ }, $localize`Subscribe to the account`)
]
if (this.isUserLoggedIn()) this.hotkeysService.add(this.hotkeys)
diff --git a/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts b/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts
index 13a709cb0..b8165e760 100644
--- a/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts
+++ b/client/src/app/+videos/+video-watch/shared/action-buttons/video-rate.component.ts
@@ -1,7 +1,6 @@
-import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { Observable } from 'rxjs'
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'
-import { Notifier, ScreenService } from '@app/core'
+import { Notifier, ScreenService, Hotkey, HotkeysService } from '@app/core'
import { VideoDetails, VideoService } from '@app/shared/shared-main'
import { UserVideoRateType } from '@peertube/peertube-models'
@@ -41,15 +40,15 @@ export class VideoRateComponent implements OnInit, OnChanges, OnDestroy {
if (this.isUserLoggedIn) {
this.hotkeys = [
- new Hotkey('shift+l', () => {
+ new Hotkey('Shift+l', () => {
this.setLike()
return false
- }, undefined, $localize`Like the video`),
+ }, $localize`Like the video`),
- new Hotkey('shift+d', () => {
+ new Hotkey('Shift+d', () => {
this.setDislike()
return false
- }, undefined, $localize`Dislike the video`)
+ }, $localize`Dislike the video`)
]
this.hotkeysService.add(this.hotkeys)
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts
index 39c9c7986..2524d0216 100644
--- a/client/src/app/+videos/+video-watch/video-watch.component.ts
+++ b/client/src/app/+videos/+video-watch/video-watch.component.ts
@@ -1,4 +1,3 @@
-import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { forkJoin, map, Observable, of, Subscription, switchMap } from 'rxjs'
import { PlatformLocation } from '@angular/common'
import { Component, ElementRef, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
@@ -13,6 +12,8 @@ import {
RestExtractor,
ScreenService,
ServerService,
+ Hotkey,
+ HotkeysService,
User,
UserService
} from '@app/core'
@@ -866,33 +867,33 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
this.hotkeys = [
// These hotkeys are managed by the player
- new Hotkey('f', e => e, undefined, $localize`Enter/exit fullscreen`),
- new Hotkey('space', e => e, undefined, $localize`Play/Pause the video`),
- new Hotkey('m', e => e, undefined, $localize`Mute/unmute the video`),
+ new Hotkey('f', e => e, $localize`Enter/exit fullscreen`),
+ new Hotkey('space', e => e, $localize`Play/Pause the video`),
+ new Hotkey('m', e => e, $localize`Mute/unmute the video`),
- new Hotkey('up', e => e, undefined, $localize`Increase the volume`),
- new Hotkey('down', e => e, undefined, $localize`Decrease the volume`),
+ new Hotkey('up', e => e, $localize`Increase the volume`),
+ new Hotkey('down', e => e, $localize`Decrease the volume`),
new Hotkey('t', e => {
this.theaterEnabled = !this.theaterEnabled
return false
- }, undefined, $localize`Toggle theater mode`)
+ }, $localize`Toggle theater mode`)
]
if (!video.isLive) {
this.hotkeys = this.hotkeys.concat([
// These hotkeys are also managed by the player but only for VOD
- new Hotkey('0-9', e => e, undefined, $localize`Skip to a percentage of the video: 0 is 0% and 9 is 90%`),
+ new Hotkey('0-9', e => e, $localize`Skip to a percentage of the video: 0 is 0% and 9 is 90%`),
- new Hotkey('right', e => e, undefined, $localize`Seek the video forward`),
- new Hotkey('left', e => e, undefined, $localize`Seek the video backward`),
+ new Hotkey('right', e => e, $localize`Seek the video forward`),
+ new Hotkey('left', e => e, $localize`Seek the video backward`),
- new Hotkey('>', e => e, undefined, $localize`Increase playback rate`),
- new Hotkey('<', e => e, undefined, $localize`Decrease playback rate`),
+ new Hotkey('>', e => e, $localize`Increase playback rate`),
+ new Hotkey('<', e => e, $localize`Decrease playback rate`),
- new Hotkey(',', e => e, undefined, $localize`Navigate in the video to the previous frame`),
- new Hotkey('.', e => e, undefined, $localize`Navigate in the video to the next frame`)
+ new Hotkey(',', e => e, $localize`Navigate in the video to the previous frame`),
+ new Hotkey('.', e => e, $localize`Navigate in the video to the next frame`)
])
}
@@ -903,7 +904,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
else this.subscribeButton.subscribe()
return false
- }, undefined, $localize`Subscribe to the account`)
+ }, $localize`Subscribe to the account`)
])
}
diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html
index da3c0e3dd..aa742bf03 100644
--- a/client/src/app/app.component.html
+++ b/client/src/app/app.component.html
@@ -2,7 +2,7 @@
Skip to main content
-
+
{
+ new Hotkey([ '/', 's' ], () => {
document.getElementById('search-video').focus()
return false
- }, undefined, $localize`Focus the search bar`),
+ }, $localize`Focus the search bar`),
- new Hotkey('b', (event: KeyboardEvent): boolean => {
+ new Hotkey('b', () => {
this.menu.toggleMenu()
return false
- }, undefined, $localize`Toggle the left menu`),
+ }, $localize`Toggle the left menu`),
- new Hotkey('g o', (event: KeyboardEvent): boolean => {
+ new Hotkey('g o', () => {
this.router.navigate([ '/videos/overview' ])
return false
- }, undefined, $localize`Go to the discover videos page`),
+ }, $localize`Go to the discover videos page`),
- new Hotkey('g t', (event: KeyboardEvent): boolean => {
+ new Hotkey('g t', () => {
this.router.navigate([ '/videos/trending' ])
return false
- }, undefined, $localize`Go to the trending videos page`),
+ }, $localize`Go to the trending videos page`),
- new Hotkey('g r', (event: KeyboardEvent): boolean => {
+ new Hotkey('g r', () => {
this.router.navigate([ '/videos/recently-added' ])
return false
- }, undefined, $localize`Go to the recently added videos page`),
+ }, $localize`Go to the recently added videos page`),
- new Hotkey('g l', (event: KeyboardEvent): boolean => {
+ new Hotkey('g l', () => {
this.router.navigate([ '/videos/local' ])
return false
- }, undefined, $localize`Go to the local videos page`),
+ }, $localize`Go to the local videos page`),
- new Hotkey('g u', (event: KeyboardEvent): boolean => {
+ new Hotkey('g u', () => {
this.router.navigate([ '/videos/upload' ])
return false
- }, undefined, $localize`Go to the videos upload page`)
+ }, $localize`Go to the videos upload page`)
])
}
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts
index 9339865f1..6485c98c9 100644
--- a/client/src/app/app.module.ts
+++ b/client/src/app/app.module.ts
@@ -26,6 +26,7 @@ import { SharedGlobalIconModule } from './shared/shared-icons'
import { SharedInstanceModule } from './shared/shared-instance'
import { SharedMainModule } from './shared/shared-main'
import { SharedUserInterfaceSettingsModule } from './shared/shared-user-settings'
+import { HotkeysCheatSheetComponent } from './hotkeys'
registerLocaleData(localeOc, 'oc')
@@ -63,7 +64,9 @@ export function loadConfigFactory (server: ServerService, pluginService: PluginS
CustomModalComponent,
AdminWelcomeModalComponent,
InstanceConfigWarningModalComponent,
- ConfirmComponent
+ ConfirmComponent,
+
+ HotkeysCheatSheetComponent
],
imports: [
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index bc67ab7a0..2ec4a4b7e 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -1,4 +1,4 @@
-import { Hotkey, HotkeysService } from 'angular2-hotkeys'
+import { Hotkey, HotkeysService } from '@app/core'
import { Observable, ReplaySubject, Subject, throwError as observableThrowError } from 'rxjs'
import { catchError, map, mergeMap, share, tap } from 'rxjs/operators'
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http'
@@ -57,22 +57,22 @@ export class AuthService {
// Set HotKeys
this.hotkeys = [
- new Hotkey('m s', (event: KeyboardEvent): boolean => {
+ new Hotkey('m s', e => {
this.router.navigate([ '/videos/subscriptions' ])
return false
- }, undefined, $localize`Go to my subscriptions`),
- new Hotkey('m v', (event: KeyboardEvent): boolean => {
+ }, $localize`Go to my subscriptions`),
+ new Hotkey('m v', e => {
this.router.navigate([ '/my-library/videos' ])
return false
- }, undefined, $localize`Go to my videos`),
- new Hotkey('m i', (event: KeyboardEvent): boolean => {
+ }, $localize`Go to my videos`),
+ new Hotkey('m i', e => {
this.router.navigate([ '/my-library/video-imports' ])
return false
- }, undefined, $localize`Go to my imports`),
- new Hotkey('m c', (event: KeyboardEvent): boolean => {
+ }, $localize`Go to my imports`),
+ new Hotkey('m c', e => {
this.router.navigate([ '/my-library/video-channels' ])
return false
- }, undefined, $localize`Go to my channels`)
+ }, $localize`Go to my channels`)
]
}
diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts
index 4a4c2321b..c66dd5b57 100644
--- a/client/src/app/core/core.module.ts
+++ b/client/src/app/core/core.module.ts
@@ -1,4 +1,3 @@
-import { HotkeyModule } from 'angular2-hotkeys'
import { MessageService } from 'primeng/api'
import { ToastModule } from 'primeng/toast'
import { CommonModule } from '@angular/common'
@@ -8,7 +7,6 @@ import { PeerTubeSocket } from '@app/core/notification/peertube-socket.service'
import { HooksService, PluginService } from '@app/core/plugins'
import { AuthService } from './auth'
import { ConfirmService } from './confirm'
-import { CheatSheetComponent } from './hotkeys'
import { MenuService } from './menu'
import { throwIfAlreadyLoaded } from './module-import-guard'
import { Notifier } from './notification'
@@ -32,30 +30,23 @@ import { ServerService } from './server'
import { ThemeService } from './theme'
import { UserLocalStorageService, UserService } from './users'
import { LocalStorageService, ScreenService, SessionStorageService } from './wrappers'
+import { HotkeysService } from './hotkeys'
@NgModule({
imports: [
CommonModule,
BrowserAnimationsModule,
- ToastModule,
-
- HotkeyModule.forRoot({
- cheatSheetCloseEsc: true,
- cheatSheetDescription: $localize`Show/hide this help menu`,
- cheatSheetCloseEscDescription: $localize`Hide this help menu`
- })
+ ToastModule
],
declarations: [
- CheatSheetComponent,
HomepageRedirectComponent
],
exports: [
ToastModule,
- CheatSheetComponent,
HomepageRedirectComponent
],
@@ -97,7 +88,9 @@ import { LocalStorageService, ScreenService, SessionStorageService } from './wra
ScrollService,
MetaService,
- MetaGuard
+ MetaGuard,
+
+ HotkeysService
]
})
export class CoreModule {
diff --git a/client/src/app/core/hotkeys/hotkey.model.ts b/client/src/app/core/hotkeys/hotkey.model.ts
new file mode 100644
index 000000000..e4e974352
--- /dev/null
+++ b/client/src/app/core/hotkeys/hotkey.model.ts
@@ -0,0 +1,59 @@
+// Thanks to https://github.com/brtnshrdr/angular2-hotkeys
+
+import { arrayify } from '@peertube/peertube-core-utils'
+
+export class Hotkey {
+ private formattedHotkey: string[]
+
+ static symbolize (combo: string): string {
+ const map: any = {
+ command: '\u2318', // ⌘
+ shift: '\u21E7', // ⇧
+ left: '\u2190', // ←
+ right: '\u2192', // →
+ up: '\u2191', // ↑
+ down: '\u2193', // ↓
+ return: '\u23CE', // ⏎
+ backspace: '\u232B' // ⌫
+ }
+ const comboSplit: string[] = combo.split('+')
+
+ for (let i = 0; i < comboSplit.length; i++) {
+ // try to resolve command / ctrl based on OS:
+ if (comboSplit[i] === 'mod') {
+ if (window.navigator?.platform.includes('Mac')) {
+ comboSplit[i] = 'command'
+ } else {
+ comboSplit[i] = 'ctrl'
+ }
+ }
+
+ comboSplit[i] = map[comboSplit[i]] || comboSplit[i]
+ }
+
+ return comboSplit.join(' + ')
+ }
+
+ constructor (
+ public combo: string | string[],
+ public callback: (event: KeyboardEvent, combo: string) => any | boolean,
+ public description?: string | Function
+ ) {
+ this.combo = arrayify(combo)
+ this.description = description || ''
+ }
+
+ get formatted (): string[] {
+ if (!this.formattedHotkey) {
+ const sequence: string[] = [ ...this.combo ]
+
+ for (let i = 0; i < sequence.length; i++) {
+ sequence[i] = Hotkey.symbolize(sequence[i])
+ }
+
+ this.formattedHotkey = sequence
+ }
+
+ return this.formattedHotkey
+ }
+}
diff --git a/client/src/app/core/hotkeys/hotkeys.component.ts b/client/src/app/core/hotkeys/hotkeys.component.ts
deleted file mode 100644
index 2c610a442..000000000
--- a/client/src/app/core/hotkeys/hotkeys.component.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { Hotkey, HotkeysService } from 'angular2-hotkeys'
-import { Subscription } from 'rxjs'
-import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'
-
-@Component({
- selector: 'my-hotkeys-cheatsheet',
- templateUrl: './hotkeys.component.html',
- styleUrls: [ './hotkeys.component.scss' ]
-})
-export class CheatSheetComponent implements OnInit, OnDestroy {
- @Input() title = $localize`Keyboard Shortcuts:`
-
- @Output() hotkeysModalStateChange = new EventEmitter
()
-
- helpVisible = false
- subscription: Subscription
-
- hotkeys: Hotkey[]
-
- constructor (
- private hotkeysService: HotkeysService
- ) {}
-
- public ngOnInit (): void {
- this.subscription = this.hotkeysService.cheatSheetToggle.subscribe((isOpen) => {
- if (isOpen !== false) {
- this.hotkeys = this.hotkeysService.hotkeys.filter(hotkey => hotkey.description)
- }
-
- if (isOpen === false) {
- this.helpVisible = false
- } else {
- this.toggleHelpVisible()
- }
-
- this.hotkeysModalStateChange.emit(this.helpVisible)
- })
- }
-
- public ngOnDestroy (): void {
- if (this.subscription) {
- this.subscription.unsubscribe()
- }
- }
-
- public toggleCheatSheet (): void {
- this.hotkeysService.cheatSheetToggle.next(!this.helpVisible)
- }
-
- public toggleHelpVisible (): void {
- this.helpVisible = !this.helpVisible
- }
-}
diff --git a/client/src/app/core/hotkeys/hotkeys.service.ts b/client/src/app/core/hotkeys/hotkeys.service.ts
new file mode 100644
index 000000000..9e0f5a70b
--- /dev/null
+++ b/client/src/app/core/hotkeys/hotkeys.service.ts
@@ -0,0 +1,121 @@
+// Thanks to https://github.com/brtnshrdr/angular2-hotkeys
+
+import { Injectable } from '@angular/core'
+import { Hotkey } from './hotkey.model'
+import { Subject } from 'rxjs'
+import { tinykeys } from 'tinykeys'
+import debug from 'debug'
+
+const debugLogger = debug('peertube:hotkeys')
+
+@Injectable()
+export class HotkeysService {
+ cheatSheetToggle = new Subject()
+
+ private hotkeys: Hotkey[] = []
+ private preventIn = [ 'INPUT', 'SELECT', 'TEXTAREA' ]
+
+ private disabled = false
+
+ private removeTinyKeysStore = new Map void)[]>()
+
+ constructor () {
+ this.initCheatSheet()
+ }
+
+ private initCheatSheet () {
+ debugLogger('Init hotkeys')
+
+ this.add([
+ new Hotkey(
+ [ '?', 'Shift+?' ],
+ () => this.cheatSheetToggle.next(undefined),
+ $localize`Show / hide this help menu`
+ ),
+
+ new Hotkey(
+ 'escape',
+ () => this.cheatSheetToggle.next(false),
+ $localize`Hide this help menu`
+ )
+ ])
+ }
+
+ add (hotkey: Hotkey): Hotkey
+ add (hotkey: Hotkey[]): Hotkey[]
+ add (hotkey: Hotkey | Hotkey[]): Hotkey[] | Hotkey {
+ if (Array.isArray(hotkey)) {
+ return hotkey.map(h => this.add(h))
+ }
+
+ this.remove(hotkey)
+ this.hotkeys.push(hotkey)
+
+ for (const combo of hotkey.combo) {
+ debugLogger('Adding hotkey ' + hotkey.formatted)
+
+ const removeTinyKey = tinykeys(window, {
+ [combo]: event => {
+ if (this.disabled) return
+
+ const target = event.target as Element
+ const nodeName: string = target.nodeName.toUpperCase()
+
+ if (this.preventIn.includes(nodeName)) {
+ return
+ }
+
+ const result = hotkey.callback.apply(this, [ event, combo ])
+
+ if (result === false) {
+ event.preventDefault()
+ event.stopPropagation()
+ }
+ }
+ })
+
+ if (!this.removeTinyKeysStore.has(hotkey)) {
+ this.removeTinyKeysStore.set(hotkey, [])
+ }
+
+ this.removeTinyKeysStore.get(hotkey).push(removeTinyKey)
+ }
+
+ return hotkey
+ }
+
+ remove (hotkey: Hotkey | Hotkey[]) {
+ if (Array.isArray(hotkey)) {
+ for (const h of hotkey) {
+ this.remove(h)
+ }
+
+ return
+ }
+
+ this.hotkeys = this.hotkeys.filter(h => h !== hotkey)
+ const removeHandlers = this.removeTinyKeysStore.get(hotkey)
+
+ if (removeHandlers) {
+ debugLogger('Removing hotkey ' + hotkey.formatted)
+
+ for (const removeHandler of removeHandlers) {
+ removeHandler()
+ }
+ }
+
+ this.removeTinyKeysStore.delete(hotkey)
+ }
+
+ getHotkeys () {
+ return this.hotkeys
+ }
+
+ disableHotkeys () {
+ this.disabled = true
+ }
+
+ enableHotkeys () {
+ this.disabled = false
+ }
+}
diff --git a/client/src/app/core/hotkeys/index.ts b/client/src/app/core/hotkeys/index.ts
index a8d807c71..192e27dbd 100644
--- a/client/src/app/core/hotkeys/index.ts
+++ b/client/src/app/core/hotkeys/index.ts
@@ -1 +1,2 @@
-export * from './hotkeys.component'
+export * from './hotkey.model'
+export * from './hotkeys.service'
diff --git a/client/src/app/core/hotkeys/hotkeys.component.html b/client/src/app/hotkeys/hotkeys-cheat-sheet.component.html
similarity index 64%
rename from client/src/app/core/hotkeys/hotkeys.component.html
rename to client/src/app/hotkeys/hotkeys-cheat-sheet.component.html
index 103f28411..ea9a46724 100644
--- a/client/src/app/core/hotkeys/hotkeys.component.html
+++ b/client/src/app/hotkeys/hotkeys-cheat-sheet.component.html
@@ -2,6 +2,13 @@
{{ title }}
+
+
+
+
-
diff --git a/client/src/app/core/hotkeys/hotkeys.component.scss b/client/src/app/hotkeys/hotkeys-cheat-sheet.component.scss
similarity index 100%
rename from client/src/app/core/hotkeys/hotkeys.component.scss
rename to client/src/app/hotkeys/hotkeys-cheat-sheet.component.scss
diff --git a/client/src/app/hotkeys/hotkeys-cheat-sheet.component.ts b/client/src/app/hotkeys/hotkeys-cheat-sheet.component.ts
new file mode 100644
index 000000000..68e6460bb
--- /dev/null
+++ b/client/src/app/hotkeys/hotkeys-cheat-sheet.component.ts
@@ -0,0 +1,74 @@
+import { Subscription } from 'rxjs'
+import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'
+import { LocalStorageService, HotkeysService, Hotkey } from '@app/core'
+
+@Component({
+ selector: 'my-hotkeys-cheat-sheet',
+ templateUrl: './hotkeys-cheat-sheet.component.html',
+ styleUrls: [ './hotkeys-cheat-sheet.component.scss' ]
+})
+export class HotkeysCheatSheetComponent implements OnInit, OnDestroy {
+ @Input() title = $localize`Keyboard Shortcuts`
+
+ @Output() hotkeysModalStateChange = new EventEmitter()
+
+ hotkeysEnabled = true
+
+ helpVisible = false
+ subscription: Subscription
+
+ hotkeys: Hotkey[]
+
+ private readonly localStorageHotkeysDisabledKey = 'peertube-hotkeys-disabled'
+
+ constructor (
+ private hotkeysService: HotkeysService,
+ private localStorage: LocalStorageService
+ ) {}
+
+ ngOnInit () {
+ if (this.localStorage.getItem(this.localStorageHotkeysDisabledKey) === 'true') {
+ this.hotkeysEnabled = false
+ this.hotkeysService.disableHotkeys()
+ }
+
+ this.subscription = this.hotkeysService.cheatSheetToggle.subscribe(isOpen => {
+ if (isOpen !== false) {
+ this.hotkeys = this.hotkeysService.getHotkeys().filter(hotkey => hotkey.description)
+ }
+
+ if (isOpen === false) {
+ this.helpVisible = false
+ } else {
+ this.toggleHelpVisible()
+ }
+
+ this.hotkeysModalStateChange.emit(this.helpVisible)
+ })
+ }
+
+ ngOnDestroy () {
+ if (this.subscription) {
+ this.subscription.unsubscribe()
+ }
+ }
+
+ toggleCheatSheet () {
+ this.hotkeysService.cheatSheetToggle.next(!this.helpVisible)
+ }
+
+ toggleHelpVisible () {
+ this.helpVisible = !this.helpVisible
+ }
+
+ onHotkeysEnabledChange () {
+ if (!this.hotkeysEnabled) {
+ this.localStorage.setItem(this.localStorageHotkeysDisabledKey, 'true')
+ this.hotkeysService.disableHotkeys()
+ return
+ }
+
+ this.hotkeysService.enableHotkeys()
+ this.localStorage.removeItem(this.localStorageHotkeysDisabledKey)
+ }
+}
diff --git a/client/src/app/hotkeys/index.ts b/client/src/app/hotkeys/index.ts
new file mode 100644
index 000000000..a00ecee62
--- /dev/null
+++ b/client/src/app/hotkeys/index.ts
@@ -0,0 +1 @@
+export * from './hotkeys-cheat-sheet.component'
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts
index 31118b476..c896da116 100644
--- a/client/src/app/menu/menu.component.ts
+++ b/client/src/app/menu/menu.component.ts
@@ -1,4 +1,3 @@
-import { HotkeysService } from 'angular2-hotkeys'
import * as debug from 'debug'
import { forkJoin, Subscription } from 'rxjs'
import { first, switchMap } from 'rxjs/operators'
@@ -10,6 +9,7 @@ import {
AuthStatus,
AuthUser,
HooksService,
+ HotkeysService,
MenuSection,
MenuService,
RedirectService,
diff --git a/client/yarn.lock b/client/yarn.lock
index cc0bd23a6..ddaaa7c9c 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -2460,11 +2460,6 @@
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b"
integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==
-"@types/mousetrap@^1.6.9":
- version "1.6.11"
- resolved "https://registry.yarnpkg.com/@types/mousetrap/-/mousetrap-1.6.11.tgz#ef9620160fdcefcb85bccda8aaa3e84d7429376d"
- integrity sha512-F0oAily9Q9QQpv9JKxKn0zMKfOo36KHCW7myYsmUyf2t0g+sBTbG3UleTPoguHdE1z3GLFr3p7/wiOio52QFjQ==
-
"@types/ms@*":
version "0.7.31"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
@@ -3293,15 +3288,6 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
-angular2-hotkeys@^13.1.0:
- version "13.4.0"
- resolved "https://registry.yarnpkg.com/angular2-hotkeys/-/angular2-hotkeys-13.4.0.tgz#a96676466936556655cd64f92e1f5cd3aeac8e10"
- integrity sha512-WvkouvdXtTYw3tpuaoEVF+ue41pvI2XSa8m4tVRPLzAblT/f7PG0uQO4npyjVw3oDIc7qnFkQR+oqGl1KM1eow==
- dependencies:
- "@types/mousetrap" "^1.6.9"
- mousetrap "^1.6.5"
- tslib "^2.3.1"
-
angularx-qrcode@16.0.0:
version "16.0.0"
resolved "https://registry.yarnpkg.com/angularx-qrcode/-/angularx-qrcode-16.0.0.tgz#c637924b8d8f9cc344216caa80adf3810ccd5e5b"
@@ -8186,11 +8172,6 @@ moment@^2.10.2:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
-mousetrap@^1.6.5:
- version "1.6.5"
- resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.5.tgz#8a766d8c272b08393d5f56074e0b5ec183485bf9"
- integrity sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==
-
mpd-parser@0.22.1, mpd-parser@^0.22.1:
version "0.22.1"
resolved "https://registry.yarnpkg.com/mpd-parser/-/mpd-parser-0.22.1.tgz#bc2bf7d3e56368e4b0121035b055675401871521"
@@ -10789,6 +10770,11 @@ thunky@^1.0.2:
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
+tinykeys@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/tinykeys/-/tinykeys-2.1.0.tgz#1341563e92a7fac9ca90053fddaf2b7553500298"
+ integrity sha512-/MESnqBD1xItZJn5oGQ4OsNORQgJfPP96XSGoyu4eLpwpL0ifO0SYR5OD76u0YMhMXsqkb0UqvI9+yXTh4xv8Q==
+
tmp@0.2.1, tmp@~0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"