git ssb

10+

Matt McKegg / patchwork



Tree: d56fe5e284a2a1b36f5b540a214aedd8c458bdcd

Files: d56fe5e284a2a1b36f5b540a214aedd8c458bdcd / main-window.js

7936 bytesRaw
1var combine = require('depject')
2var entry = require('depject/entry')
3var electron = require('electron')
4var h = require('mutant/h')
5var when = require('mutant/when')
6var onceTrue = require('mutant/once-true')
7var computed = require('mutant/computed')
8var catchLinks = require('./lib/catch-links')
9var themes = require('./styles')
10var nest = require('depnest')
11var LatestUpdate = require('./lib/latest-update')
12var ref = require('ssb-ref')
13var setupContextMenuAndSpellCheck = require('./lib/context-menu-and-spellcheck')
14var watch = require('mutant/watch')
15
16module.exports = function (config) {
17 var sockets = combine(
18 overrideConfig(config),
19 addCommand('app.navigate', setView),
20 require('./modules'),
21 require('./plugs'),
22 require('patch-settings'),
23 require('patchcore'),
24 require('./overrides')
25 )
26
27 var api = entry(sockets, nest({
28 'config.sync.load': 'first',
29 'keys.sync.id': 'first',
30 'sbot.obs.connection': 'first',
31 'sbot.async.get': 'first',
32 'blob.sync.url': 'first',
33 'page.html.render': 'first',
34 'app.html.search': 'first',
35 'app.html.channels': 'first',
36 'app.views': 'first',
37 'app.fullscreen': 'first',
38 'app.sync.externalHandler': 'first',
39 'app.html.progressNotifier': 'first',
40 'profile.sheet.edit': 'first',
41 'profile.html.preview': 'first',
42 'app.navigate': 'first',
43 'app.linkPreview': 'first',
44 'channel.obs.subscribed': 'first',
45 'settings.obs.get': 'first',
46 'intl.sync.i18n': 'first'
47 }))
48
49 setupContextMenuAndSpellCheck(api.config.sync.load())
50
51 const i18n = api.intl.sync.i18n
52
53 var id = api.keys.sync.id()
54 var latestUpdate = LatestUpdate()
55 var subscribedChannels = api.channel.obs.subscribed(id)
56
57 // prompt to setup profile on first use
58 onceTrue(api.sbot.obs.connection, (sbot) => {
59 sbot.latestSequence(sbot.id, (_, key) => {
60 if (key == null) {
61 api.profile.sheet.edit()
62 }
63 })
64 })
65
66 var views = api.app.views(api.page.html.render, [
67 '/public', '/private', id, '/mentions'
68 ])
69
70 var pendingCount = views.get('/mentions').pendingUpdates
71
72 watch(pendingCount, count => {
73 electron.remote.app.setBadgeCount(count)
74 })
75
76 document.head.appendChild(
77 h('style', {
78 innerHTML: computed(api.settings.obs.get('patchwork.theme', 'light'), themeName => {
79 return themes[themeName] || themes['light']
80 })
81 })
82 )
83
84 document.head.appendChild(
85 h('style', {
86 innerHTML: computed(api.settings.obs.get('patchwork.fontSize'), size => {
87 if (size) {
88 return 'html, body {font-size: ' + size + ';}'
89 }
90 })
91 })
92 )
93
94 var container = h(`MainWindow -${process.platform}`, {
95 classList: [ when(api.app.fullscreen(), '-fullscreen') ]
96 }, [
97 h('div.top', [
98 h('span.history', [
99 h('a', {
100 'ev-click': views.goBack,
101 classList: [ when(views.canGoBack, '-active') ]
102 }),
103 h('a', {
104 'ev-click': views.goForward,
105 classList: [ when(views.canGoForward, '-active') ]
106 })
107 ]),
108 h('span.nav', [
109 tab(i18n('Public'), '/public'),
110 tab(i18n('Private'), '/private'),
111 dropTab(i18n('More'), [
112 getSubscribedChannelMenu,
113 [i18n('Gatherings'), '/gatherings'],
114 [i18n('Extended Network'), '/all'],
115 {separator: true},
116 [i18n('Settings'), '/settings']
117 ])
118 ]),
119 h('span.appTitle', [
120 h('span.title', i18n('Patchwork')),
121 api.app.html.progressNotifier()
122 ]),
123 h('span', [ api.app.html.search(api.app.navigate) ]),
124 h('span.nav', [
125 tab(i18n('Profile'), id),
126 tab(i18n('Mentions'), '/mentions')
127 ])
128 ]),
129 when(latestUpdate,
130 h('div.info', [
131 h('a.message -update', { href: 'https://github.com/ssbc/patchwork/releases' }, [
132 h('strong', ['Patchwork ', latestUpdate, i18n(' has been released.')]), i18n(' Click here to download and view more info!'),
133 h('a.ignore', {'ev-click': latestUpdate.ignore}, 'X')
134 ])
135 ])
136 ),
137 views.html
138 ])
139
140 var previewElement = api.app.linkPreview(container, 500)
141
142 catchLinks(container, (href, external, anchor) => {
143 if (external) {
144 electron.shell.openExternal(href)
145 } else if (href && href.startsWith('&')) { // (ref.isBlob(href)) {
146 electron.shell.openExternal(api.blob.sync.url(href))
147 } else if (ref.isMsg(href)) {
148 getExternalHandler(href, (err, handler) => {
149 if (!err && handler) {
150 handler(href)
151 } else {
152 api.app.navigate(href, anchor)
153 }
154 })
155 } else {
156 api.app.navigate(href, anchor)
157 }
158 })
159
160 return [container, previewElement]
161
162 // scoped
163
164 function getSubscribedChannelMenu () {
165 var channels = Array.from(subscribedChannels()).sort(localeCompare)
166
167 if (channels.length) {
168 return {
169 label: i18n('Channels'),
170 submenu: [
171 { label: i18n('Browse All'),
172 click () {
173 setView('/channels')
174 }
175 },
176 {type: 'separator'}
177 ].concat(channels.map(channel => {
178 return {
179 label: `#${channel}`,
180 click () {
181 setView(`#${channel}`)
182 }
183 }
184 }))
185 }
186 } else {
187 return {
188 label: i18n('Browse Channels'),
189 click () {
190 setView('/channels')
191 }
192 }
193 }
194 }
195
196 function dropTab (title, items) {
197 var element = h('a -drop', {
198 'ev-click': (ev) => {
199 var rects = element.getBoundingClientRect()
200 electron.remote.getCurrentWindow().webContents.getZoomFactor((factor) => {
201 var menu = electron.remote.Menu.buildFromTemplate(items.map(item => {
202 if (typeof item === 'function') {
203 return item()
204 } else if (item.separator) {
205 return { type: 'separator' }
206 } else {
207 return {
208 label: item[0],
209 click () {
210 setView(item[1])
211 }
212 }
213 }
214 }))
215 menu.popup(electron.remote.getCurrentWindow(), {
216 x: Math.round(rects.left * factor),
217 y: Math.round(rects.bottom * factor) + 4,
218 async: true
219 })
220 })
221 }
222 }, title)
223 return element
224 }
225
226 function setView (href, anchor) {
227 previewElement.cancel()
228 views.setView(href, anchor)
229 }
230
231 function getExternalHandler (key, cb) {
232 api.sbot.async.get(key, function (err, value) {
233 if (err) return cb(err)
234 cb(null, api.app.sync.externalHandler({key, value}))
235 })
236 }
237
238 function tab (name, view) {
239 var instance = views.get(view)
240 return h('a', {
241 'ev-click': function (ev) {
242 var isSelected = views.currentView() === view
243 var needsRefresh = instance.pendingUpdates && instance.pendingUpdates()
244
245 // refresh if tab is clicked when there are pending items or the page is already selected
246 if ((needsRefresh || isSelected) && instance.reload) {
247 instance.reload()
248 }
249 },
250 href: view,
251 classList: [
252 when(selected(view), '-selected')
253 ]
254 }, [
255 name,
256 when(instance.pendingUpdates, [
257 ' (', instance.pendingUpdates, ')'
258 ])
259 ])
260 }
261
262 function selected (view) {
263 return computed([views.currentView, view], (currentView, view) => {
264 return currentView === view
265 })
266 }
267}
268
269function overrideConfig (config) {
270 return {
271 'patchwork/config': {
272 gives: nest('config.sync.load'),
273 create: function (api) {
274 return nest('config.sync.load', () => config)
275 }
276 }
277 }
278}
279
280function addCommand (id, cb) {
281 return {
282 [`patchwork/command/${id}`]: {
283 gives: nest(id),
284 create: function (api) {
285 return nest(id, cb)
286 }
287 }
288 }
289}
290
291function localeCompare (a, b) {
292 return a.localeCompare(b)
293}
294

Built with git-ssb-web