git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: 56ec071bbd3b367a7e702487f671bc947642e52c

Files: 56ec071bbd3b367a7e702487f671bc947642e52c / main-window.js

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

Built with git-ssb-web