git ssb

1+

Daan Patchwork / patchwork



Tree: 2c11a9dc2541a07740f246f2ab4dd2d8e0b679fb

Files: 2c11a9dc2541a07740f246f2ab4dd2d8e0b679fb / index.js

9368 bytesRaw
1process.on('uncaughtException', function (err) {
2 console.log(err)
3 process.exit()
4})
5
6const electron = require('electron')
7const openWindow = require('./lib/window')
8
9const Path = require('path')
10const defaultMenu = require('electron-default-menu')
11const WindowState = require('electron-window-state')
12const Menu = electron.Menu
13const extend = require('xtend')
14const ssbKeys = require('ssb-keys')
15
16const windows = {
17 dialogs: new Set()
18}
19let ssbConfig = null
20let quitting = false
21
22/**
23 * It's not possible to run two instances of patchwork as it would create two
24 * ssb-server instances that conflict on the same port. Before opening patchwork,
25 * we check if it's already running and if it is we focus the existing window
26 * rather than opening a new instance.
27 */
28function quitIfAlreadyRunning () {
29 if (!electron.app.requestSingleInstanceLock()) {
30 console.log('Patchwork is already running!')
31 console.log('Please close the existing instance before starting a new one.')
32 return electron.app.quit()
33 }
34 electron.app.on('second-instance', () => {
35 // Someone tried to run a second instance, we should focus our window.
36 if (windows.main) {
37 if (windows.main.isMinimized()) windows.main.restore()
38 windows.main.focus()
39 }
40 })
41}
42
43const config = {
44 server: !(process.argv.includes('-g') || process.argv.includes('--use-global-ssb'))
45}
46// a flag so we don't start git-ssb-web if a custom path is passed in
47if (process.argv.includes('--path')) {
48 config.customPath = true
49}
50
51quitIfAlreadyRunning()
52
53electron.app.on('ready', () => {
54 setupContext(process.env.ssb_appname || 'ssb', {
55 server: !(process.argv.includes('-g') || process.argv.includes('--use-global-ssb'))
56 }, () => {
57 const browserWindow = openMainWindow()
58
59 browserWindow.on('app-command', (e, cmd) => {
60 switch (cmd) {
61 case 'browser-backward': {
62 browserWindow.webContents.send('goBack')
63 break
64 }
65 case 'browser-forward': {
66 browserWindow.webContents.send('goForward')
67 break
68 }
69 }
70 })
71
72 const menu = defaultMenu(electron.app, electron.shell)
73
74 menu.splice(4, 0, {
75 label: 'Navigation',
76 submenu: [
77 {
78 label: 'Activate Search Field',
79 accelerator: 'CmdOrCtrl+L',
80 click: () => {
81 browserWindow.webContents.send('activateSearch')
82 }
83 },
84 {
85 label: 'Back',
86 accelerator: 'CmdOrCtrl+[',
87 click: () => {
88 browserWindow.webContents.send('goBack')
89 }
90 },
91 {
92 label: 'Forward',
93 accelerator: 'CmdOrCtrl+]',
94 click: () => {
95 browserWindow.webContents.send('goForward')
96 }
97 },
98 {
99 type: 'separator'
100 },
101 {
102 label: 'Settings',
103 accelerator: 'CmdOrCtrl+,',
104 click: () => {
105 browserWindow.webContents.send('goToSettings')
106 }
107 },
108 {
109 label: 'Status',
110 accelerator: 'CmdOrCtrl+.',
111 click: () => {
112 browserWindow.webContents.send('goToStatus')
113 }
114 }
115 ]
116 })
117
118 const view = menu.find(x => x.label === 'View')
119 view.submenu = [
120 { role: 'reload' },
121 { role: 'toggledevtools' },
122 { type: 'separator' },
123 { role: 'resetzoom' },
124 { role: 'zoomin', accelerator: 'CmdOrCtrl+=' },
125 { role: 'zoomout', accelerator: 'CmdOrCtrl+-' },
126 { type: 'separator' },
127 { role: 'togglefullscreen' }
128 ]
129 const help = menu.find(x => x.label === 'Help')
130 help.submenu = [
131 {
132 label: 'Learn More',
133 click () { require('electron').shell.openExternal('https://scuttlebutt.nz') }
134 }
135 ]
136 if (process.platform === 'darwin') {
137 const win = menu.find(x => x.label === 'Window')
138 win.submenu = [
139 { role: 'minimize' },
140 { role: 'zoom' },
141 { role: 'close', label: 'Close' },
142 { type: 'separator' },
143 { role: 'front' }
144 ]
145 }
146
147 Menu.setApplicationMenu(Menu.buildFromTemplate(menu))
148 })
149
150 electron.app.on('activate', function () {
151 if (windows.main) {
152 windows.main.show()
153 }
154 })
155
156 electron.app.on('before-quit', function () {
157 quitting = true
158 })
159
160 electron.ipcMain.handle('navigation-menu-popup', (event, data) => {
161 const {items, x, y} = data
162 const window = event.sender
163 const factor = event.sender.zoomFactor
164 const menuItems = buildMenu(items, window)
165 const menu = electron.Menu.buildFromTemplate(menuItems);
166 menu.popup({
167 window,
168 x: Math.round(x * factor),
169 y: Math.round(y * factor) + 4,
170 });
171 })
172
173 electron.ipcMain.handle('setSpellcheckLangs', (ev, params) => {
174 if (!windows.main) { return }
175 const { langs, enabled } = params
176 windows.main.webContents.session.setSpellCheckerLanguages(enabled ? langs : []);
177 })
178 electron.ipcMain.handle('consoleLog', (ev, o) => console.log(o))
179 electron.ipcMain.handle('consoleError', (ev, o) => console.error(o))
180 electron.ipcMain.handle('badgeCount', (ev, count) => {
181 electron.app.badgeCount = count;
182 });
183 electron.ipcMain.on('exit', (ev, code) => process.exit(code))
184
185})
186
187function openServerDevTools () {
188 if (windows.background) {
189 windows.background.webContents.openDevTools({ mode: 'detach' })
190 }
191}
192
193function buildMenu(items, window) {
194 const result = []
195 for (let item of items) {
196 switch (item.type) {
197 case 'separator':
198 result.push(item)
199 break
200 case 'submenu':
201 result.push({
202 ...item,
203 submenu: buildMenu(item.submenu, window),
204 })
205 break
206 case 'normal':
207 result.push({
208 ...item,
209 click: () => navigateTo(item.target)
210 })
211 break
212 default:
213 throw Error(`Unknown menu item of type "${item.type}": ${JSON.stringify(item, null, 2)}`);
214 }
215 }
216 return result
217}
218
219function navigateTo(target) {
220 if (windows?.main) {
221 windows.main.send('navigate-to', target)
222 }
223}
224
225function openMainWindow () {
226 if (!windows.main) {
227 const windowState = WindowState({
228 defaultWidth: 1024,
229 defaultHeight: 768
230 })
231 windows.main = openWindow(ssbConfig, Path.join(__dirname, 'lib', 'main-window.js'), {
232 minWidth: 800,
233 x: windowState.x,
234 y: windowState.y,
235 width: windowState.width,
236 height: windowState.height,
237 titleBarStyle: 'hiddenInset',
238 autoHideMenuBar: true,
239 title: 'Patchwork',
240 show: true,
241 backgroundColor: '#EEE',
242 icon: Path.join(__dirname, 'assets/icon.png'),
243 },
244 openServerDevTools,
245 navigateTo,
246 )
247
248 windowState.manage(windows.main)
249 windows.main.setSheetOffset(40)
250 windows.main.on('close', function (e) {
251 if (!quitting && process.platform === 'darwin') {
252 e.preventDefault()
253 windows.main.hide()
254 }
255 })
256 windows.main.on('closed', function () {
257 windows.main = null
258 if (process.platform !== 'darwin') electron.app.quit()
259 })
260 }
261 return windows.main
262}
263
264function setupContext (appName, opts, cb) {
265 ssbConfig = require('ssb-config/inject')(appName, extend({
266 port: 8008,
267 blobsPort: 8989, // matches ssb-ws
268 friends: { // not using ssb-friends (sbot/contacts fixes hops at 2, so this setting won't do anything)
269 dunbar: 150,
270 hops: 2 // down from 3
271 }
272 }, opts))
273
274 // disable gossip auto-population from {type: 'pub'} messages as we handle this manually in sbot/index.js
275 if (!ssbConfig.gossip) ssbConfig.gossip = {}
276 ssbConfig.gossip.autoPopulate = false
277
278 ssbConfig.keys = ssbKeys.loadOrCreateSync(Path.join(ssbConfig.path, 'secret'))
279
280 const keys = ssbConfig.keys
281 const pubkey = keys.id.slice(1).replace(`.${keys.curve}`, '')
282
283 if (process.platform === 'win32') {
284 // fix offline on windows by specifying 127.0.0.1 instead of localhost (default)
285 ssbConfig.remote = `net:127.0.0.1:${ssbConfig.port}~shs:${pubkey}`
286 } else {
287 const socketPath = Path.join(ssbConfig.path, 'socket')
288 ssbConfig.connections.incoming.unix = [{ scope: 'device', transform: 'noauth' }]
289 ssbConfig.remote = `unix:${socketPath}:~noauth:${pubkey}`
290 }
291
292 // Support rooms
293 ssbConfig.connections.incoming.tunnel = [{ scope: 'public', transform: 'shs' }]
294 ssbConfig.connections.outgoing.tunnel = [{ transform: 'shs' }]
295
296 // Support DHT invites (only as a client, for now)
297 ssbConfig.connections.outgoing.dht = [{ transform: 'shs' }]
298
299 const redactedConfig = JSON.parse(JSON.stringify(ssbConfig))
300 redactedConfig.keys.private = null
301 console.dir(redactedConfig, { depth: null })
302
303 if (opts.server === false) {
304 cb && cb()
305 } else {
306 electron.ipcMain.once('server-started', function (ev, config) {
307 ssbConfig = config
308 cb && cb()
309 })
310 windows.background = openWindow(ssbConfig, Path.join(__dirname, 'lib', 'server-process.js'), {
311 connect: false,
312 center: true,
313 fullscreen: false,
314 fullscreenable: false,
315 height: 150,
316 maximizable: false,
317 minimizable: false,
318 resizable: false,
319 show: false,
320 skipTaskbar: true,
321 title: 'patchwork-server',
322 useContentSize: true,
323 width: 150
324 })
325 // windows.background.on('close', (ev) => {
326 // ev.preventDefault()
327 // windows.background.hide()
328 // })
329 }
330}
331

Built with git-ssb-web