Files: 1b789e63aa59f5d3e5539a4432c8b3c466977a0e / index.js
9368 bytesRaw
1 | process.on('uncaughtException', function (err) { |
2 | console.log(err) |
3 | process.exit() |
4 | }) |
5 | |
6 | const electron = require('electron') |
7 | const openWindow = require('./lib/window') |
8 | |
9 | const Path = require('path') |
10 | const defaultMenu = require('electron-default-menu') |
11 | const WindowState = require('electron-window-state') |
12 | const Menu = electron.Menu |
13 | const extend = require('xtend') |
14 | const ssbKeys = require('ssb-keys') |
15 | |
16 | const windows = { |
17 | dialogs: new Set() |
18 | } |
19 | let ssbConfig = null |
20 | let 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 | */ |
28 | function 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 | |
43 | const 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 |
47 | if (process.argv.includes('--path')) { |
48 | config.customPath = true |
49 | } |
50 | |
51 | quitIfAlreadyRunning() |
52 | |
53 | electron.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 | |
187 | function openServerDevTools () { |
188 | if (windows.background) { |
189 | windows.background.webContents.openDevTools({ mode: 'detach' }) |
190 | } |
191 | } |
192 | |
193 | function 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 | |
219 | function navigateTo(target) { |
220 | if (windows?.main) { |
221 | windows.main.send('navigate-to', target) |
222 | } |
223 | } |
224 | |
225 | function 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 | |
264 | function 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