git ssb

2+

mixmix / ticktack



Commit a712df77a26a0ced340c16b0f754794049f19fe2

importing and creating identities working #122

andre alves garzia committed on 5/20/2018, 8:09:22 PM
Parent: e828eb68d13e6361f6c31b66a89a0b880ec6f257

Files changed

backup/async/exportIdentity.jschanged
backup/async/importIdentity.jsadded
backup/index.jschanged
ftu/app.jschanged
ftu/index.jschanged
index.jschanged
translations/en.jschanged
backup/async/exportIdentity.jsView
@@ -2,12 +2,11 @@
22 const { onceTrue } = require('mutant')
33 const path = require('path')
44 const fs = require('fs')
55 const os = require('os')
6-const homedir = os.homedir()
7-const ssbPath = `${homedir}/.ssb/`
8-const peersFile = path.join(homedir, ".ssb", "gossip.json")
9-const secretFile = path.join(homedir, ".ssb", "secret")
6+const config = require('../../config').create().config.sync.load()
7+const peersFile = path.join(config.path, "gossip.json")
8+const secretFile = path.join(config.path, "secret")
109
1110
1211 exports.gives = nest('backup.async.exportIdentity')
1312
backup/async/importIdentity.jsView
@@ -1,0 +1,23 @@
1+const nest = require('depnest')
2+const { onceTrue } = require('mutant')
3+const path = require('path')
4+const fs = require('fs')
5+const os = require('os')
6+const config = require('../../config').create().config.sync.load()
7+const peersFile = path.join(config.path, "gossip.json")
8+const secretFile = path.join(config.path, "secret")
9+
10+// TODO: files should take into account env vars
11+
12+exports.gives = nest('backup.async.importIdentity')
13+
14+exports.create = function (api) {
15+ return nest('backup.async.importIdentity', (importData, cb) => {
16+
17+ fs.writeFileSync(peersFile, JSON.stringify(importData.peers), "utf8")
18+ fs.writeFileSync(secretFile, importData.secret, "utf8")
19+
20+ cb()
21+
22+ })
23+}
backup/index.jsView
@@ -2,7 +2,8 @@
22 html: {
33 backup: require('./html/backup')
44 },
55 async: {
6- exportIdentity: require('./async/exportIdentity')
6+ exportIdentity: require('./async/exportIdentity'),
7+ importIdentity: require('./async/importIdentity')
78 }
89 }
ftu/app.jsView
@@ -1,19 +1,31 @@
1-const { h, Value, when } = require('mutant')
1+const { h, Value, when, resolve, computed, Struct, watch } = require('mutant')
22 const nest = require('depnest')
33 const path = require('path')
44 const fs = require('fs')
55 const { remote } = require('electron')
66 const insertCss = require('insert-css')
77 const values = require('lodash/values')
88 const electron = require('electron')
9+const { dialog } = require('electron').remote
10+const os = require('os')
11+const appName = process.env.SSB_APPNAME || 'ssb'
12+const configFolder = path.join(os.homedir(), `.${appName}`)
913
14+var isBusy = Value(false)
15+var isPresentingOptions = Value(true)
1016
17+// these initial values are overwritten by the identity file.
18+var state = Struct({
19+ latestSequence: 0,
20+ currentSequence: -1
21+})
22+
1123 exports.gives = nest('ftu.app')
1224
1325 exports.needs = nest({
1426 'styles.css': 'reduce',
15- 'translations.sync.strings': 'first'
27+ 'translations.sync.strings': 'first',
1628 })
1729
1830 exports.create = (api) => {
1931 return nest({
@@ -23,47 +35,128 @@
2335
2436 const css = values(api.styles.css()).join('\n')
2537 insertCss(css)
2638
27- var isBusy = Value(false)
28-
2939 var actionButtons = h('section', [
30- h('div.left', h('Button', strings.backup.ftu.importAction)),
31- h('div.right', h('Button', { 'ev-click': () => actionCreateNewOne(isBusy) }, strings.backup.ftu.createAction))
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))
3242 ])
3343
3444 var busyMessage = h('p', strings.backup.ftu.busyMessage)
3545
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(state, 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+ state.latestSequence.set(previousData.latestSequence)
75+ state.currentSequence.set(previousData.currentSequence)
76+ isPresentingOptions.set(false)
77+ observeSequence()
78+ }
79+
3680 var app = h('App', [
3781 h('Header', [
3882 windowControls()
3983 ]),
40- h('Page -ftu', [
41- h('div.content', [
42- h('h1', strings.backup.ftu.welcomeHeader),
43- h('p', strings.backup.ftu.welcomeMessage),
44- when(isBusy, busyMessage, actionButtons)
45- ])
46- ])
84+ when(isPresentingOptions, initialOptions, importProcess)
4785 ])
4886
4987 return app
5088 }
51-
5289 })
53-
5490 }
5591
56-function actionCreateNewOne(isBusy) {
92+electron.ipcRenderer.on('import-started', function (ev, c) {
93+ console.log('background process is running, begin observing')
94+
95+ observeSequence()
96+})
97+
98+
99+
100+function actionCreateNewOne() {
57101 isBusy.set(true)
58- const config = require('../config').create().config.sync.load()
59102 const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, "../manifest.json")))
60- fs.writeFileSync(path.join(config.path, 'manifest.json'), JSON.stringify(manifest))
103+ const manifestFile = path.join(configFolder, 'manifest.json')
104+ if (!fs.existsSync(configFolder)) {
105+ fs.mkdirSync(configFolder)
106+ }
107+ fs.writeFileSync(manifestFile, JSON.stringify(manifest))
61108
62109
63110 electron.ipcRenderer.send('create-new-identity')
64111 }
65112
113+function actionImportIdentity(strings) {
114+ const peersFile = path.join(configFolder, "gossip.json")
115+ const secretFile = path.join(configFolder, "secret")
116+ const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, "../manifest.json")))
117+ const manifestFile = path.join(configFolder, 'manifest.json')
118+
119+ // place the other files first
120+ dialog.showOpenDialog(
121+ {
122+ title: strings.backup.import.dialog.title,
123+ butttonLabel: strings.backup.import.dialog.label,
124+ defaultPath: 'ticktack-identity.backup',
125+ properties: ['openFile']
126+ },
127+ (filenames) => {
128+ if (typeof filenames !== "undefined") {
129+ let filename = filenames[0]
130+ let data = JSON.parse(fs.readFileSync(filename))
131+ if (data.hasOwnProperty("secret") && data.hasOwnProperty("peers") && data.hasOwnProperty("latestSequence")) {
132+ if (!fs.existsSync(configFolder)) {
133+ fs.mkdirSync(configFolder)
134+ }
135+
136+ fs.writeFileSync(manifestFile, JSON.stringify(manifest))
137+ fs.writeFileSync(peersFile, JSON.stringify(data.peers), "utf8")
138+ fs.writeFileSync(secretFile, data.secret, "utf8")
139+ state.latestSequence.set(data.latestSequence)
140+ state.currentSequence.set(0)
141+ isPresentingOptions.set(false)
142+
143+ data.importing = true
144+ data.currentSequence = 0
145+
146+ setImportData(data)
147+
148+ electron.ipcRenderer.send('import-identity')
149+ } else {
150+ console.log("> bad export file")
151+ console.log(data)
152+ alert("Bad Export File")
153+ }
154+ }
155+ }
156+ )
157+}
158+
66159 function windowControls() {
67160 if (process.platform === 'darwin') return
68161
69162 const window = remote.getCurrentWindow()
@@ -93,4 +186,58 @@
93186
94187 function assetPath(name) {
95188 return path.join(__dirname, '../assets', name)
96189 }
190+
191+
192+function getImportData() {
193+ var importFile = path.join(configFolder, 'importing.json')
194+ if (fs.existsSync(importFile)) {
195+ let data = JSON.parse(fs.readFileSync(importFile))
196+ return data || false
197+ } else {
198+ return false
199+ }
200+}
201+
202+function setImportData(data) {
203+ var importFile = path.join(configFolder, 'importing.json')
204+ fs.writeFileSync(importFile, JSON.stringify(data))
205+}
206+
207+function observeSequence() {
208+ const pull = require('pull-stream')
209+ const Client = require('ssb-client')
210+ const config = require('../config').create().config.sync.load()
211+ const _ = require('lodash')
212+
213+ Client(config.keys, config, (err, ssbServer) => {
214+ if (err) {
215+ console.error('problem starting client', err)
216+ } else {
217+ console.log('> sbot running!!!!')
218+ ssbServer.whoami((err, data) => {
219+ console.log("whoami", data.id)
220+
221+ var feedSource = ssbServer.createUserStream({
222+ live: true,
223+ id: data.id
224+ })
225+
226+ var valueLogger = pull.drain((msg) => {
227+ console.log("msg", msg)
228+
229+ let seq = _.get(msg, "value.sequence", false)
230+ if (seq) {
231+ state.currentSequence.set(seq)
232+ }
233+ })
234+
235+ pull(
236+ feedSource,
237+ valueLogger,
238+ )
239+
240+ })
241+ }
242+ })
243+}
ftu/index.jsView
@@ -14,9 +14,9 @@
1414 // need some modules first
1515 {
1616 styles: require('../styles'),
1717 settings: require('patch-settings'),
18- translations: require('../translations/sync')
18+ translations: require('../translations/sync'),
1919 },
2020 {
2121 app: require('./app')
2222 }
index.jsView
@@ -3,14 +3,15 @@
33 var electron = require('electron')
44 var Menu = electron.Menu
55 var Path = require('path')
66
7-// FTU needs
7+// First-Time User Experience (FTU) needs the items below
88 const fs = require('fs')
9-const Config = require('ssb-config/inject')
10-const appName = process.env.ssb_appname || 'ssb'
11-const config = Config(appName)
12-const isInstalled = fs.existsSync(Path.join(config.path, 'secret'))
9+const path = require('path')
10+const os = require('os')
11+const appName = process.env.SSB_APPNAME || 'ssb'
12+const configFolder = path.join(os.homedir(), `.${appName}`)
13+const isInstalled = fs.existsSync(Path.join(configFolder, 'secret'))
1314
1415 var windows = {}
1516 var quitting = false
1617
@@ -39,9 +40,8 @@
3940 }
4041
4142 Menu.setApplicationMenu(Menu.buildFromTemplate(menu))
4243
43- // TODO: FTU must happen before this part.
4444 if (!isInstalled) {
4545 console.log('Ticktack or SSB not installed, run FTU')
4646 openFTUWindow()
4747 } else {
@@ -50,15 +50,38 @@
5050
5151 // FTU told app to create new identity, so proceed as normal
5252 electron.ipcMain.once('create-new-identity', function (ev) {
5353 console.log('create new identity')
54+ setImportRunningFlag(false)
5455 startBackgroundProcess()
5556 })
5657
58+ // FTU told app to import some identity, need to start sbot and keep FTU running
59+ electron.ipcMain.once('import-identity', function (ev) {
60+ console.log('import identity')
61+ setImportRunningFlag(true)
62+ startBackgroundProcess()
63+ })
64+
65+ // FTU import finished, ready to start main window
66+ electron.ipcMain.once('import-completed', function (ev) {
67+ console.log('> import finished, opening main window')
68+ setImportRunningFlag(false)
69+ openMainWindow()
70+ })
71+
5772 // wait until server has started before opening main window
5873 electron.ipcMain.once('server-started', function (ev, config) {
59- console.log("> Opening main window")
60- openMainWindow()
74+ let keepFTURunning = getImportRunningFlag(false)
75+ if (!keepFTURunning) {
76+ console.log("> Opening main window")
77+ openMainWindow()
78+ } else {
79+ // sbot started but we're importing an older identity, need
80+ // to tell FTU to wait for sync.
81+ openFTUWindow()
82+ windows.ftu.webContents.send('import-started')
83+ }
6184 })
6285
6386 electron.app.on('before-quit', function () {
6487 quitting = true
@@ -193,4 +216,26 @@
193216
194217 window.loadURL('file://' + Path.join(__dirname, 'assets', 'base.html'))
195218 return window
196219 }
220+
221+function getImportRunningFlag(defaultValue) {
222+ var importFile = Path.join(configFolder, 'importing.json')
223+ if (fs.existsSync(importFile)) {
224+ let data = JSON.parse(fs.readFileSync(importFile))
225+ return data.importing || defaultValue
226+ } else {
227+ return defaultValue
228+ }
229+}
230+
231+function setImportRunningFlag(v) {
232+ let data = {}
233+ var importFile = Path.join(configFolder, 'importing.json')
234+ if (fs.existsSync(importFile)) {
235+ data = JSON.parse(fs.readFileSync(importFile))
236+ }
237+
238+ data.importing = v
239+
240+ fs.writeFileSync(importFile, JSON.stringify(data))
241+}
translations/en.jsView
@@ -189,9 +189,15 @@
189189 title: 'Export Identity'
190190 }
191191 },
192192 import: {
193- importAction: 'Import Identity'
193+ header: 'Import identity',
194+ importAction: 'Import Identity',
195+ synchronizeMessage: 'Synchronizing feed: ',
196+ dialog: {
197+ label: 'Import Identity',
198+ title: 'Import Identity'
199+ }
194200 }
195201 },
196202 languages: {
197203 en: 'English',

Built with git-ssb-web