Files: ef0e26935108e5a9748cd1b03ef830fceff93640 / app / sync / catch-keyboard-shortcut.js
3179 bytesRaw
1 | const nest = require('depnest') |
2 | |
3 | exports.gives = nest('app.sync.catchKeyboardShortcut') |
4 | |
5 | exports.create = function (api) { |
6 | return nest('app.sync.catchKeyboardShortcut', catchKeyboardShortcut) |
7 | |
8 | function catchKeyboardShortcut (root, opts) { |
9 | var gPressed = false |
10 | |
11 | root.addEventListener('keydown', (ev) => { |
12 | isTextFieldEvent(ev) |
13 | ? textFieldShortcuts(ev) |
14 | : genericShortcuts(ev, opts) |
15 | }) |
16 | } |
17 | } |
18 | |
19 | // TODO build better apis for navigation, search, and publishing |
20 | |
21 | function isTextFieldEvent (ev) { |
22 | const tag = ev.target.nodeName |
23 | return (tag === 'INPUT' || tag === 'TEXTAREA') |
24 | } |
25 | |
26 | function textFieldShortcuts (ev) { |
27 | if (ev.keyCode === 13 && ev.ctrlKey) { |
28 | ev.target.publish() // expects the textField to have a publish method |
29 | } |
30 | } |
31 | |
32 | function genericShortcuts (ev, { tabs, search }) { |
33 | // Messages |
34 | if (ev.keyCode === 71) { // gg = scroll to top |
35 | if (!gPressed) { |
36 | gPressed = true |
37 | return |
38 | } |
39 | tabs.get(tabs.selected[0]).firstChild.scroll('first') |
40 | } |
41 | gPressed = false |
42 | |
43 | switch (ev.keyCode) { |
44 | |
45 | // Messages (cont'd) |
46 | case 74: // j = older |
47 | return tabs.get(tabs.selected[0]).firstChild.scroll(1) |
48 | case 75: // k = newer |
49 | return tabs.get(tabs.selected[0]).firstChild.scroll(-1) |
50 | case 13: // enter = open |
51 | return goToMessage(ev, tabs) |
52 | case 79: // o = open |
53 | return goToMessage(ev, tabs) |
54 | case 192: // ` = toggle raw message view |
55 | return toggleRawMessage(ev) |
56 | |
57 | // Tabs |
58 | case 72: // h = left |
59 | return tabs.selectRelative(-1) |
60 | case 76: // l = right |
61 | return tabs.selectRelative(1) |
62 | case 88: // x = close |
63 | if (tabs.selected) { |
64 | var sel = tabs.selected |
65 | var i = sel.reduce(function (a, b) { return Math.min(a, b) }) |
66 | tabs.remove(sel) |
67 | tabs.select(Math.max(i - 1, 0)) |
68 | } |
69 | return |
70 | |
71 | // Search |
72 | case 191: // / = routes search |
73 | if (ev.shiftKey) search.activate('?', ev) |
74 | else search.activate('/', ev) |
75 | return |
76 | case 50: // @ = mention search |
77 | if (ev.shiftKey) search.activate('@', ev) |
78 | return |
79 | case 51: // # = channel search |
80 | if (ev.shiftKey) search.activate('#', ev) |
81 | return |
82 | case 53: // % = message search |
83 | if (ev.shiftKey) search.activate('%', ev) |
84 | return |
85 | } |
86 | } |
87 | |
88 | function goToMessage (ev, tabs) { |
89 | const msg = ev.target |
90 | if (!msg.classList.contains('Message')) return |
91 | |
92 | // this uses a crudely exported nav api |
93 | const search = document.querySelector('input[type=search]') |
94 | |
95 | const { root, id } = msg.dataset |
96 | if (!root) return search.go(id) |
97 | |
98 | search.go(root) |
99 | scrollDownToMessage(id, tabs) |
100 | } |
101 | |
102 | function scrollDownToMessage (id, tabs) { |
103 | tabs.get(tabs.selected[0]).firstChild.scroll('first') |
104 | locateKey() |
105 | |
106 | function locateKey () { |
107 | const msg = tabs.get(tabs.selected[0]).querySelector(`[data-id='${id}']`) |
108 | if (msg === null) return setTimeout(locateKey, 100) |
109 | |
110 | ;(msg.scrollIntoViewIfNeeded || msg.scrollIntoView).call(msg) |
111 | msg.focus() |
112 | } |
113 | } |
114 | |
115 | function toggleRawMessage (ev) { |
116 | const msg = ev.target |
117 | if (!msg.classList.contains('Message')) return |
118 | |
119 | // this uses a crudely exported nav api |
120 | msg.querySelector('.meta .toggle-raw-msg').click() |
121 | } |
122 | |
123 |
Built with git-ssb-web