/
appinfo.go
290 lines (251 loc) · 9.34 KB
/
appinfo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
package load
import (
"fmt"
"runtime"
"sync"
"time"
giouiApp "gioui.org/app"
"gioui.org/layout"
"gioui.org/unit"
"gioui.org/widget/material"
"github.com/crypto-power/cryptopower/app"
"github.com/crypto-power/cryptopower/libwallet"
"github.com/crypto-power/cryptopower/libwallet/utils"
"github.com/crypto-power/cryptopower/ui/assets"
"github.com/crypto-power/cryptopower/ui/values"
)
type AssetsManagerInitFn func(utils.NetworkType) (*libwallet.AssetsManager, error)
// TODO: This should ultimately replace Load, acting as the container for all
// properties and methods that every app page and window relies on. Should
// probably rename as well.
type AppInfo struct {
version string
buildDate time.Time
startUpTime time.Time
cfg *AppConfig
allowNetTypeSwitching bool
initAssetsManager AssetsManagerInitFn
AssetsManager *libwallet.AssetsManager
windowMtx sync.Mutex
window *giouiApp.Window
startPage app.Page
currentAppWidth unit.Dp
isMobileView bool
}
// StartApp returns an instance of AppInfo with the startUpTime set to the
// current time. If netType is empty, the netType to use will be read from the
// appCfg. If a netType is provided, it'll be used instead of the netType in the
// appCfg; and in-app network type switching will be disabled.
func StartApp(version string, buildDate time.Time, netType string, appCfg *AppConfig, initAssetsManager AssetsManagerInitFn) (*AppInfo, error) {
var allowNetTypeSwitching bool
if netType == "" {
netType = appCfg.Values().NetType
allowNetTypeSwitching = true
}
net := utils.ToNetworkType(netType)
if net == utils.Unknown {
return nil, fmt.Errorf("invalid netType: %s", netType)
}
assetsManager, err := initAssetsManager(net)
if err != nil {
return nil, err
}
return &AppInfo{
version: version,
buildDate: buildDate,
startUpTime: time.Now(),
cfg: appCfg,
allowNetTypeSwitching: allowNetTypeSwitching,
initAssetsManager: initAssetsManager,
AssetsManager: assetsManager,
}, nil
}
// BuildDate returns the app's build date.
func (app *AppInfo) BuildDate() time.Time {
return app.buildDate
}
// Version returns the app's version.
func (app *AppInfo) Version() string {
return app.version
}
// StartupTime returns the app's startup time.
func (app *AppInfo) StartupTime() time.Time {
return app.startUpTime
}
// ReadyForDisplay marks the app as display-ready by storing the window and
// startPage used by the app.
//
// TODO: Is it possible to create Load here and bring the actual display
// functionality over from the ui package?
func (app *AppInfo) ReadyForDisplay(window *giouiApp.Window, startPage app.Page) {
app.windowMtx.Lock()
defer app.windowMtx.Unlock()
if app.window != nil || app.startPage != nil {
panic("duplicate call to AppInfo.ReadyForDisplay()")
}
app.window = window
app.startPage = startPage
}
// Window returns the gio app window that hosts this app and is used to display
// different pages and modals.
//
// TODO: Is it possible for Window to be a custom type that has navigation
// methods?
func (app *AppInfo) Window() *giouiApp.Window {
app.windowMtx.Lock()
defer app.windowMtx.Unlock()
return app.window
}
// StartPage returns the first page that is displayed when the app is launched.
// This page would be re-displayed if the app is restarted, e.g. when the
// network type is changed.
func (app *AppInfo) StartPage() app.Page {
app.windowMtx.Lock()
defer app.windowMtx.Unlock()
return app.startPage
}
// CanChangeNetworkType is true if it is possible to change the network type
// used by the app.
func (app *AppInfo) CanChangeNetworkType() bool {
return app.allowNetTypeSwitching
}
// ChangeAssetsManagerNetwork changes the network type used by the app to the
// value provided. A new AssetsManager for the specified network type is
// initialized and returned, but not used. Call ChangeAssetsManager to use the
// new AssetsManager.
func (app *AppInfo) ChangeAssetsManagerNetwork(netType utils.NetworkType) (*libwallet.AssetsManager, error) {
if !app.allowNetTypeSwitching {
return nil, fmt.Errorf("this operation is not permitted")
}
currentNetType := app.AssetsManager.NetType()
if netType == currentNetType {
return nil, fmt.Errorf("new network type is the same as current network type")
}
// Initialize a new AssetsManager for the new netType. This will be used to
// replace the current AssetsManager after the current one is shutdown.
newAssetsManager, err := app.initAssetsManager(netType)
if err != nil {
return nil, fmt.Errorf("failed to init new AssetsManager for %s network: %v", netType, err)
}
// Save the new netType to appCfg and only proceed with the network switch
// if updating the cfg is successful.
err = app.cfg.Update(func(appCfgValues *AppConfigValues) {
appCfgValues.NetType = string(netType)
})
if err != nil {
newAssetsManager.Shutdown()
return nil, fmt.Errorf("update app config error: %v", err)
}
return newAssetsManager, nil
}
// ChangeAssetsManager closes all open pages, shuts down the current
// AssetsManager, switches to the provided AssetsManager and then restarts the
// app by displaying the app's first page.
//
// TODO: If *AppInfo.Window is changed to a custom type that implements
// app.WindowNavigator, this method won't need to take an app.PageNavigator
// parameter. See the TODO comment on *AppInfo.ReadyForDisplay() and
// *AppInfo.Window().
func (app *AppInfo) ChangeAssetsManager(newAssetsManager *libwallet.AssetsManager, pageNav app.PageNavigator) {
if !app.allowNetTypeSwitching {
// Should never happen, because *AppInfo.ChangeAssetsManagerNetwork()
// would not even produce a newAssetsManager if network type change is
// disabled.
panic("network type change is not permitted")
}
appStartPage := app.StartPage()
if appStartPage == nil {
// Something is wrong. Close the app and let the user restart the app.
// The new netType will be used.
panic("cannot complete network type change without a pre-set app StartPage")
}
// Note when the network type / assets manager switch began. We want to
// ensure that the temporary "restarting app" page is visible to the user
// for some seconds at least.
start := time.Now()
// Close all pages that are currently open and display a temporary
// "restarting app" page.
currentNetType, newNetType := app.AssetsManager.NetType(), newAssetsManager.NetType()
pageNav.ClearStackAndDisplay(networkSwitchTempPage(currentNetType, newNetType))
// Display the newNetType on the app title if its not on mainnet.
appTitle := giouiApp.Title(values.String(values.StrAppName))
if newNetType != utils.Mainnet {
appTitle = giouiApp.Title(values.StringF(values.StrAppTitle, newNetType.Display()))
}
app.Window().Option(appTitle)
// Shutdown the current AssetsManager and begin using the new one.
app.AssetsManager.Shutdown()
app.AssetsManager = newAssetsManager
// If the network type / assets manager switch was swift, wait a bit so the
// user clearly sees the temporary "restarting app" page before displaying
// the app's start page.
minWait := 3 * time.Second
if timeTaken := time.Since(start); timeTaken < minWait {
<-time.After(minWait - timeTaken)
}
pageNav.ClearStackAndDisplay(appStartPage)
}
// SetCurrentAppWidth sets the specified value as the app's current width, using
// the provided device-dependent metric unit conversion.
//
// TODO: If actual display functionality is brought here over from the ui
// package, setting the app window's current width will be more seamless and
// this method can be unexported. See the TODO comment on
// *AppInfo.ReadyForDisplay().
func (app *AppInfo) SetCurrentAppWidth(appWidth int, metric unit.Metric) {
app.currentAppWidth = metric.PxToDp(appWidth)
app.isMobileView = app.currentAppWidth <= values.StartMobileView
}
// CurrentAppWidth returns the current width of the app's window.
func (app *AppInfo) CurrentAppWidth() unit.Dp {
return app.currentAppWidth
}
// IsMobileView returns true if the app's window width is less than the mobile
// view width.
func (app *AppInfo) IsMobileView() bool {
return app.isMobileView
}
func (app *AppInfo) IsIOS() bool {
return runtime.GOOS == "ios"
}
// ConvertTextSize returns the appropriate text size for desktop and mobile,
// that corresponds to the provided value.
func (app *AppInfo) ConvertTextSize(size unit.Sp) unit.Sp {
if !app.isMobileView {
return size
}
switch size {
case values.TextSize60:
return values.TextSize36
case values.TextSize20, values.TextSize24:
return values.TextSize18
case values.TextSize18:
return values.TextSize16
case values.TextSize16:
return values.TextSize14
default:
return size
}
}
// ConvertIconSize returns the appropriate icon size for desktop and mobile,
// that corresponds to the provided value.
func (app *AppInfo) ConvertIconSize(size unit.Dp) unit.Dp {
if !app.isMobileView {
return size
}
switch size {
case values.MarginPadding24:
return values.MarginPadding16
default:
return size
}
}
func networkSwitchTempPage(currentNetType, newNetType utils.NetworkType) app.Page {
theme := material.NewTheme(assets.FontCollection())
text := fmt.Sprintf("Switching from %s to %s, please wait...", currentNetType, newNetType)
lbl := material.Body1(theme, text)
return app.NewWidgetDisplayPage(func(gtx layout.Context) layout.Dimensions {
gtx.Constraints.Min = gtx.Constraints.Max
return layout.Center.Layout(gtx, lbl.Layout)
})
}