git ssb

16+

Dominic / patchbay



Tree: c0bb55d9b169c5943cb5dfabbd5a58a241a238af

Files: c0bb55d9b169c5943cb5dfabbd5a58a241a238af / page / html / render / tabs.js

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

Built with git-ssb-web