Files: 2b05be8ee023fdd9580d6a845ba074a425756685 / app / sync / catch-keyboard-shortcut.js
3161 bytesRaw
1 | const nest = require('depnest') |
2 | |
3 | exports.gives = nest('app.sync.catchKeyboardShortcut') |
4 | |
5 | exports.needs = nest({ |
6 | 'app.html.searchBar': 'first', |
7 | 'app.html.tabs': 'first', |
8 | 'app.sync.goTo': 'first' |
9 | }) |
10 | |
11 | var gPressed = false |
12 | |
13 | exports.create = function (api) { |
14 | return nest('app.sync.catchKeyboardShortcut', catchKeyboardShortcut) |
15 | |
16 | function catchKeyboardShortcut (root) { |
17 | var tabs = api.app.html.tabs() |
18 | var search = api.app.html.searchBar() |
19 | var goTo = api.app.sync.goTo |
20 | |
21 | root.addEventListener('keydown', (ev) => { |
22 | isTextFieldEvent(ev) |
23 | ? textFieldShortcuts(ev) |
24 | : genericShortcuts(ev, { tabs, search, goTo }) |
25 | }) |
26 | } |
27 | } |
28 | |
29 | function isTextFieldEvent (ev) { |
30 | const tag = ev.target.nodeName |
31 | return (tag === 'INPUT' || tag === 'TEXTAREA') |
32 | } |
33 | |
34 | function textFieldShortcuts (ev) { |
35 | switch (ev.keyCode) { |
36 | case 13: // ctrl+enter |
37 | if (ev.ctrlKey) { |
38 | ev.target.publish && ev.target.publish() // expects the textField to have a publish method |
39 | } |
40 | return |
41 | case 27: // esc |
42 | return ev.target.blur() |
43 | case 33: // PgUp |
44 | return ev.preventDefault() |
45 | case 34: // PgDn |
46 | return ev.preventDefault() |
47 | } |
48 | } |
49 | |
50 | function genericShortcuts (ev, { tabs, search, goTo, back }) { |
51 | let scroll |
52 | const currentPage = tabs.currentPage() |
53 | if (currentPage) { |
54 | scroll = currentPage.keyboardScroll || noop |
55 | } else { |
56 | scroll = noop |
57 | } |
58 | |
59 | // Messages |
60 | if (ev.keyCode === 71) { // gg = scroll to top |
61 | if (!gPressed) { |
62 | gPressed = true |
63 | window.setTimeout(() => { |
64 | gPressed = false |
65 | }, 3000) |
66 | return |
67 | } |
68 | scroll('first') |
69 | } |
70 | gPressed = false |
71 | |
72 | switch (ev.keyCode) { |
73 | // Messages (cont'd) |
74 | case 74: // j = older |
75 | return scroll(1) |
76 | case 75: // k = newer |
77 | return scroll(-1) |
78 | case 13: // enter = open |
79 | return goToMessage(ev, { goTo }) |
80 | case 79: // o = open |
81 | return goToMessage(ev, { goTo }) |
82 | case 192: // ` = toggle raw message view |
83 | return toggleRawMessage(ev) |
84 | |
85 | // Tabs |
86 | case 72: // h = left |
87 | tabs.selectRelative(-1) |
88 | return goTo(JSON.parse(tabs.currentPage().id)) |
89 | case 76: // l = right |
90 | tabs.selectRelative(1) |
91 | return goTo(JSON.parse(tabs.currentPage().id)) |
92 | case 88: // x = close |
93 | tabs.closeCurrentTab() |
94 | return |
95 | |
96 | // Search |
97 | case 191: // / = routes search |
98 | if (ev.shiftKey) search.activate('?', ev) |
99 | else search.activate('/', ev) |
100 | return |
101 | case 50: // @ = mention search |
102 | if (ev.shiftKey) search.activate('@', ev) |
103 | return |
104 | case 51: // # = channel search |
105 | if (ev.shiftKey) search.activate('#', ev) |
106 | return |
107 | case 53: // % = message search |
108 | if (ev.shiftKey) search.activate('%', ev) |
109 | } |
110 | } |
111 | |
112 | function goToMessage (ev, { goTo }) { |
113 | const msg = ev.target |
114 | |
115 | if (msg.dataset && msg.dataset.key) { |
116 | goTo(msg.dataset.key) |
117 | // TODO - rm the dataset.root decorator |
118 | } |
119 | } |
120 | |
121 | function toggleRawMessage (ev) { |
122 | const msg = ev.target |
123 | if (!msg.classList.contains('Message')) return |
124 | |
125 | // this uses a crudely exported nav api |
126 | const target = msg.querySelector('.meta .toggle-raw-msg') |
127 | if (target) target.click() |
128 | } |
129 | |
130 | function noop () {} |
131 |
Built with git-ssb-web