git ssb

2+

mixmix / ticktack



Tree: 3d4eca03f309655578710ab4453f309c514268d1

Files: 3d4eca03f309655578710ab4453f309c514268d1 / ftu / app.js

6760 bytesRaw
1const { h, Value, when, computed, Struct, watch, throttle } = require('mutant')
2const nest = require('depnest')
3const path = require('path')
4const fs = require('fs')
5const electron = require('electron')
6const os = require('os')
7const progress = require('progress-string')
8const values = require('lodash/values')
9
10const observeSequence = require('./observeSequence')
11const windowControls = require('../windowControls')
12const ftuCss = require('./styles')
13
14const appName = process.env.SSB_APPNAME || 'ssb'
15const CONFIG_FOLDER = path.join(os.homedir(), `.${appName}`)
16const IMPORT_FILE = path.join(CONFIG_FOLDER, 'importing.json')
17
18var isBusy = Value(false)
19var isPresentingOptions = Value(true)
20var checkerTimeout
21
22// these initial values are overwritten by the identity file.
23var state = Struct({
24 latestSequence: 0,
25 confirmedRemotely: false,
26 currentSequence: -1
27})
28
29exports.gives = nest('ftu.app')
30
31exports.needs = nest({
32 'styles.css': 'reduce',
33 'translations.sync.strings': 'first'
34})
35
36exports.create = (api) => {
37 return nest({
38 'ftu.app': function app () {
39 const strings = api.translations.sync.strings()
40
41 const css = [...values(api.styles.css()), ftuCss].join('\n')
42 document.head.appendChild(h('style', { innerHTML: css }))
43
44 // This watcher is responsible for switching from FTU to Ticktack main app
45 watch(throttle(state, 500), s => {
46 if (s.currentSequence >= s.latestSequence && s.confirmedRemotely) {
47 console.log('all imported')
48 clearTimeout(checkerTimeout)
49 electron.ipcRenderer.send('import-completed')
50 }
51 })
52
53 if (fs.existsSync(path.join(CONFIG_FOLDER, 'secret'))) {
54 // somehow the FTU started but the identity is already in place.
55 // treat it as a failed import and start importing...
56 console.log('resuming import')
57 let previousData = getImportData()
58 if (previousData === false) {
59 // there is a secret but there is no previous import data.
60 // so, we proceed as normal because we can't do anything else,
61 // it looks like a normal standard installation...
62 setImportData({ importing: false })
63 electron.ipcRenderer.send('import-completed')
64 } else {
65 state.latestSequence.set(previousData.latestSequence)
66 state.currentSequence.set(previousData.currentSequence)
67 isPresentingOptions.set(false)
68 observeSequence({ state, timeout: checkerTimeout })
69 }
70 }
71
72 var app = h('App', [
73 h('Header', [
74 h('img.logoName', { src: assetPath('logo_and_name.png') }),
75 windowControls()
76 ]),
77 when(isPresentingOptions, InitialOptions(), ImportProgress())
78 ])
79
80 return app
81
82 function InitialOptions () {
83 const { welcomeHeader, welcomeMessage, busyMessage, importAction, createAction } = strings.backup.ftu
84
85 return h('Page', [
86 h('div.content', [
87 h('section.welcome', [
88 h('h1', welcomeHeader),
89 h('div', welcomeMessage)
90 ]),
91 when(isBusy,
92 h('p', busyMessage),
93 h('section.actionButtons', [
94 h('div.left', h('Button', { 'ev-click': () => actionImportIdentity(strings) }, importAction)),
95 h('div.right', h('Button -strong', { 'ev-click': () => actionCreateNewOne() }, createAction))
96 ])
97 )
98 ])
99 ])
100 }
101
102 function ImportProgress () {
103 const { header, synchronizeMessage, details } = strings.backup.import
104
105 return h('Page', [
106 h('div.content', [
107 h('h1', header),
108 h('p', synchronizeMessage),
109 h('pre', computed(state, s => {
110 return progress({
111 width: 42,
112 total: s.latestSequence,
113 style: function (complete, incomplete) {
114 // add an arrow at the head of the completed part
115 return `${complete}>${incomplete} (${s.currentSequence}/ ${s.latestSequence})`
116 }
117 })(s.currentSequence)
118 })),
119 h('p', details)
120 ])
121 ])
122 }
123 }
124 })
125}
126
127electron.ipcRenderer.on('import-resumed', function (ev, c) {
128 console.log('background process is running, begin observing')
129
130 observeSequence({ state, timeout: checkerTimeout })
131})
132
133function actionCreateNewOne () {
134 isBusy.set(true)
135 const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, '../manifest.json')))
136 const manifestFile = path.join(CONFIG_FOLDER, 'manifest.json')
137 if (!fs.existsSync(CONFIG_FOLDER)) {
138 fs.mkdirSync(CONFIG_FOLDER)
139 }
140 fs.writeFileSync(manifestFile, JSON.stringify(manifest))
141
142 electron.ipcRenderer.send('create-new-identity')
143}
144
145function actionImportIdentity (strings) {
146 const peersFile = path.join(CONFIG_FOLDER, 'gossip.json')
147 const secretFile = path.join(CONFIG_FOLDER, 'secret')
148 const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, '../manifest.json')))
149 const manifestFile = path.join(CONFIG_FOLDER, 'manifest.json')
150
151 // place the other files first
152 electron.remote.dialog.showOpenDialog(
153 {
154 title: strings.backup.import.dialog.title,
155 butttonLabel: strings.backup.import.dialog.label,
156 defaultPath: 'ticktack-identity.backup',
157 properties: ['openFile']
158 },
159 (filenames) => {
160 if (typeof filenames !== 'undefined') {
161 let filename = filenames[0]
162 let data = JSON.parse(fs.readFileSync(filename))
163 if (data.hasOwnProperty('secret') && data.hasOwnProperty('peers') && data.hasOwnProperty('latestSequence')) {
164 if (!fs.existsSync(CONFIG_FOLDER)) {
165 fs.mkdirSync(CONFIG_FOLDER)
166 }
167
168 fs.writeFileSync(manifestFile, JSON.stringify(manifest))
169 fs.writeFileSync(peersFile, JSON.stringify(data.peers), 'utf8')
170 fs.writeFileSync(secretFile, data.secret, 'utf8')
171 state.latestSequence.set(data.latestSequence)
172 state.currentSequence.set(0)
173 isPresentingOptions.set(false)
174
175 data.importing = true
176 data.currentSequence = 0
177
178 setImportData(data)
179
180 electron.ipcRenderer.send('import-identity')
181 } else {
182 console.log('> bad export file')
183 console.log(data)
184 alert('Bad Export File')
185 }
186 }
187 }
188 )
189}
190
191function assetPath (name) {
192 return path.join(__dirname, '../assets', name)
193}
194
195function getImportData () {
196 if (fs.existsSync(IMPORT_FILE)) {
197 let data = JSON.parse(fs.readFileSync(IMPORT_FILE))
198 return data || false
199 } else {
200 return false
201 }
202}
203
204function setImportData (data) {
205 fs.writeFileSync(IMPORT_FILE, JSON.stringify(data))
206}
207

Built with git-ssb-web