git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 3d066cf8a7445f30f1c19065098c5b87acb82a80

Files: 3d066cf8a7445f30f1c19065098c5b87acb82a80 / main-window.js

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

Built with git-ssb-web