git ssb

2+

mixmix / ticktack



Tree: eb1f4dc0536fe618895a84955b5f44818876c5e1

Files: eb1f4dc0536fe618895a84955b5f44818876c5e1 / ftu / app.js

7186 bytesRaw
1const { h, Value, when, resolve, computed, Struct, watch, throttle } = require('mutant')
2const nest = require('depnest')
3const path = require('path')
4const fs = require('fs')
5const { remote } = require('electron')
6const insertCss = require('insert-css')
7const values = require('lodash/values')
8const electron = require('electron')
9const { dialog } = require('electron').remote
10const os = require('os')
11const appName = process.env.SSB_APPNAME || 'ssb'
12const configFolder = path.join(os.homedir(), `.${appName}`)
13
14var isBusy = Value(false)
15var isPresentingOptions = Value(true)
16
17// these initial values are overwritten by the identity file.
18var state = Struct({
19 latestSequence: 0,
20 currentSequence: -1
21})
22
23exports.gives = nest('ftu.app')
24
25exports.needs = nest({
26 'styles.css': 'reduce',
27 'translations.sync.strings': 'first',
28})
29
30exports.create = (api) => {
31 return nest({
32 'ftu.app': function app() {
33
34 const strings = api.translations.sync.strings()
35
36 const css = values(api.styles.css()).join('\n')
37 insertCss(css)
38
39 var actionButtons = h('section', [
40 h('div.left', h('Button', { 'ev-click': () => actionImportIdentity(strings) }, strings.backup.ftu.importAction)),
41 h('div.right', h('Button', { 'ev-click': () => actionCreateNewOne() }, strings.backup.ftu.createAction))
42 ])
43
44 var busyMessage = h('p', strings.backup.ftu.busyMessage)
45
46 var initialOptions = h('Page -ftu', [
47 h('div.content', [
48 h('h1', strings.backup.ftu.welcomeHeader),
49 h('p', strings.backup.ftu.welcomeMessage),
50 when(isBusy, busyMessage, actionButtons)
51 ])
52 ])
53
54 var importProcess = h('Page -ftu', [
55 h('div.content', [
56 h('h1', strings.backup.import.header),
57 h('p', [strings.backup.import.synchronizeMessage, state.currentSequence, '/', state.latestSequence]),
58 ])
59 ])
60
61 // This watcher is responsible for switching from FTU to Ticktack main app
62 watch(throttle(state, 500), s => {
63 if (s.currentSequence >= s.latestSequence) {
64 console.log('all imported')
65 electron.ipcRenderer.send('import-completed')
66 }
67 })
68
69 if (fs.existsSync(path.join(configFolder, "secret"))) {
70 // somehow the FTU started but the identity is already in place.
71 // treat it as a failed import and start importing...
72 console.log('resuming import')
73 let previousData = getImportData()
74 if (previousData === false) {
75 // there is a secret but there is no previous import data.
76 // so, we proceed as normal because we can't do anything else,
77 // it looks like a normal standard installation...
78 setImportData({ importing: false })
79 electron.ipcRenderer.send('import-completed')
80 } else {
81 state.latestSequence.set(previousData.latestSequence)
82 state.currentSequence.set(previousData.currentSequence)
83 isPresentingOptions.set(false)
84 observeSequence()
85 }
86 }
87
88 var app = h('App', [
89 h('Header', [
90 windowControls()
91 ]),
92 when(isPresentingOptions, initialOptions, importProcess)
93 ])
94
95 return app
96 }
97 })
98}
99
100electron.ipcRenderer.on('import-started', function (ev, c) {
101 console.log('background process is running, begin observing')
102
103 observeSequence()
104})
105
106
107
108function actionCreateNewOne() {
109 isBusy.set(true)
110 const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, "../manifest.json")))
111 const manifestFile = path.join(configFolder, 'manifest.json')
112 if (!fs.existsSync(configFolder)) {
113 fs.mkdirSync(configFolder)
114 }
115 fs.writeFileSync(manifestFile, JSON.stringify(manifest))
116
117
118 electron.ipcRenderer.send('create-new-identity')
119}
120
121function actionImportIdentity(strings) {
122 const peersFile = path.join(configFolder, "gossip.json")
123 const secretFile = path.join(configFolder, "secret")
124 const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, "../manifest.json")))
125 const manifestFile = path.join(configFolder, 'manifest.json')
126
127 // place the other files first
128 dialog.showOpenDialog(
129 {
130 title: strings.backup.import.dialog.title,
131 butttonLabel: strings.backup.import.dialog.label,
132 defaultPath: 'ticktack-identity.backup',
133 properties: ['openFile']
134 },
135 (filenames) => {
136 if (typeof filenames !== "undefined") {
137 let filename = filenames[0]
138 let data = JSON.parse(fs.readFileSync(filename))
139 if (data.hasOwnProperty("secret") && data.hasOwnProperty("peers") && data.hasOwnProperty("latestSequence")) {
140 if (!fs.existsSync(configFolder)) {
141 fs.mkdirSync(configFolder)
142 }
143
144 fs.writeFileSync(manifestFile, JSON.stringify(manifest))
145 fs.writeFileSync(peersFile, JSON.stringify(data.peers), "utf8")
146 fs.writeFileSync(secretFile, data.secret, "utf8")
147 state.latestSequence.set(data.latestSequence)
148 state.currentSequence.set(0)
149 isPresentingOptions.set(false)
150
151 data.importing = true
152 data.currentSequence = 0
153
154 setImportData(data)
155
156 electron.ipcRenderer.send('import-identity')
157 } else {
158 console.log("> bad export file")
159 console.log(data)
160 alert("Bad Export File")
161 }
162 }
163 }
164 )
165}
166
167function windowControls() {
168 if (process.platform === 'darwin') return
169
170 const window = remote.getCurrentWindow()
171 const minimize = () => window.minimize()
172 const maximize = () => {
173 if (!window.isMaximized()) window.maximize()
174 else window.unmaximize()
175 }
176 const close = () => window.close()
177
178 return h('div.window-controls', [
179 h('img.min', {
180 src: assetPath('minimize.png'),
181 'ev-click': minimize
182 }),
183 h('img.max', {
184 src: assetPath('maximize.png'),
185 'ev-click': maximize
186 }),
187 h('img.close', {
188 src: assetPath('close.png'),
189 'ev-click': close
190 })
191 ])
192}
193
194
195function assetPath(name) {
196 return path.join(__dirname, '../assets', name)
197}
198
199
200function getImportData() {
201 var importFile = path.join(configFolder, 'importing.json')
202 if (fs.existsSync(importFile)) {
203 let data = JSON.parse(fs.readFileSync(importFile))
204 return data || false
205 } else {
206 return false
207 }
208}
209
210function setImportData(data) {
211 var importFile = path.join(configFolder, 'importing.json')
212 fs.writeFileSync(importFile, JSON.stringify(data))
213}
214
215function observeSequence() {
216 const pull = require('pull-stream')
217 const Client = require('ssb-client')
218 const config = require('../config').create().config.sync.load()
219 const _ = require('lodash')
220
221 Client(config.keys, config, (err, ssbServer) => {
222 if (err) {
223 console.error('problem starting client', err)
224 } else {
225 console.log('> sbot running!!!!')
226
227 var feedSource = ssbServer.createUserStream({
228 live: true,
229 id: ssbServer.id
230 })
231
232 var valueLogger = pull.drain((msg) => {
233 let seq = _.get(msg, "value.sequence", false)
234 if (seq) {
235 state.currentSequence.set(seq)
236 }
237 })
238
239 pull(
240 feedSource,
241 valueLogger,
242 )
243
244 }
245 })
246}

Built with git-ssb-web