git ssb

10+

Matt McKegg / patchwork



Tree: 81554c524a546fd9b22c64b0bbf4d46af4c7c30b

Files: 81554c524a546fd9b22c64b0bbf4d46af4c7c30b / main-window.js

7219 bytesRaw
1var combine = require('depject')
2var entry = require('depject/entry')
3var electron = require('electron')
4var h = require('mutant/h')
5var Value = require('mutant/value')
6var when = require('mutant/when')
7var computed = require('mutant/computed')
8var toCollection = require('mutant/dict-to-collection')
9var MutantDict = require('mutant/dict')
10var MutantMap = require('mutant/map')
11var Url = require('url')
12var insertCss = require('insert-css')
13var nest = require('depnest')
14var addSuggest = require('suggest-box')
15var LatestUpdate = require('./lib/latest-update')
16
17require('./lib/context-menu-and-spellcheck.js')
18
19module.exports = function (config) {
20 var sockets = combine(
21 overrideConfig(config),
22 require('./modules'),
23 require('./plugs'),
24 require('patchcore'),
25 require('./overrides')
26 )
27
28 var api = entry(sockets, nest({
29 'page.html.render': 'first',
30 'keys.sync.id': 'first',
31 'blob.sync.url': 'first',
32 'profile.async.suggest': 'first',
33 'channel.async.suggest': 'first'
34 }))
35
36 var renderPage = api.page.html.render
37 var id = api.keys.sync.id()
38 var getProfileSuggestions = api.profile.async.suggest()
39 var getChannelSuggestions = api.channel.async.suggest()
40 var latestUpdate = LatestUpdate()
41
42 var searchTimer = null
43 var searchBox = h('input.search', {
44 type: 'search',
45 placeholder: 'word, @key, #channel',
46 'ev-suggestselect': (ev) => {
47 setView(ev.detail.id)
48 searchBox.value = ev.detail.id
49 }
50 })
51
52 searchBox.oninput = function () {
53 clearTimeout(searchTimer)
54 searchTimer = setTimeout(doSearch, 500)
55 }
56
57 searchBox.onfocus = function () {
58 if (searchBox.value) {
59 doSearch()
60 }
61 }
62
63 var forwardHistory = []
64 var backHistory = []
65
66 var views = MutantDict({
67 // preload tabs (and subscribe to update notifications)
68 '/public': renderPage('/public'),
69 '/private': renderPage('/private'),
70 [id]: renderPage(id),
71 '/mentions': renderPage('/mentions')
72 })
73
74 var lastViewed = {}
75 var defaultViews = views.keys()
76
77 // delete cached view after 5 mins of last seeing
78 setInterval(() => {
79 views.keys().forEach((view) => {
80 if (!defaultViews.includes(view)) {
81 if (lastViewed[view] !== true && Date.now() - lastViewed[view] > (5 * 60e3) && view !== currentView()) {
82 views.delete(view)
83 }
84 }
85 })
86 }, 60e3)
87
88 var canGoForward = Value(false)
89 var canGoBack = Value(false)
90 var currentView = Value('/public')
91
92 var viewCollection = toCollection(views)
93
94 var mainElement = h('div.main', MutantMap(viewCollection, (item) => {
95 return h('div.view', {
96 hidden: computed([item.key, currentView], (a, b) => a !== b)
97 }, [ item.value ])
98 }))
99
100 insertCss(require('./styles'))
101
102 var container = h(`MainWindow -${process.platform}`, {
103 events: {
104 click: catchLinks
105 }
106 }, [
107 h('div.top', [
108 h('span.history', [
109 h('a', {
110 'ev-click': goBack,
111 classList: [ when(canGoBack, '-active') ]
112 }, '<'),
113 h('a', {
114 'ev-click': goForward,
115 classList: [ when(canGoForward, '-active') ]
116 }, '>')
117 ]),
118 h('span.nav', [
119 tab('Public', '/public'),
120 tab('Private', '/private')
121 ]),
122 h('span.appTitle', ['Patchwork']),
123 h('span', [ searchBox ]),
124 h('span.nav', [
125 tab('Profile', id),
126 tab('Mentions', '/mentions')
127 ])
128 ]),
129 when(latestUpdate,
130 h('div.info', [
131 h('a.message -update', { href: 'https://github.com/mmckegg/patchwork-next/releases' }, [
132 h('strong', ['Patchwork ', latestUpdate, ' has been released.']), ' Click here for more info!'
133 ])
134 ])
135 ),
136 mainElement
137 ])
138
139 addSuggest(searchBox, (inputText, cb) => {
140 if (inputText[0] === '@') {
141 cb(null, getProfileSuggestions(inputText.slice(1)), {idOnly: true})
142 } else if (inputText[0] === '#') {
143 cb(null, getChannelSuggestions(inputText.slice(1)))
144 }
145 }, {cls: 'SuggestBox'})
146
147 return container
148
149 // scoped
150
151 function catchLinks (ev) {
152 if (ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.defaultPrevented) {
153 return true
154 }
155
156 var anchor = null
157 for (var n = ev.target; n.parentNode; n = n.parentNode) {
158 if (n.nodeName === 'A') {
159 anchor = n
160 break
161 }
162 }
163 if (!anchor) return true
164
165 var href = anchor.getAttribute('href')
166
167 if (href) {
168 var url = Url.parse(href)
169 if (url.host) {
170 electron.shell.openExternal(href)
171 } else if (href.charAt(0) === '&') {
172 electron.shell.openExternal(api.blob.sync.url(href))
173 } else if (href !== '#') {
174 setView(href)
175 }
176 }
177
178 ev.preventDefault()
179 ev.stopPropagation()
180 }
181
182 function tab (name, view) {
183 var instance = views.get(view)
184 lastViewed[view] = true
185 return h('a', {
186 'ev-click': function (ev) {
187 if (instance.pendingUpdates && instance.pendingUpdates() && instance.reload) {
188 instance.reload()
189 }
190 },
191 href: view,
192 classList: [
193 when(selected(view), '-selected')
194 ]
195 }, [
196 name,
197 when(instance.pendingUpdates, [
198 ' (', instance.pendingUpdates, ')'
199 ])
200 ])
201 }
202
203 function goBack () {
204 if (backHistory.length) {
205
206 canGoForward.set(true)
207 forwardHistory.push(currentView())
208
209 var view = backHistory.pop()
210 if (!views.has(view)) {
211 views.put(view, renderPage(view))
212 }
213
214 currentView.set(view)
215 canGoBack.set(backHistory.length > 0)
216 }
217 }
218
219 function goForward () {
220 if (forwardHistory.length) {
221 backHistory.push(currentView())
222
223 var view = forwardHistory.pop()
224 if (!views.has(view)) {
225 views.put(view, renderPage(view))
226 }
227
228 currentView.set(view)
229 canGoForward.set(forwardHistory.length > 0)
230 canGoBack.set(true)
231 }
232 }
233
234 function setView (view) {
235 if (!views.has(view)) {
236 views.put(view, renderPage(view))
237 }
238
239 if (lastViewed[view] !== true) {
240 lastViewed[view] = Date.now()
241 }
242
243 if (currentView() && lastViewed[currentView()] !== true) {
244 lastViewed[currentView()] = Date.now()
245 }
246
247 if (view !== currentView()) {
248 canGoForward.set(false)
249 canGoBack.set(true)
250 forwardHistory.length = 0
251 backHistory.push(currentView())
252 currentView.set(view)
253 }
254 }
255
256 function doSearch () {
257 var value = searchBox.value.trim()
258 if (value.startsWith('/') || value.startsWith('?') || value.startsWith('@') || value.startsWith('#') || value.startsWith('%')) {
259 if (value.startsWith('@') && value.length < 30) {
260 return // probably not a key
261 } else if (value.length > 2) {
262 setView(value)
263 }
264 } else if (value.trim()) {
265 if (value.length > 2) {
266 setView(`?${value.trim()}`)
267 }
268 } else {
269 setView('/public')
270 }
271 }
272
273 function selected (view) {
274 return computed([currentView, view], (currentView, view) => {
275 return currentView === view
276 })
277 }
278}
279
280function overrideConfig (config) {
281 return [{
282 gives: nest('config.sync.load'),
283 create: function (api) {
284 return nest('config.sync.load', () => config)
285 }
286 }]
287}
288

Built with git-ssb-web