git ssb

1+

Daan Patchwork / patchwork



Tree: 1ba77b6b13b6ff3be4180dc4de6c449ba4570d90

Files: 1ba77b6b13b6ff3be4180dc4de6c449ba4570d90 / index.js

9140 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('consoleLog', (ev, o) => console.log(o))
174 electron.ipcMain.handle('consoleError', (ev, o) => console.error(o))
175 electron.ipcMain.handle('badgeCount', (ev, count) => {
176 electron.app.badgeCount = count;
177 });
178 electron.ipcMain.on('exit', (ev, code) => process.exit(code))
179
180})
181
182function openServerDevTools () {
183 if (windows.background) {
184 windows.background.webContents.openDevTools({ mode: 'detach' })
185 }
186}
187
188function buildMenu(items, window) {
189 const result = []
190 for (let item of items) {
191 switch (item.type) {
192 case 'separator':
193 result.push(item)
194 break
195 case 'submenu':
196 result.push({
197 ...item,
198 submenu: buildMenu(item.submenu, window),
199 })
200 break
201 case 'normal':
202 result.push({
203 ...item,
204 click: () => navigateTo(item.target)
205 })
206 break
207 default:
208 throw Error(`Unknown menu item of type "${item.type}": ${JSON.stringify(item, null, 2)}`);
209 }
210 }
211 return result
212}
213
214function navigateTo(target) {
215 if (windows?.main) {
216 windows.main.send('navigate-to', target)
217 }
218}
219
220function openMainWindow () {
221 if (!windows.main) {
222 const windowState = WindowState({
223 defaultWidth: 1024,
224 defaultHeight: 768
225 })
226 windows.main = openWindow(ssbConfig, Path.join(__dirname, 'lib', 'main-window.js'), {
227 minWidth: 800,
228 x: windowState.x,
229 y: windowState.y,
230 width: windowState.width,
231 height: windowState.height,
232 titleBarStyle: 'hiddenInset',
233 autoHideMenuBar: true,
234 title: 'Patchwork',
235 show: true,
236 backgroundColor: '#EEE',
237 icon: Path.join(__dirname, 'assets/icon.png'),
238 },
239 openServerDevTools,
240 navigateTo,
241 )
242
243 windowState.manage(windows.main)
244 windows.main.setSheetOffset(40)
245 windows.main.on('close', function (e) {
246 if (!quitting && process.platform === 'darwin') {
247 e.preventDefault()
248 windows.main.hide()
249 }
250 })
251 windows.main.on('closed', function () {
252 windows.main = null
253 if (process.platform !== 'darwin') electron.app.quit()
254 })
255 }
256 return windows.main
257}
258
259function setupContext (appName, opts, cb) {
260 ssbConfig = require('ssb-config/inject')(appName, extend({
261 port: 8008,
262 blobsPort: 8989, // matches ssb-ws
263 friends: { // not using ssb-friends (sbot/contacts fixes hops at 2, so this setting won't do anything)
264 dunbar: 150,
265 hops: 2 // down from 3
266 }
267 }, opts))
268
269 // disable gossip auto-population from {type: 'pub'} messages as we handle this manually in sbot/index.js
270 if (!ssbConfig.gossip) ssbConfig.gossip = {}
271 ssbConfig.gossip.autoPopulate = false
272
273 ssbConfig.keys = ssbKeys.loadOrCreateSync(Path.join(ssbConfig.path, 'secret'))
274
275 const keys = ssbConfig.keys
276 const pubkey = keys.id.slice(1).replace(`.${keys.curve}`, '')
277
278 if (process.platform === 'win32') {
279 // fix offline on windows by specifying 127.0.0.1 instead of localhost (default)
280 ssbConfig.remote = `net:127.0.0.1:${ssbConfig.port}~shs:${pubkey}`
281 } else {
282 const socketPath = Path.join(ssbConfig.path, 'socket')
283 ssbConfig.connections.incoming.unix = [{ scope: 'device', transform: 'noauth' }]
284 ssbConfig.remote = `unix:${socketPath}:~noauth:${pubkey}`
285 }
286
287 // Support rooms
288 ssbConfig.connections.incoming.tunnel = [{ scope: 'public', transform: 'shs' }]
289 ssbConfig.connections.outgoing.tunnel = [{ transform: 'shs' }]
290
291 // Support DHT invites (only as a client, for now)
292 ssbConfig.connections.outgoing.dht = [{ transform: 'shs' }]
293
294 const redactedConfig = JSON.parse(JSON.stringify(ssbConfig))
295 redactedConfig.keys.private = null
296 console.dir(redactedConfig, { depth: null })
297
298 if (opts.server === false) {
299 cb && cb()
300 } else {
301 electron.ipcMain.once('server-started', function (ev, config) {
302 ssbConfig = config
303 cb && cb()
304 })
305 windows.background = openWindow(ssbConfig, Path.join(__dirname, 'lib', 'server-process.js'), {
306 connect: false,
307 center: true,
308 fullscreen: false,
309 fullscreenable: false,
310 height: 150,
311 maximizable: false,
312 minimizable: false,
313 resizable: false,
314 show: false,
315 skipTaskbar: true,
316 title: 'patchwork-server',
317 useContentSize: true,
318 width: 150
319 })
320 // windows.background.on('close', (ev) => {
321 // ev.preventDefault()
322 // windows.background.hide()
323 // })
324 }
325}
326

Built with git-ssb-web