git ssb

2+

mixmix / ticktack



Tree: e82f383b2cc56a63e4afa6eb79a83f8b874713f6

Files: e82f383b2cc56a63e4afa6eb79a83f8b874713f6 / ftu / app.js

7970 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 manageProgress = require('./manageProgress')
11const windowControls = require('../windowControls')
12const ftuCss = require('./styles')
13
14// const config = require('../config').create().config.sync.load()
15const config = {
16 path: path.join(os.homedir(), `.${process.env.SSB_APPNAME || process.env.ssb_appname || 'ssb'}`)
17}
18const SECRET_PATH = path.join(config.path, 'secret')
19const MANIFEST_PATH = path.join(config.path, 'manifest.json')
20const GOSSIP_PATH = path.join(config.path, 'gossip.json')
21const IMPORT_PATH = path.join(config.path, 'importing.json')
22
23// these initial values are overwritten by the identity file.
24var state = Struct({
25 isPresentingOptions: true,
26 creatingNewIdentity: false,
27 mySequence: Struct({
28 current: 0,
29 latest: 0,
30 latestConfirmed: false
31 }),
32 peerSequences: Struct({
33 current: 0,
34 latest: 0
35 }),
36 importComplete: false
37})
38
39state.peerSequences(console.log)
40
41watch(throttle(state.peersLatestSequence, 1000), console.log)
42
43// Note you can't want state and get updates to mySequence!
44watch(throttle(state, 500), s => {
45 const myFeedSynced = s.mySequence.current >= s.mySequence.latest && s.mySequence.latestConfirmed
46 const enoughFriends = s.peerSequences.current > 0.95 * s.peerSequences.latest
47
48 if (myFeedSynced && enoughFriends) state.importComplete.set(true)
49})
50
51exports.gives = nest('ftu.app')
52
53exports.needs = nest({
54 'styles.css': 'reduce',
55 'translations.sync.strings': 'first'
56})
57
58exports.create = (api) => {
59 return nest({
60 'ftu.app': function app () {
61 const strings = api.translations.sync.strings()
62
63 const css = [...values(api.styles.css()), ftuCss].join('\n')
64 document.head.appendChild(h('style', { innerHTML: css }))
65
66 // This watcher is responsible for switching from FTU to Ticktack main app
67 watch(state.importComplete, importComplete => {
68 if (importComplete) electron.ipcRenderer.send('import-completed')
69 })
70
71 if (fs.existsSync(SECRET_PATH)) {
72 // somehow the FTU started but the identity is already in place.
73 // treat it as a failed import and start importing...
74 console.log('resuming import')
75 let previousData = getImportData()
76 if (previousData === false) {
77 // there is a secret but there is no previous import data.
78 // so, we proceed as normal because we can't do anything else,
79 // it looks like a normal standard installation...
80 setImportData({ importing: false })
81 electron.ipcRenderer.send('import-completed')
82 } else {
83 state.mySequence.latest.set(previousData.mySequence.latest)
84 // state.peerSequences.latest.set(previousData.peerSequences.latest) // nor made in exportIdentity yet
85 state.isPresentingOptions.set(false)
86 manageProgress({ state, config })
87 }
88 }
89
90 var app = h('App', [
91 h('Header', [
92 h('img.logoName', { src: assetPath('logo_and_name.png') }),
93 windowControls()
94 ]),
95 when(state.isPresentingOptions, InitialOptions(), ImportProgress())
96 ])
97
98 return app
99
100 function InitialOptions () {
101 const { welcomeHeader, welcomeMessage, busyMessage, importAction, createAction } = strings.backup.ftu
102
103 return h('Page', [
104 h('div.content', [
105 h('section.welcome', [
106 h('h1', welcomeHeader),
107 h('div', welcomeMessage)
108 ]),
109 when(state.creatingNewIdentity,
110 h('p', busyMessage),
111 h('section.actionButtons', [
112 h('div.left', h('Button', { 'ev-click': () => actionImportIdentity(strings) }, importAction)),
113 h('div.right', h('Button -strong', { 'ev-click': () => actionCreateNewOne() }, createAction))
114 ])
115 )
116 ])
117 ])
118 }
119
120 function ImportProgress () {
121 const { header, myFeedProgress, myFriendsProgress, details } = strings.backup.import
122
123 return h('Page', [
124 h('div.content', [
125 h('h1', header),
126 h('h2', myFeedProgress),
127 h('pre', computed(state.mySequence, s => {
128 return progress({
129 width: 42,
130 total: s.latest,
131 complete: '/',
132 incomplete: '-',
133 style: function (complete, incomplete) {
134 // add an arrow at the head of the completed part
135 return `[${complete}${incomplete}] (${s.current}/ ${s.latest})`
136 }
137 })(s.current)
138 })),
139 h('p', details),
140 h('h2', myFriendsProgress),
141 h('pre', computed(state.peerSequences, s => {
142 return progress({
143 width: 42,
144 total: s.latest,
145 complete: '\\',
146 incomplete: '-',
147 style: function (complete, incomplete) {
148 // add an arrow at the head of the completed part
149 return `[${complete}${incomplete}] (${s.current}/ ${Math.max(s.latest, s.current)})`
150 }
151 })(s.current)
152 }))
153 ])
154 ])
155 }
156 }
157 })
158}
159
160electron.ipcRenderer.on('import-resumed', function (ev, c) {
161 console.log('background process is running, begin observing')
162
163 manageProgress({ state, config })
164})
165
166function actionCreateNewOne () {
167 state.creatingNewIdentity.set(true)
168 /// //////////!!!!!!
169 // WARNING TODO: this needs replacing with manifest exported from actual sbot running!
170 const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, '../manifest.json')))
171 /// //////////!!!!!!
172 if (!fs.existsSync(config.path)) {
173 fs.mkdirSync(config.path)
174 }
175 fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest))
176
177 electron.ipcRenderer.send('create-new-identity')
178}
179
180function actionImportIdentity (strings) {
181 /// /////////!!!!!!
182 // WARNING TODO (same as above warning)
183 const manifest = JSON.parse(fs.readFileSync(path.join(__dirname, '../manifest.json')))
184
185 // place the other files first
186 electron.remote.dialog.showOpenDialog(
187 {
188 title: strings.backup.import.dialog.title,
189 butttonLabel: strings.backup.import.dialog.label,
190 defaultPath: 'ticktack-identity.backup',
191 properties: ['openFile']
192 },
193 (filenames) => {
194 if (typeof filenames !== 'undefined') {
195 let filename = filenames[0]
196 let data = JSON.parse(fs.readFileSync(filename))
197 const requiredProps = ['secret', 'gossip', 'mySequence', 'peersLatestSequence']
198
199 if (requiredProps.every(prop => data.hasOwnProperty(prop))) {
200 if (!fs.existsSync(config.path)) {
201 fs.mkdirSync(config.path)
202 }
203
204 fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest))
205 fs.writeFileSync(GOSSIP_PATH, JSON.stringify(data.gossip), 'utf8')
206 fs.writeFileSync(SECRET_PATH, data.secret, 'utf8')
207
208 state.mySequence.latest.set(data.mySequence.latest)
209 state.isPresentingOptions.set(false)
210
211 data.importing = true
212
213 setImportData(data)
214
215 electron.ipcRenderer.send('import-identity')
216 } else {
217 console.log('> bad export file')
218 console.log(data)
219 alert('Bad Export File')
220 }
221 }
222 }
223 )
224}
225
226function assetPath (name) {
227 return path.join(__dirname, '../assets', name)
228}
229
230function getImportData () {
231 if (fs.existsSync(IMPORT_PATH)) {
232 let data = JSON.parse(fs.readFileSync(IMPORT_PATH))
233 return data || false
234 } else {
235 return false
236 }
237}
238
239function setImportData (data) {
240 fs.writeFileSync(IMPORT_PATH, JSON.stringify(data))
241}
242

Built with git-ssb-web