git ssb

16+

Dominic / patchbay



Tree: bd411fd1d719442e7658db49d6b8c5d22b10fb25

Files: bd411fd1d719442e7658db49d6b8c5d22b10fb25 / router / html / tabs.js

7028 bytesRaw
1const { h } = require('mutant')
2const Tabs = require('hypertabs')
3const open = require('open-external')
4const { webFrame, remote, clipboard } = require('electron') || {}
5const nest = require('depnest')
6
7// const keyscroll = require('../../keyscroll')
8
9exports.needs = nest({
10 'router.html.page': 'first'
11 // 'app.html.menu': 'first',
12 // helpers: {
13 // build_error: 'first',
14 // build_scroller: 'first',
15 // external_confirm:'first',
16 // },
17 // 'app.html.search_box': 'first'
18})
19
20exports.gives = nest('router.html.tabs')
21
22exports.create = function (api) {
23 return function (path) {
24 function setSelected (indexes) {
25 const ids = indexes.map(index => tabs.get(index).content.id)
26 if (search)
27 if (ids.length > 1)
28 search.input.value = 'split(' + ids.join(',') + ')'
29 else
30 search.input.value = ids[0]
31 }
32
33 const tabs = Tabs(setSelected)
34 const search = api.search_box((path, change) => {
35 if (tabs.has(path)) {
36 tabs.select(path)
37 return true
38 }
39
40 const el = api.page(path)
41 if (!el) return
42
43 if (!el.title) el.title = path
44 // el.scroll = keyscroll(el.querySelector('.Scroller .content'))
45 tabs.add(el, change)
46// localStorage.openTabs = JSON.stringify(tabs.tabs)
47 return change
48 })
49
50 // TODO add options to Tabs : e.g. Tabs(setSelected, { append: el })
51 tabs.firstChild.appendChild(
52 h('div.extra', [
53 search,
54 api.menu()
55 ])
56 )
57
58 var saved = []
59 // try { saved = JSON.parse(localStorage.openTabs) }
60 // catch (_) { }
61
62 if (!saved || saved.length < 3)
63 saved = ['/public', '/private', '/notifications']
64
65 saved.forEach(function (path) {
66 var el = api.page(path)
67 if (!el) return
68 el.id = el.id || path
69 if (!el) return
70 el.scroll = keyscroll(el.querySelector('.Scroller .content'))
71 if (el) tabs.add(el, false, false)
72 })
73
74 tabs.select(0)
75 search.input.value = null // start with an empty field to show placeholder
76
77 // handle link clicks
78 window.onclick = function (ev) {
79 var link = ancestorAnchor(ev.target)
80 if (!link) return
81 var path = link.hash.substring(1)
82
83 ev.preventDefault()
84 ev.stopPropagation()
85
86 // let the application handle this link
87 if (link.getAttribute('href') === '#') return
88
89 // open external links.
90 // this ought to be made into something more runcible
91 if (link.href && open.isExternal(link.href)) return api.helpers.external_confirm(link.href)
92
93 if (tabs.has(path))
94 return tabs.select(path, !ev.ctrlKey, !!ev.shiftKey)
95
96 var el = api.page(path)
97 if (el) {
98 el.id = el.id || path
99 el.scroll = keyscroll(el.querySelector('.Scroller .content'))
100 tabs.add(el, !ev.ctrlKey, !!ev.shiftKey)
101 // localStorage.openTabs = JSON.stringify(tabs.tabs)
102 }
103
104 return false
105 }
106
107 var gPressed = false
108 window.addEventListener('keydown', function (ev) {
109 if (ev.target.nodeName === 'INPUT' || ev.target.nodeName === 'TEXTAREA')
110 return
111
112 // scroll to top
113 if (ev.keyCode == 71) { // g
114 if (!gPressed) return gPressed = true
115 var el = tabs.get(tabs.selected[0]).firstChild.scroll('first')
116 gPressed = false
117 } else {
118 gPressed = false
119 }
120
121 switch (ev.keyCode) {
122 // scroll through tabs
123 case 72: // h
124 return tabs.selectRelative(-1)
125 case 76: // l
126 return tabs.selectRelative(1)
127
128 // scroll through messages
129 case 74: // j
130 return tabs.get(tabs.selected[0]).firstChild.scroll(1)
131 case 75: // k
132 return tabs.get(tabs.selected[0]).firstChild.scroll(-1)
133
134 // close a tab
135 case 88: // x
136 if (tabs.selected) {
137 var sel = tabs.selected
138 var i = sel.reduce(function (a, b) { return Math.min(a, b) })
139 tabs.remove(sel)
140 tabs.select(Math.max(i - 1, 0))
141 }
142 return
143
144 // activate the search field
145 case 191: // /
146 if (ev.shiftKey)
147 search.activate('?', ev)
148 else
149 search.activate('/', ev)
150 return
151
152 // navigate to a feed
153 case 50: // 2
154 if (ev.shiftKey)
155 search.activate('@', ev)
156 return
157
158 // navigate to a channel
159 case 51: // 3
160 if (ev.shiftKey)
161 search.activate('#', ev)
162 return
163
164 // navigate to a message
165 case 53: // 5
166 if (ev.shiftKey)
167 search.activate('%', ev)
168 return
169 }
170 })
171
172 // errors tab
173 var {
174 container: errorsScroller,
175 content: errorsContent
176 } = api.helpers.build_scroller()
177
178 errorsScroller.id = '/errors'
179 errorsScroller.classList.add('-errors')
180
181 // remove loader error handler (currently disabled)
182 // if (window.onError) {
183 // window.removeEventListener('error', window.onError)
184 // delete window.onError
185 // }
186
187 // put errors in a tab
188 window.addEventListener('error', ev => {
189 const err = ev.error || ev
190 if (!tabs.has('/errors'))
191 tabs.add(errorsScroller, false)
192
193 const el = api.helpers.build_error(err)
194 if (errorsContent.firstChild)
195 errorsContent.insertBefore(el, errorsContent.firstChild)
196 else
197 errorsContent.appendChild(el)
198 })
199
200 if (process.versions.electron) {
201 window.addEventListener('mousewheel', ev => {
202 const { ctrlKey, deltaY } = ev
203 if (ctrlKey) {
204 const direction = (deltaY / Math.abs(deltaY))
205 const currentZoom = webFrame.getZoomLevel()
206 webFrame.setZoomLevel(currentZoom - direction)
207 }
208 })
209
210 window.addEventListener('contextmenu', ev => {
211 ev.preventDefault()
212 const Menu = remote.Menu
213 const MenuItem = remote.MenuItem
214 const menu = new Menu()
215 menu.append(new MenuItem({
216 label: 'Inspect Element',
217 click: () => {
218 remote.getCurrentWindow().inspectElement(ev.x, ev.y)
219 }
220 }))
221
222 var message = ancestorMessage(ev.target)
223 if (message && message.dataset.key) {
224 menu.append(new MenuItem({
225 label: 'Copy id',
226 click: () => clipboard.writeText(message.dataset.key)
227 }))
228 }
229 if (message && message.dataset.text) {
230 menu.append(new MenuItem({
231 label: 'Copy text',
232 click: () => clipboard.writeText(message.dataset.text)
233 }))
234 }
235 menu.popup(remote.getCurrentWindow())
236 })
237 }
238
239 return tabs
240 }
241}
242
243function ancestorAnchor (el) {
244 if (!el) return
245 if (el.tagName !== 'A') return ancestorAnchor(el.parentElement)
246 return el
247}
248
249function ancestorMessage (el) {
250 if (!el) return
251 if (!el.classList.contains('Message')) {
252 if (el.parentElement)
253 return ancestorMessage(el.parentElement)
254 else
255 return null
256 }
257 return el
258}
259
260

Built with git-ssb-web