git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 9cef1e5a6f8635bc015c83e35effb07ad1e49ac6

Files: 9cef1e5a6f8635bc015c83e35effb07ad1e49ac6 / main-window.js

5622 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')
14
15module.exports = function (config) {
16 var sockets = combine(
17 overrideConfig(config),
18 require('./modules'),
19 require('./plugs'),
20 require('patchcore'),
21 require('./overrides')
22 )
23
24 var api = entry(sockets, nest({
25 'page.html.render': 'first',
26 'keys.sync.id': 'first'
27 }))
28
29 var renderPage = api.page.html.render
30 var id = api.keys.sync.id()
31
32 var searchTimer = null
33 var searchBox = h('input.search', {
34 type: 'search',
35 placeholder: 'word, @key, #channel'
36 })
37
38 searchBox.oninput = function () {
39 clearTimeout(searchTimer)
40 searchTimer = setTimeout(doSearch, 500)
41 }
42
43 searchBox.onfocus = function () {
44 if (searchBox.value) {
45 doSearch()
46 }
47 }
48
49 var forwardHistory = []
50 var backHistory = []
51
52 var views = MutantDict({
53 // preload tabs (and subscribe to update notifications)
54 '/public': renderPage('/public'),
55 '/private': renderPage('/private'),
56 [id]: renderPage(id),
57 // '/notifications': renderPage('/notifications')
58 })
59
60 var lastViewed = {}
61 var defaultViews = views.keys()
62
63 // delete cached view after 30 mins of last seeing
64 setInterval(() => {
65 views.keys().forEach((view) => {
66 if (!defaultViews.includes(view)) {
67 if (lastViewed[view] !== true && Date.now() - lastViewed[view] > (5 * 60e3) && view !== currentView()) {
68 views.delete(view)
69 }
70 }
71 })
72 }, 60e3)
73
74 var canGoForward = Value(false)
75 var canGoBack = Value(false)
76 var currentView = Value('/public')
77
78 var mainElement = h('div.main', MutantMap(toCollection(views), (item) => {
79 return h('div.view', {
80 hidden: computed([item.key, currentView], (a, b) => a !== b)
81 }, [ item.value ])
82 }))
83
84 insertCss(require('./styles'))
85
86 return h(`MainWindow -${process.platform}`, {
87 events: {
88 click: catchLinks
89 }
90 }, [
91 h('div.top', [
92 h('span.history', [
93 h('a', {
94 'ev-click': goBack,
95 classList: [ when(canGoBack, '-active') ]
96 }, '<'),
97 h('a', {
98 'ev-click': goForward,
99 classList: [ when(canGoForward, '-active') ]
100 }, '>')
101 ]),
102 h('span.nav', [
103 tab('Public', '/public'),
104 tab('Private', '/private')
105 ]),
106 h('span.appTitle', ['Patchwork']),
107 h('span', [ searchBox ]),
108 h('span.nav', [
109 tab('Profile', id),
110 // tab('Mentions', '/notifications')
111 ])
112 ]),
113 mainElement
114 ])
115
116 // scoped
117
118 function catchLinks (ev) {
119 if (ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.defaultPrevented) {
120 return true
121 }
122
123 var anchor = null
124 for (var n = ev.target; n.parentNode; n = n.parentNode) {
125 if (n.nodeName === 'A') {
126 anchor = n
127 break
128 }
129 }
130 if (!anchor) return true
131
132 var href = anchor.getAttribute('href')
133
134 if (href) {
135 var url = Url.parse(href)
136 if (url.host) {
137 electron.shell.openExternal(href)
138 } else if (href !== '#') {
139 setView(href)
140 }
141 }
142
143 ev.preventDefault()
144 ev.stopPropagation()
145 }
146
147 function tab (name, view) {
148 var instance = views.get(view)
149 lastViewed[view] = true
150 return h('a', {
151 'ev-click': function (ev) {
152 if (instance.pendingUpdates && instance.pendingUpdates() && instance.reload) {
153 instance.reload()
154 }
155 },
156 href: view,
157 classList: [
158 when(selected(view), '-selected')
159 ]
160 }, [
161 name,
162 when(instance.pendingUpdates, [
163 ' (', instance.pendingUpdates, ')'
164 ])
165 ])
166 }
167
168 function goBack () {
169 if (backHistory.length) {
170 canGoForward.set(true)
171 forwardHistory.push(currentView())
172 currentView.set(backHistory.pop())
173 canGoBack.set(backHistory.length > 0)
174 }
175 }
176
177 function goForward () {
178 if (forwardHistory.length) {
179 backHistory.push(currentView())
180 currentView.set(forwardHistory.pop())
181 canGoForward.set(forwardHistory.length > 0)
182 canGoBack.set(true)
183 }
184 }
185
186 function setView (view) {
187 if (!views.has(view)) {
188 views.put(view, renderPage(view))
189 }
190
191 if (lastViewed[view] !== true) {
192 lastViewed[view] = Date.now()
193 }
194
195 if (currentView() && lastViewed[currentView()] !== true) {
196 lastViewed[currentView()] = Date.now()
197 }
198
199 if (view !== currentView()) {
200 canGoForward.set(false)
201 canGoBack.set(true)
202 forwardHistory.length = 0
203 backHistory.push(currentView())
204 currentView.set(view)
205 }
206 }
207
208 function doSearch () {
209 var value = searchBox.value.trim()
210 if (value.startsWith('/') || value.startsWith('?') || value.startsWith('@') || value.startsWith('#') || value.startsWith('%')) {
211 setView(value)
212 } else if (value.trim()) {
213 setView(`?${value.trim()}`)
214 } else {
215 setView('/public')
216 }
217 }
218
219 function selected (view) {
220 return computed([currentView, view], (currentView, view) => {
221 return currentView === view
222 })
223 }
224}
225
226function overrideConfig (config) {
227 return [{
228 gives: nest('config.sync.load'),
229 create: function (api) {
230 return nest('config.sync.load', () => config)
231 }
232 }]
233}
234

Built with git-ssb-web