git ssb

0+

alanz / patchwork



forked from Matt McKegg / patchwork

Tree: c2bf836f7a466f336a9335100afcbe4df76f27ce

Files: c2bf836f7a466f336a9335100afcbe4df76f27ce / main-window.js

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

Built with git-ssb-web