git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 18d5d5c12c41651882d96e8d597c169e95dc4e73

Files: 18d5d5c12c41651882d96e8d597c169e95dc4e73 / main-window.js

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

Built with git-ssb-web