git ssb

10+

Matt McKegg / patchwork



Tree: 3d5a912d4f26780685c60d2ccc930cd6dd9567cc

Files: 3d5a912d4f26780685c60d2ccc930cd6dd9567cc / main-window.js

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

Built with git-ssb-web