mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2024-10-26 14:08:12 +03:00
Allow to disable all hotkeys
Added angular2-hotkeys dependency inside PeerTube, to tweak some settings It will also allow us to support non latin keyboard in the future as we can choose the "mouse trap" dependency
This commit is contained in:
parent
e6b455b4ea
commit
50e415e12e
@ -195,7 +195,6 @@
|
||||
"path-browserify",
|
||||
"deep-merge",
|
||||
"escape-string-regexp",
|
||||
"mousetrap",
|
||||
"is-plain-object",
|
||||
"parse-srcset",
|
||||
"deepmerge",
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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`)
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<a i18n class="visually-hidden-focusable skip-to-content" href="#content" (click)="$event.preventDefault(); mainContent.focus()">Skip to main content</a>
|
||||
|
||||
<my-hotkeys-cheatsheet (hotkeysModalStateChange)="onHotkeysModalStateChange($event)"></my-hotkeys-cheatsheet>
|
||||
<my-hotkeys-cheat-sheet (hotkeysModalStateChange)="onHotkeysModalStateChange($event)"></my-hotkeys-cheat-sheet>
|
||||
|
||||
<div
|
||||
class="peertube-container"
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
|
||||
import { delay, forkJoin } from 'rxjs'
|
||||
import { filter, first, map } from 'rxjs/operators'
|
||||
import { DOCUMENT, getLocaleDirection, PlatformLocation } from '@angular/common'
|
||||
@ -15,7 +14,9 @@ import {
|
||||
ServerService,
|
||||
ThemeService,
|
||||
User,
|
||||
UserLocalStorageService
|
||||
UserLocalStorageService,
|
||||
Hotkey,
|
||||
HotkeysService
|
||||
} from '@app/core'
|
||||
import { HooksService } from '@app/core/plugins/hooks.service'
|
||||
import { PluginService } from '@app/core/plugins/plugin.service'
|
||||
@ -313,40 +314,40 @@ export class AppComponent implements OnInit, AfterViewInit {
|
||||
|
||||
private initHotkeys () {
|
||||
this.hotkeysService.add([
|
||||
new Hotkey([ '/', 's' ], (event: KeyboardEvent): boolean => {
|
||||
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`)
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -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: [
|
||||
|
@ -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`)
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
59
client/src/app/core/hotkeys/hotkey.model.ts
Normal file
59
client/src/app/core/hotkeys/hotkey.model.ts
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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<boolean>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
121
client/src/app/core/hotkeys/hotkeys.service.ts
Normal file
121
client/src/app/core/hotkeys/hotkeys.service.ts
Normal file
@ -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<boolean>()
|
||||
|
||||
private hotkeys: Hotkey[] = []
|
||||
private preventIn = [ 'INPUT', 'SELECT', 'TEXTAREA' ]
|
||||
|
||||
private disabled = false
|
||||
|
||||
private removeTinyKeysStore = new Map<Hotkey, (() => 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
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
export * from './hotkeys.component'
|
||||
export * from './hotkey.model'
|
||||
export * from './hotkeys.service'
|
||||
|
@ -2,6 +2,13 @@
|
||||
<div class="cfp-hotkeys">
|
||||
<h1 class="cfp-hotkeys-title">{{ title }}</h1>
|
||||
|
||||
<div class="d-flex justify-content-center m-3">
|
||||
<my-peertube-checkbox
|
||||
inputName="enable-hotkeys" [(ngModel)]="hotkeysEnabled" (ngModelChange)="onHotkeysEnabledChange()"
|
||||
i18n-labelText labelText="Enable hotkeys in this web browser"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
<ul role="presentation">
|
||||
<li *ngFor="let hotkey of hotkeys">
|
||||
<div class="cfp-hotkeys-keys">
|
74
client/src/app/hotkeys/hotkeys-cheat-sheet.component.ts
Normal file
74
client/src/app/hotkeys/hotkeys-cheat-sheet.component.ts
Normal file
@ -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<boolean>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
1
client/src/app/hotkeys/index.ts
Normal file
1
client/src/app/hotkeys/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './hotkeys-cheat-sheet.component'
|
@ -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,
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user