git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 95b78ea2b31b86864bff7ce9ef9922187a02daa1

Files: 95b78ea2b31b86864bff7ce9ef9922187a02daa1 / main-window.js

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

Built with git-ssb-web