git ssb

10+

Matt McKegg / patchwork



Tree: 54230cc515f74edc6c4b0530f202d28f2e999774

Files: 54230cc515f74edc6c4b0530f202d28f2e999774 / main-window.js

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

Built with git-ssb-web