diff --git a/.vscode/settings.json b/.vscode/settings.json index c0a42c9f..f04ae7fa 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,5 +18,8 @@ "typescript.preferences.importModuleSpecifier": "relative", "typescript.suggest.autoImports": true, "javascript.suggest.autoImports": true, - "editor.acceptSuggestionOnEnter": "on" + "editor.acceptSuggestionOnEnter": "on", + "files.associations": { + "*.tbx": "json" + } } diff --git a/app/app.go b/app/app.go index 129b118e..928fc475 100644 --- a/app/app.go +++ b/app/app.go @@ -3,33 +3,29 @@ package app import ( "context" "encoding/json" - "os" - "path/filepath" + "errors" + "fmt" "sync" "github.com/TrueBlocks/trueblocks-browse/pkg/daemons" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" - coreConfig "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/names" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/output" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" - "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils" sdk "github.com/TrueBlocks/trueblocks-sdk/v3" "github.com/wailsapp/wails/v2/pkg/runtime" ) type App struct { - sdk.Globals `json:",inline"` + sdk.Globals ctx context.Context meta coreTypes.MetaData dirty bool - renderCtxs map[base.Address][]*output.RenderCtx - // Containers projects types.ProjectContainer monitors types.MonitorContainer @@ -43,101 +39,112 @@ type App struct { config types.ConfigContainer // Memory caches - HistoryCache *types.HistoryMap `json:"historyCache"` - EnsCache *sync.Map `json:"ensCache"` - BalanceCache *sync.Map `json:"balanceCache"` + ensCache *sync.Map + balanceCache *sync.Map + namesMap map[base.Address]coreTypes.Name + historyCache *types.HistoryMap + renderCtxs map[base.Address][]*output.RenderCtx // Controllers - ScraperController *daemons.DaemonScraper - FreshenController *daemons.DaemonFreshen - IpfsController *daemons.DaemonIpfs + scraperController *daemons.DaemonScraper + freshenController *daemons.DaemonFreshen + ipfsController *daemons.DaemonIpfs + + // During initialization, we do things that may cause errors, but + // we have not yet opened the window, so we defer them until we can + // decide what to do. + deferredErrors []error +} + +func (a *App) String() string { + bytes, _ := json.Marshal(a) + return string(bytes) } func NewApp() *App { - a := App{ - renderCtxs: make(map[base.Address][]*output.RenderCtx), + a := &App{ + ensCache: &sync.Map{}, + balanceCache: &sync.Map{}, + namesMap: make(map[base.Address]coreTypes.Name), + historyCache: &types.HistoryMap{}, + renderCtxs: make(map[base.Address][]*output.RenderCtx), } - a.EnsCache = &sync.Map{} - a.BalanceCache = &sync.Map{} - a.HistoryCache = &types.HistoryMap{} - a.names.NamesMap = make(map[base.Address]coreTypes.Name) - a.projects = types.NewProjectContainer("", []types.HistoryContainer{}) + a.freshenController = daemons.NewFreshen(a, "freshen", 3000, a.IsShowing("freshen")) + a.scraperController = daemons.NewScraper(a, "scraper", 7000, a.IsShowing("scraper")) + a.ipfsController = daemons.NewIpfs(a, "ipfs", 10000, a.IsShowing("ipfs")) a.session.LastSub = make(map[string]string) - return &a + return a } -func (a *App) String() string { - bytes, _ := json.MarshalIndent(a, "", " ") - return string(bytes) -} +var ErrLoadingNames = errors.New("error loading names") +var ErrWindowSize = errors.New("error fixing window size") func (a *App) Startup(ctx context.Context) { a.ctx = ctx - a.loadSession() - a.Chain = a.session.LastChain - a.FreshenController = daemons.NewFreshen(a, "freshen", 3000, a.IsShowing("freshen")) - a.ScraperController = daemons.NewScraper(a, "scraper", 7000, a.IsShowing("scraper")) - a.IpfsController = daemons.NewIpfs(a, "ipfs", 10000, a.IsShowing("ipfs")) - go a.startDaemons() + // We do various setups prior to showing the window. Improves interactivity. + // But...something may fail, so we need to keep track of errors. + var err error + if err = a.session.Load(); err != nil { + a.deferredErrors = append(a.deferredErrors, err) + } - fn := filepath.Join(a.session.LastFolder, a.session.LastFile) - if file.FileExists(fn) { - a.LoadFile(fn) - a.dirty = false - } else { - a.dirty = true + // Load the trueBlocks.toml file + if err = a.config.Load(); err != nil { + a.deferredErrors = append(a.deferredErrors, err) + } + if a.session.LastChain, err = a.config.IsValidChain(a.session.LastChain); err != nil { + a.deferredErrors = append(a.deferredErrors, err) } + a.Chain = a.session.LastChain - logger.Info("Starting freshen process...") - _ = a.Refresh() + // We always need names, so let's load it before showing the window + if a.namesMap, err = names.LoadNamesMap(a.getChain(), coreTypes.All, nil); err == nil { + wErr := fmt.Errorf("%w: %v", ErrLoadingNames, err) + a.deferredErrors = append(a.deferredErrors, wErr) + } } +// DomReady is called by Wails when the app is ready to go. Adjust the window size and show it. func (a *App) DomReady(ctx context.Context) { - win := a.GetWindow() - runtime.WindowSetPosition(a.ctx, win.X, win.Y) - runtime.WindowSetSize(a.ctx, win.Width, win.Height) - runtime.WindowShow(a.ctx) + var err error - if path, err := utils.GetConfigFn("", "trueBlocks.toml"); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) - } else { - if err := coreConfig.ReadToml(path, &a.config.Config); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) - } + // We're ready to open the window, but first we need to make sure it will show... + if a.session.Window, err = a.session.CleanWindowSize(a.ctx); err != nil { + wErr := fmt.Errorf("%w: %v", ErrWindowSize, err) + a.deferredErrors = append(a.deferredErrors, wErr) + } + // DO NOT COLLAPSE - A VALID WINDOW IS RETURNED EVEN ON ERROR + runtime.WindowSetPosition(a.ctx, a.session.Window.X, a.session.Window.Y) + runtime.WindowSetSize(a.ctx, a.session.Window.Width, a.session.Window.Height) + runtime.WindowShow(a.ctx) + if err != nil { + a.deferredErrors = append(a.deferredErrors, err) } -} -func (a *App) Shutdown(ctx context.Context) { - a.saveSession() -} + // We now have a window, so we can finally show any accumulated errors + for _, err := range a.deferredErrors { + a.emitErrorMsg(err, nil) + } -func (a *App) saveSession() { - if !isTesting { - a.session.Window.X, a.session.Window.Y = runtime.WindowGetPosition(a.ctx) - a.session.Window.Width, a.session.Window.Height = runtime.WindowGetSize(a.ctx) - a.session.Window.Y += 38 // TODO: This is a hack to account for the menu bar - not sure why it's needed + fn := a.getFullPath() + if file.FileExists(fn) { + a.readFile(fn) + } else { + a.newFile() } - _ = a.session.Save() -} -func (a *App) loadSession() { - _ = a.session.Load() - a.session.CleanWindowSize(a.ctx) - a.Chain = a.session.LastChain -} + go a.Refresh() -func (a *App) Logger(msg string) { - logger.Info(msg) -} + go a.freshenController.Run() + go a.scraperController.Run() + go a.ipfsController.Run() -var isTesting bool + logger.Info("Fininished loading...") +} -func init() { - isTesting = os.Getenv("TB_TEST_MODE") == "true" +// Shutdown is called by Wails when the app is closed +func (a *App) Shutdown(ctx context.Context) { + a.saveSession() } diff --git a/app/app_debug.go b/app/app_debug.go deleted file mode 100644 index b9e32314..00000000 --- a/app/app_debug.go +++ /dev/null @@ -1,7 +0,0 @@ -package app - -import "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" - -func debugMsg(v ...any) { - logger.Info(v...) -} diff --git a/app/app_getters.go b/app/app_getters.go index 480b1a66..daf39946 100644 --- a/app/app_getters.go +++ b/app/app_getters.go @@ -34,16 +34,12 @@ func (a *App) GetEnv(key string) string { return os.Getenv(key) } -func (a *App) GetMeta() coreTypes.MetaData { - return a.meta -} - func (a *App) GetAppTitle() string { return a.session.Window.Title } func (a *App) GetRoute() string { - if !a.IsConfigured() { + if !a.isConfigured() { return "/wizard" } @@ -55,12 +51,12 @@ func (a *App) GetRoute() string { return route } -func (a *App) GetAddress() base.Address { +func (a *App) GetSelected() base.Address { addr := a.session.LastSub["/history"] return base.HexToAddress(addr) } -func (a *App) GetChain() string { +func (a *App) getChain() string { return a.Chain } diff --git a/app/app_info.go b/app/app_info.go new file mode 100644 index 00000000..6970d6e4 --- /dev/null +++ b/app/app_info.go @@ -0,0 +1,39 @@ +package app + +import ( + "path/filepath" + + coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" +) + +type AppInfo struct { + Chain string `json:"chain"` + Filename string `json:"filename"` + Dirty bool `json:"dirty"` + Meta coreTypes.MetaData `json:"meta"` + State coreTypes.WizState `json:"state"` + IsConfigured bool `json:"isConfigured"` +} + +func (a *App) getFolder() string { + return a.session.LastFolder +} + +func (a *App) getFilename() string { + return a.session.LastFile +} + +func (a *App) getFullPath() string { + return filepath.Join(a.getFolder(), a.getFilename()) +} + +func (a *App) GetAppInfo() AppInfo { + return AppInfo{ + Chain: a.Chain, + Filename: a.getFullPath(), + Dirty: a.dirty, + Meta: a.meta, + State: a.getWizardState(), + IsConfigured: a.isConfigured(), + } +} diff --git a/app/app_setters.go b/app/app_setters.go index 54b3237b..032d0589 100644 --- a/app/app_setters.go +++ b/app/app_setters.go @@ -3,7 +3,6 @@ package app import ( "os" - "github.com/TrueBlocks/trueblocks-browse/pkg/types" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" ) @@ -22,11 +21,12 @@ func (a *App) SetRoute(route, subRoute string) { } func (a *App) SetChain(chain string, address base.Address) { - a.CancelAllContexts() // cancel what's happening on the old chain + a.emitInfoMsg("Switching to chain", chain) + a.CancelAllContexts() a.Chain = chain a.session.LastChain = chain a.saveSession() - a.Reload(address) + a.GoToAddress(address) a.monitors = types.MonitorContainer{} a.names = types.NameContainer{} a.abis = types.AbiContainer{} diff --git a/app/data_abi.go b/app/data_abi.go index 9dcff6ba..31d9fc07 100644 --- a/app/data_abi.go +++ b/app/data_abi.go @@ -9,7 +9,6 @@ import ( "sync/atomic" "time" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" @@ -76,12 +75,10 @@ func (a *App) loadAbis(wg *sync.WaitGroup, errorChan chan error) error { // EXISTING_CODE // EXISTING_CODE if err := sdk.SortAbis(a.abis.Items, a.abis.Sorts); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) } a.abis.Summarize() - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: "Loaded abis"}) + a.emitInfoMsg("Loaded abis", "") } return nil @@ -95,10 +92,7 @@ func (a *App) ModifyAbi(modData *ModifyData) error { opts.Globals.Decache = true if _, _, err := opts.Abis(); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - Address: modData.Address, - }) + a.emitAddressErrorMsg(err, modData.Address) return err } else { newAbis := make([]coreTypes.Abi, 0, len(a.abis.Items)) @@ -113,8 +107,7 @@ func (a *App) ModifyAbi(modData *ModifyData) error { } a.abis.LastUpdate = time.Time{} a.abis.Items = newAbis - msg := fmt.Sprintf("ModifyAbi delete: %s", modData.Address.Hex()) - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: msg}) + a.emitInfoMsg("ModifyAbi delete", modData.Address.Hex()) return nil } } diff --git a/app/data_history.go b/app/data_history.go index cc0ee615..7b5e8176 100644 --- a/app/data_history.go +++ b/app/data_history.go @@ -6,6 +6,7 @@ import ( "fmt" "sort" "sync" + "time" "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" @@ -19,20 +20,18 @@ func (a *App) HistoryPage(addr string, first, pageSize int) *types.HistoryContai address, ok := a.ConvertToAddress(addr) if !ok { err := fmt.Errorf("Invalid address: " + addr) - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) return &types.HistoryContainer{} } - _, exists := a.HistoryCache.Load(address) + _, exists := a.historyCache.Load(address) if !exists { return &types.HistoryContainer{} } first = base.Max(0, base.Min(first, a.txCount(address)-1)) last := base.Min(a.txCount(address), first+pageSize) - history, _ := a.HistoryCache.Load(address) + history, _ := a.historyCache.Load(address) history.Summarize() copy := history.ShallowCopy().(*types.HistoryContainer) copy.Balance = a.getBalance(address) @@ -46,10 +45,7 @@ func (a *App) getHistoryCnt(address base.Address) uint64 { Globals: a.toGlobals(), } if appearances, meta, err := opts.ListCount(); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - Address: address, - }) + a.emitAddressErrorMsg(err, address) return 0 } else if len(appearances) == 0 { return 0 @@ -60,13 +56,13 @@ func (a *App) getHistoryCnt(address base.Address) uint64 { } func (a *App) isFileOpen(address base.Address) bool { - _, isOpen := a.HistoryCache.Load(address) + _, isOpen := a.historyCache.Load(address) return isOpen } func (a *App) txCount(address base.Address) int { if a.isFileOpen(address) { - history, _ := a.HistoryCache.Load(address) + history, _ := a.historyCache.Load(address) return len(history.Items) } else { return 0 @@ -89,7 +85,7 @@ func (a *App) loadHistory(address base.Address, wg *sync.WaitGroup, errorChan ch // } // defer historyLock.CompareAndSwap(1, 0) - history, exists := a.HistoryCache.Load(address) + history, exists := a.historyCache.Load(address) if exists { if !history.NeedsUpdate(a.forceHistory()) { return nil @@ -99,10 +95,7 @@ func (a *App) loadHistory(address base.Address, wg *sync.WaitGroup, errorChan ch _ = errorChan // delint logger.Info("Loading history for address: ", address.Hex()) if err := a.thing(address, 15); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - Address: address, - }) + a.emitAddressErrorMsg(err, address) return err } a.loadProjects(nil, nil) @@ -133,10 +126,10 @@ func (a *App) thing(address base.Address, freq int) error { if !ok { continue } - summary, _ := a.HistoryCache.Load(address) + summary, _ := a.historyCache.Load(address) summary.NTotal = nItems summary.Address = address - summary.Name = a.names.NamesMap[address].Name + summary.Name = a.namesMap[address].Name summary.Items = append(summary.Items, *tx) if len(summary.Items)%(freq*3) == 0 { sort.Slice(summary.Items, func(i, j int) bool { @@ -145,24 +138,17 @@ func (a *App) thing(address base.Address, freq int) error { } return summary.Items[i].BlockNumber > summary.Items[j].BlockNumber }) - messages.EmitMessage(a.ctx, messages.Progress, &messages.MessageMsg{ - Address: address, - Num1: len(summary.Items), - Num2: int(nItems), - }) + a.emitProgressMsg(messages.Progress, address, len(summary.Items), int(nItems)) } if len(summary.Items) == 0 { - a.HistoryCache.Delete(address) + a.historyCache.Delete(address) } else { - a.HistoryCache.Store(address, summary) + a.historyCache.Store(address, summary) } case err := <-opts.RenderCtx.ErrorChan: - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - Address: address, - }) + a.emitAddressErrorMsg(err, address) default: if opts.RenderCtx.WasCanceled() { @@ -178,7 +164,7 @@ func (a *App) thing(address base.Address, freq int) error { } a.meta = *meta - history, _ := a.HistoryCache.Load(address) + history, _ := a.historyCache.Load(address) sort.Slice(history.Items, func(i, j int) bool { if history.Items[i].BlockNumber == history.Items[j].BlockNumber { return history.Items[i].TransactionIndex > history.Items[j].TransactionIndex @@ -186,12 +172,8 @@ func (a *App) thing(address base.Address, freq int) error { return history.Items[i].BlockNumber > history.Items[j].BlockNumber }) history.Summarize() - a.HistoryCache.Store(address, history) - messages.EmitMessage(a.ctx, messages.Completed, &messages.MessageMsg{ - Address: address, - Num1: a.txCount(address), - Num2: a.txCount(address), - }) + a.historyCache.Store(address, history) + a.emitProgressMsg(messages.Completed, address, a.txCount(address), a.txCount(address)) return nil } @@ -201,3 +183,36 @@ func (a *App) forceHistory() (force bool) { // EXISTING_CODE return } + +// EXISTING_CODE +func (a *App) Reload() { + switch a.session.LastRoute { + case "/names": + logger.InfoC("Reloading names") + a.names.LastUpdate = time.Time{} + if err := a.loadNames(nil, nil); err != nil { + a.emitErrorMsg(err, nil) + } + } +} + +func (a *App) GoToAddress(address base.Address) { + logger.Info("--------------------------- enter -------------------------------------------") + logger.Info("GoToAddress: ", address.Hex()) + if address == base.ZeroAddr { + logger.Info("--------------------------- zeroAddr exit -------------------------------------------") + return + } + + a.SetRoute("/history", address.Hex()) + + a.cancelContext(address) + a.historyCache.Delete(address) + a.loadHistory(address, nil, nil) + + a.emitNavigateMsg(a.GetRoute()) + a.emitInfoMsg("viewing address", address.Hex()) + logger.Info("--------------------------- exit -------------------------------------------") +} + +// EXISTING_CODE diff --git a/app/data_index.go b/app/data_index.go index 65f7b6b1..4ac5a3ae 100644 --- a/app/data_index.go +++ b/app/data_index.go @@ -8,7 +8,6 @@ import ( "sync" "sync/atomic" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" sdk "github.com/TrueBlocks/trueblocks-sdk/v3" @@ -71,12 +70,10 @@ func (a *App) loadIndexes(wg *sync.WaitGroup, errorChan chan error) error { // EXISTING_CODE // EXISTING_CODE if err := sdk.SortIndexes(a.indexes.Items, a.indexes.Sorts); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) } a.indexes.Summarize() - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: "Loaded indexes"}) + a.emitInfoMsg("Loaded indexes", "") } return nil diff --git a/app/data_manifest.go b/app/data_manifest.go index cd806dc4..e6e22563 100644 --- a/app/data_manifest.go +++ b/app/data_manifest.go @@ -8,7 +8,6 @@ import ( "sync" "sync/atomic" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" sdk "github.com/TrueBlocks/trueblocks-sdk/v3" @@ -71,12 +70,10 @@ func (a *App) loadManifests(wg *sync.WaitGroup, errorChan chan error) error { // EXISTING_CODE // EXISTING_CODE if err := sdk.SortManifests(a.manifests.Items, a.manifests.Sorts); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) } a.manifests.Summarize() - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: "Loaded manifests"}) + a.emitInfoMsg("Loaded manifests", "") } return nil diff --git a/app/data_monitor.go b/app/data_monitor.go index ab8fe3a8..ff8ab02e 100644 --- a/app/data_monitor.go +++ b/app/data_monitor.go @@ -9,7 +9,6 @@ import ( "sync" "sync/atomic" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/crud" @@ -71,7 +70,7 @@ func (a *App) loadMonitors(wg *sync.WaitGroup, errorChan chan error) error { } else { // EXISTING_CODE for i := 0; i < len(monitors); i++ { - monitors[i].Name = a.names.NamesMap[monitors[i].Address].Name + monitors[i].Name = a.namesMap[monitors[i].Address].Name } // EXISTING_CODE a.meta = *meta @@ -86,7 +85,7 @@ func (a *App) loadMonitors(wg *sync.WaitGroup, errorChan chan error) error { }) // EXISTING_CODE a.monitors.Summarize() - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: "Loaded monitors"}) + a.emitInfoMsg("Loaded monitors", "") } return nil @@ -116,9 +115,7 @@ func (a *App) ModifyMonitors(modData *ModifyData) error { } if _, _, err := opts.Monitors(); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) return err } diff --git a/app/data_name.go b/app/data_name.go index c7fd85ce..4400d5d6 100644 --- a/app/data_name.go +++ b/app/data_name.go @@ -8,7 +8,6 @@ import ( "sync" "sync/atomic" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" coreConfig "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" @@ -85,7 +84,7 @@ func (a *App) loadNames(wg *sync.WaitGroup, errorChan chan error) error { // EXISTING_CODE // EXISTING_CODE a.names.Summarize() - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: "Loaded names"}) + a.emitInfoMsg("Loaded names", "") } return nil @@ -114,7 +113,7 @@ func (a *App) ModifyName(modData *ModifyData) error { Source: "TrueBlocks Browse", Tags: "99-User-Defined", } - if existing, ok := a.names.NamesMap[modData.Address]; ok { + if existing, ok := a.namesMap[modData.Address]; ok { if existing.IsCustom { // We preserve the tags if it's already customized newName.Tags = existing.Tags @@ -128,9 +127,7 @@ func (a *App) ModifyName(modData *ModifyData) error { opts.Globals.Chain = namesChain if _, _, err := opts.ModifyName(crud.OpFromString(op), cd); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) return err } @@ -154,7 +151,7 @@ func (a *App) ModifyName(modData *ModifyData) error { } } nameMutex.Lock() - a.names.NamesMap[modData.Address] = name + a.namesMap[modData.Address] = name nameMutex.Unlock() } newArray = append(newArray, name) diff --git a/app/data_project.go b/app/data_project.go index 28281fa8..d9c25690 100644 --- a/app/data_project.go +++ b/app/data_project.go @@ -9,7 +9,6 @@ import ( "sync" "sync/atomic" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/crud" @@ -45,12 +44,11 @@ func (a *App) loadProjects(wg *sync.WaitGroup, errorChan chan error) error { // EXISTING_CODE items := []types.HistoryContainer{} - a.HistoryCache.Range(func(_ base.Address, h types.HistoryContainer) bool { + a.historyCache.Range(func(_ base.Address, h types.HistoryContainer) bool { items = append(items, h) return true }) a.projects = types.NewProjectContainer(a.session.LastChain, items) - if !a.projects.NeedsUpdate(a.forceProject()) { return nil } @@ -82,10 +80,7 @@ func (a *App) loadProjects(wg *sync.WaitGroup, errorChan chan error) error { aj := a.projects.Items[j].Address return ai.Hex() < aj.Hex() }) - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{ - String1: fmt.Sprintf("After loadProject: %d projects", len(a.projects.Items)), - }) - + a.emitInfoMsg("Loaded projects", "") return nil } @@ -97,39 +92,17 @@ func (a *App) forceProject() (force bool) { } // EXISTING_CODE -func (a *App) ModifyProject(modData *ModifyData) { +func (a *App) DeleteAddress(modData *ModifyData) { switch crud.OpFromString(modData.Operation) { case crud.Delete: a.cancelContext(modData.Address) - a.HistoryCache.Delete(modData.Address) - for i, history := range a.projects.Items { - if history.Address == modData.Address { - a.projects.Items = append(a.projects.Items[:i], a.projects.Items[i+1:]...) - break - } - } - a.loadProjects(nil, nil) - } -} + a.dirty = true -func (a *App) Reload(address base.Address) { - a.ModifyProject(&ModifyData{ - Operation: "delete", - Address: address, - }) - a.loadHistory(a.GetAddress(), nil, nil) - _ = a.Refresh() - a.loadProjects(nil, nil) -} - -func (a *App) GoToHistory(address base.Address) { - a.SetRoute("/history", address.Hex()) - a.Reload(address) + a.historyCache.Delete(modData.Address) + a.loadProjects(nil, nil) - route := "/history/" + address.Hex() - messages.EmitMessage(a.ctx, messages.Navigate, &messages.MessageMsg{ - String1: route, - }) + a.emitInfoMsg(a.getFullPath(), fmt.Sprint("deleted address", modData.Address.Hex())) + } } // EXISTING_CODE diff --git a/app/data_session.go b/app/data_session.go index 3efdc075..d69c12e7 100644 --- a/app/data_session.go +++ b/app/data_session.go @@ -8,9 +8,9 @@ import ( "sync" "sync/atomic" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" sdk "github.com/TrueBlocks/trueblocks-sdk/v3" + "github.com/wailsapp/wails/v2/pkg/runtime" ) // EXISTING_CODE @@ -69,7 +69,7 @@ func (a *App) loadSessions(wg *sync.WaitGroup, errorChan chan error) error { a.session.Session = ss // EXISTING_CODE a.session.Summarize() - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: "Loaded sessions"}) + a.emitInfoMsg("Loaded sessions", "") } return nil } @@ -81,4 +81,13 @@ func (a *App) forceSession() (force bool) { } // EXISTING_CODE +func (a *App) saveSession() { + if !isTesting { + a.session.Window.X, a.session.Window.Y = runtime.WindowGetPosition(a.ctx) + a.session.Window.Width, a.session.Window.Height = runtime.WindowGetSize(a.ctx) + a.session.Window.Y += 38 // TODO: This is a hack to account for the menu bar - not sure why it's needed + } + _ = a.session.Save() +} + // EXISTING_CODE diff --git a/app/data_settings.go b/app/data_settings.go index 4c8737b8..34429731 100644 --- a/app/data_settings.go +++ b/app/data_settings.go @@ -6,7 +6,6 @@ import ( "sync" "sync/atomic" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" coreConfig "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils" @@ -36,17 +35,13 @@ func (a *App) loadSettings(wg *sync.WaitGroup, errorChan chan error) error { _ = errorChan // delint if path, err := utils.GetConfigFn("", "trueBlocks.toml"); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) } else { if err := coreConfig.ReadToml(path, &a.config.Config); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) } } - a.loadSession() + a.settings = types.NewSettingsGroup(&a.status.Status, &a.config.Config, &a.session.Session) a.settings.Summarize() @@ -111,7 +106,7 @@ func (a *App) l oadStatus(wg *sync.WaitGroup, errorChan chan error) error { }) a.status.Summarize() logger.SetLoggerWriter(w) - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: "Loaded status"}) + a.emitInfoMsg("Loaded status") } return nil } diff --git a/app/data_status.go b/app/data_status.go index 0998cbd7..cd4db51e 100644 --- a/app/data_status.go +++ b/app/data_status.go @@ -10,7 +10,6 @@ import ( "sync" "sync/atomic" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-browse/pkg/types" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" @@ -82,7 +81,7 @@ func (a *App) loadStatus(wg *sync.WaitGroup, errorChan chan error) error { logger.SetLoggerWriter(w) // EXISTING_CODE a.status.Summarize() - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: "Loaded status"}) + a.emitInfoMsg("Loaded status", "") } return nil diff --git a/app/editor_names.go b/app/editor_names.go index 377f51a0..ab87beda 100644 --- a/app/editor_names.go +++ b/app/editor_names.go @@ -11,7 +11,7 @@ import ( ) func (a *App) LoadName(addr string) editors.Name { - if name, ok := a.names.NamesMap[base.HexToAddress(addr)]; ok { + if name, ok := a.namesMap[base.HexToAddress(addr)]; ok { logger.Info("Found name for ", name.Address.Hex()) return editors.CoreToName(name) } else { diff --git a/app/menu_file.go b/app/menu_file.go index 17b0ca93..f505549b 100644 --- a/app/menu_file.go +++ b/app/menu_file.go @@ -1,43 +1,27 @@ package app import ( - "fmt" - "path/filepath" - "sync" + "errors" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" - "github.com/TrueBlocks/trueblocks-browse/pkg/types" - "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" - coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" "github.com/wailsapp/wails/v2/pkg/menu" "github.com/wailsapp/wails/v2/pkg/runtime" ) -func (a *App) FileNew(cd *menu.CallbackData) { - // save current file (it will ask to save if it's dirty) - if _, err := a.SaveFile(); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: fmt.Sprintf("error saving project: %s", err.Error()), - }) +var ErrSavingProject = errors.New("error saving project file") +var ErrOpeningProject = errors.New("error opening file") +var ErrLoadingProject = errors.New("error loading file") +var ErrProjectNotSaved = errors.New("project not saved") + +func (a *App) FileNew(cb *menu.CallbackData) { + if ok := a.shouldSaveDialog(); !ok { return } - a.projects = types.NewProjectContainer("Untitled.tbx", []types.HistoryContainer{}) - messages.EmitMessage(a.ctx, messages.Navigate, &messages.MessageMsg{ - String1: "/", - }) - messages.EmitMessage(a.ctx, messages.Document, &messages.MessageMsg{ - String1: filepath.Join(a.session.LastFolder, a.session.LastFile), - String2: "New file created: Untitled.tbx", - }) + a.newFile() } -func (a *App) FileOpen(cd *menu.CallbackData) { - // save current file (it will ask to save if it's dirty) - if _, err := a.SaveFile(); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: fmt.Sprintf("error saving project: %s", err.Error()), - }) +func (a *App) FileOpen(cb *menu.CallbackData) { + if ok := a.shouldSaveDialog(); !ok { return } @@ -53,133 +37,26 @@ func (a *App) FileOpen(cd *menu.CallbackData) { {DisplayName: "Monitor Groups", Pattern: "*.tbx"}, }, }); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: fmt.Sprintf("error opening project: %s", err.Error()), - }) - } else if len(fn) == 0 { - messages.EmitMessage(a.ctx, messages.Document, &messages.MessageMsg{ - String1: "No file was opened", - }) - } else { - if _, err := a.LoadFile(fn); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: fmt.Sprintf("error opening project: %s", err.Error()), - }) - } else { - messages.EmitMessage(a.ctx, messages.Document, &messages.MessageMsg{ - String1: filepath.Join(a.session.LastFolder, a.session.LastFile), - String2: "Opened", - }) - - } - } -} + a.emitErrorMsg(ErrOpeningProject, err) -func (a *App) FileSave(cd *menu.CallbackData) { - a.SaveFile() -} - -func (a *App) FileSaveAs(cd *menu.CallbackData) { - a.dirty = true - a.SaveFile() -} - -// ------------------------------------------------------------------ -func (a *App) LoadFile(fn string) (bool, error) { - newProject := types.NewProjectContainer(a.session.LastChain, []types.HistoryContainer{}) - if pF, err := newProject.Load(fn); err != nil { - return false, fmt.Errorf("newProject::Load failed: %s", err.Error()) - - } else if len(pF.Addresses) == 0 { - logger.Info("I am here:", pF.String()) - return false, fmt.Errorf("project file contains no records: %s", fn) + } else if len(fn) == 0 { + a.emitInfoMsg("no file was opened", "") } else { a.CancelAllContexts() - a.HistoryCache = &types.HistoryMap{} - histories := []types.HistoryContainer{} - for _, address := range pF.Addresses { - history := types.NewHistoryContainer(a.session.LastChain, []coreTypes.Transaction{}, address) - histories = append(histories, history) - a.HistoryCache.Store(address, history) - } - a.projects = types.NewProjectContainer(a.session.LastChain, histories) - - a.session.LastFolder, a.session.LastFile = filepath.Split(fn) - a.session.LastSub[pF.Selected.Hex()] = pF.Selected.Hex() - a.saveSession() - - messages.EmitMessage(a.ctx, messages.Document, &messages.MessageMsg{ - String1: filepath.Join(a.session.LastFolder, a.session.LastFile), - String2: "Opened", - }) - - var wg sync.WaitGroup - for _, history := range a.projects.Items { - wg.Add(1) - go a.loadHistory(history.Address, &wg, nil) + if _, err := a.readFile(fn); err != nil { + a.emitErrorMsg(ErrOpeningProject, err) + } else { + a.emitInfoMsg(a.getFullPath(), "file was opened") } - wg.Wait() - - return true, nil } } -func (a *App) WriteFile(fn string) (bool, error) { - if err := a.projects.Save(fn, a.GetAddress()); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: fmt.Sprintf("error writing project: %s", err.Error()), - }) - return false, err - } - a.session.LastFolder, a.session.LastFile = filepath.Split(fn) - a.saveSession() - a.dirty = false - messages.EmitMessage(a.ctx, messages.Document, &messages.MessageMsg{ - String1: filepath.Join(a.session.LastFolder, a.session.LastFile), - String2: "Saved", - }) - return true, nil -} - -func (a *App) SaveFile() (bool, error) { - if !a.dirty { - return a.WriteFile(filepath.Join(a.session.LastFolder, a.session.LastFile)) - } - - var fn string - var err error - if isTesting { - fn = filepath.Join(a.session.LastFolder, a.session.LastFile) - } else { - fn, err = runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{ - DefaultDirectory: a.session.LastFolder, - DefaultFilename: a.session.LastFile, - Title: "Save File", - CanCreateDirectories: true, - ShowHiddenFiles: false, - TreatPackagesAsDirectories: false, - Filters: []runtime.FileFilter{ - {DisplayName: "Projects", Pattern: "*.tbx"}, - }, - }) - } - - if err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: fmt.Sprintf("error saving project: %s", err.Error()), - }) - return false, err - } else if len(fn) == 0 { - messages.EmitMessage(a.ctx, messages.Document, &messages.MessageMsg{ - String1: "User hit escape...file not saved", - }) - return false, nil - } else { - return a.WriteFile(fn) - } +func (a *App) FileSave(cb *menu.CallbackData) { + a.dirty, _ = a.saveFileDialog() } -func (a *App) Filename() string { - return filepath.Join(a.session.LastFolder, a.session.LastFile) +func (a *App) FileSaveAs(cb *menu.CallbackData) { + a.dirty = true // force the dialog + a.dirty, _ = a.saveFileDialog() } diff --git a/app/menu_file_load.go b/app/menu_file_load.go new file mode 100644 index 00000000..3dcb0e69 --- /dev/null +++ b/app/menu_file_load.go @@ -0,0 +1,59 @@ +package app + +import ( + "fmt" + "path/filepath" + + "github.com/TrueBlocks/trueblocks-browse/pkg/types" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" + coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" +) + +func (a *App) newFile() { + a.session.LastFile = "Untitled.tbx" + a.saveSession() + + address := base.HexToAddress("0x3836b0e02b4a613ba1d15834e6d77f409099d8f8") + history := types.NewHistoryContainer(a.getChain(), []coreTypes.Transaction{}, address) + + a.historyCache = &types.HistoryMap{} + a.historyCache.Store(address, history) + a.projects = types.NewProjectContainer(a.getChain(), []types.HistoryContainer{history}) + + a.emitNavigateMsg("/") + a.emitInfoMsg(a.getFullPath(), "new file created") +} + +func (a *App) readFile(fn string) (bool, error) { + newProject := types.NewProjectContainer(a.session.LastChain, []types.HistoryContainer{}) + if pF, err := newProject.Load(fn); err != nil { + return false, fmt.Errorf("%w: %v", ErrLoadingProject, err) + + } else if len(pF.Addresses) == 0 { + return false, fmt.Errorf("project file contains no records: %s", fn) + + } else { + a.CancelAllContexts() + a.historyCache = &types.HistoryMap{} + histories := []types.HistoryContainer{} + for _, address := range pF.Addresses { + history := types.NewHistoryContainer(a.getChain(), []coreTypes.Transaction{}, address) + histories = append(histories, history) + a.historyCache.Store(address, history) + } + a.projects = types.NewProjectContainer(a.getChain(), histories) + a.dirty = false + + a.session.LastFolder, a.session.LastFile = filepath.Split(fn) + a.session.LastSub["/history"] = pF.Selected.Hex() + a.saveSession() + + for _, history := range a.projects.Items { + go a.loadHistory(history.Address, nil, nil) + } + + a.emitInfoMsg(a.getFullPath(), "file was opened") + + return true, nil + } +} diff --git a/app/menu_file_save.go b/app/menu_file_save.go new file mode 100644 index 00000000..4c5d3d92 --- /dev/null +++ b/app/menu_file_save.go @@ -0,0 +1,55 @@ +package app + +import ( + "fmt" + "path/filepath" + + "github.com/wailsapp/wails/v2/pkg/runtime" +) + +func (a *App) saveFileDialog() (bool, error) { + if !a.dirty { + return true, nil + } + + fn, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{ + DefaultDirectory: a.session.LastFolder, + DefaultFilename: a.session.LastFile, + Title: "Save File", + CanCreateDirectories: true, + ShowHiddenFiles: false, + TreatPackagesAsDirectories: false, + Filters: []runtime.FileFilter{ + {DisplayName: "Projects", Pattern: "*.tbx"}, + }, + }) + + if err != nil { + a.emitErrorMsg(ErrSavingProject, err) + return false, err + + } else if len(fn) == 0 { + a.emitInfoMsg(a.getFullPath(), "file was not saved") + return false, nil + + } else { + a.CancelAllContexts() + if _, err := a.writeFile(fn); err != nil { + a.emitErrorMsg(ErrSavingProject, err) + return false, err + } else { + a.emitInfoMsg(a.getFullPath(), "file was saved") + return true, nil + } + } +} + +func (a *App) writeFile(fn string) (bool, error) { + if err := a.projects.Save(fn, a.GetSelected()); err != nil { + return false, fmt.Errorf("%w: %v", ErrProjectNotSaved, err) + } + a.dirty = false + a.session.LastFolder, a.session.LastFile = filepath.Split(fn) + a.saveSession() + return true, nil +} diff --git a/app/menu_file_shouldsave.go b/app/menu_file_shouldsave.go new file mode 100644 index 00000000..8f3d703a --- /dev/null +++ b/app/menu_file_shouldsave.go @@ -0,0 +1,38 @@ +package app + +import "strings" + +func (a *App) shouldSaveDialog() bool { + if !a.dirty { + return true + } + + if response, err := a.okCancel(saveFile); err != nil { + // there was an error, do not proceed + a.emitErrorMsg(ErrSavingProject, err) + return false + + } else if response.canceled { + // user hit cancel, do not proceed + return false + + } else if response.save { + // saveFileDialog sends messages since it is called from FileSave, etc. + if strings.HasPrefix(a.getFilename(), "Untitled.") { + if saved, err := a.saveFileDialog(); err != nil || !saved { + // there was an error or the user told us not to save. + // in both bases, messages have been sent. do not proceed. + return false + } + } else { + if a.dirty, err = a.writeFile(a.getFullPath()); err != nil { + a.emitErrorMsg(ErrSavingProject, err) + return false + } + } + } else { + // do nothing - user said not to save, so just continue + } + + return true +} diff --git a/app/menu_file_test.go b/app/menu_file_test.go deleted file mode 100644 index fcd1d714..00000000 --- a/app/menu_file_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package app - -import ( - "fmt" - "os" - "path/filepath" - "testing" - - "github.com/TrueBlocks/trueblocks-browse/pkg/types" - "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" - "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" -) - -func TestFileSave(t *testing.T) { - sessionFn := "/Users/jrush/Library/Application Support/TrueBlocks/browse/session.json" - contents := file.AsciiFileToString(sessionFn) - os.Setenv("TB_TEST_MODE", "true") - app := NewApp() - app.SetRoute("/history", "0xf503017d7baf7fbc0fff7492b751025c6a78179b") - app.SetRoute("/", "") - fn := "/tmp/test1.tbx" - app.session.LastFolder, app.session.LastFile = filepath.Split(fn) - app.saveSession() - app.projects = types.NewProjectContainer("Untitled.tbx", []types.HistoryContainer{{Address: base.HexToAddress("0xf503017d7baf7fbc0fff7492b751025c6a78179b")}}) - app.dirty = true - saved, err := app.SaveFile() - fmt.Println(saved, err) - fmt.Println(file.AsciiFileToString(fn)) - os.Remove(fn) - file.StringToAsciiFile(sessionFn, contents) -} diff --git a/app/menu_system.go b/app/menu_system.go index 616e4dc9..ed0c8c9d 100644 --- a/app/menu_system.go +++ b/app/menu_system.go @@ -6,10 +6,11 @@ import ( "github.com/wailsapp/wails/v2/pkg/runtime" ) -func (a *App) SystemAbout(cd *menu.CallbackData) { +func (a *App) SystemAbout(cb *menu.CallbackData) { logger.Info("This is the about box plus some other stuff") } -func (a *App) SystemQuit(cd *menu.CallbackData) { +func (a *App) SystemQuit(cb *menu.CallbackData) { + a.dirty, _ = a.saveFileDialog() runtime.Quit(a.ctx) } diff --git a/app/menu_toggle.go b/app/menu_toggle.go index d633c9b3..bccf7f34 100644 --- a/app/menu_toggle.go +++ b/app/menu_toggle.go @@ -7,35 +7,27 @@ import ( "github.com/wailsapp/wails/v2/pkg/menu" ) -func (a *App) HeaderToggle(cd *menu.CallbackData) { +func (a *App) HeaderToggle(cb *menu.CallbackData) { which := "header" - messages.EmitMessage(a.ctx, messages.ToggleLayout, &messages.MessageMsg{ - String1: which, - }) + a.emitMsg(messages.ToggleLayout, &messages.MessageMsg{String1: which}) } -func (a *App) MenuToggle(cd *menu.CallbackData) { +func (a *App) MenuToggle(cb *menu.CallbackData) { which := "menu" - messages.EmitMessage(a.ctx, messages.ToggleLayout, &messages.MessageMsg{ - String1: which, - }) + a.emitMsg(messages.ToggleLayout, &messages.MessageMsg{String1: which}) } -func (a *App) HelpToggle(cd *menu.CallbackData) { +func (a *App) HelpToggle(cb *menu.CallbackData) { which := "help" - messages.EmitMessage(a.ctx, messages.ToggleLayout, &messages.MessageMsg{ - String1: which, - }) + a.emitMsg(messages.ToggleLayout, &messages.MessageMsg{String1: which}) } -func (a *App) FooterToggle(cd *menu.CallbackData) { +func (a *App) FooterToggle(cb *menu.CallbackData) { which := "footer" - messages.EmitMessage(a.ctx, messages.ToggleLayout, &messages.MessageMsg{ - String1: which, - }) + a.emitMsg(messages.ToggleLayout, &messages.MessageMsg{String1: which}) } -func (a *App) AccordionToggle(cd *menu.CallbackData) { +func (a *App) AccordionToggle(cb *menu.CallbackData) { route := a.GetRoute() route = strings.TrimPrefix(route, "/") parts := strings.Split(route, "/") @@ -43,17 +35,15 @@ func (a *App) AccordionToggle(cd *menu.CallbackData) { if route == "" { route = "project" } - messages.EmitMessage(a.ctx, messages.ToggleHeader, &messages.MessageMsg{ - String1: route, - }) + a.emitMsg(messages.ToggleAccordion, &messages.MessageMsg{String1: route}) } -func (a *App) SwitchTabPrev(cd *menu.CallbackData) { +func (a *App) SwitchTabPrev(cb *menu.CallbackData) { which := "prev" - messages.EmitMessage(a.ctx, messages.SwitchTab, &messages.MessageMsg{String1: which}) + a.emitMsg(messages.SwitchTab, &messages.MessageMsg{String1: which}) } -func (a *App) SwitchTabNext(cd *menu.CallbackData) { +func (a *App) SwitchTabNext(cb *menu.CallbackData) { which := "next" - messages.EmitMessage(a.ctx, messages.SwitchTab, &messages.MessageMsg{String1: which}) + a.emitMsg(messages.SwitchTab, &messages.MessageMsg{String1: which}) } diff --git a/app/menu_view.go b/app/menu_view.go index e919872d..62ae61d2 100644 --- a/app/menu_view.go +++ b/app/menu_view.go @@ -7,57 +7,57 @@ import ( "github.com/wailsapp/wails/v2/pkg/menu" ) -func (a *App) ProjectView(cd *menu.CallbackData) { +func (a *App) ProjectView(cb *menu.CallbackData) { a.Navigate("/", "") } -func (a *App) HistoryView(cd *menu.CallbackData) { - address := a.GetAddress() +func (a *App) HistoryView(cb *menu.CallbackData) { + address := a.GetSelected() a.Navigate("/history", address.Hex()) } -func (a *App) MonitorsView(cd *menu.CallbackData) { +func (a *App) MonitorsView(cb *menu.CallbackData) { a.Navigate("/monitors", "") } -func (a *App) NamesView(cd *menu.CallbackData) { +func (a *App) NamesView(cb *menu.CallbackData) { a.Navigate("/names", "") } -func (a *App) AbisView(cd *menu.CallbackData) { +func (a *App) AbisView(cb *menu.CallbackData) { a.Navigate("/abis", "") } -func (a *App) IndexesView(cd *menu.CallbackData) { +func (a *App) IndexesView(cb *menu.CallbackData) { a.Navigate("/indexes", "") } -func (a *App) ManifestsView(cd *menu.CallbackData) { +func (a *App) ManifestsView(cb *menu.CallbackData) { a.Navigate("/manifests", "") } -func (a *App) StatusView(cd *menu.CallbackData) { +func (a *App) StatusView(cb *menu.CallbackData) { a.Navigate("/status", "") } -func (a *App) SettingsView(cd *menu.CallbackData) { +func (a *App) SettingsView(cb *menu.CallbackData) { a.Navigate("/settings", "") } -func (a *App) DaemonsView(cd *menu.CallbackData) { +func (a *App) DaemonsView(cb *menu.CallbackData) { a.Navigate("/daemons", "") } -func (a *App) SessionView(cd *menu.CallbackData) { +func (a *App) SessionView(cb *menu.CallbackData) { a.Navigate("/session", "") } -func (a *App) ConfigView(cd *menu.CallbackData) { +func (a *App) ConfigView(cb *menu.CallbackData) { a.Navigate("/config", "") } -func (a *App) WizardView(cd *menu.CallbackData) { - if a.IsConfigured() { +func (a *App) WizardView(cb *menu.CallbackData) { + if a.isConfigured() { a.StepWizard(coreTypes.Reset) } else { a.StepWizard(coreTypes.Next) diff --git a/app/message.go b/app/message.go new file mode 100644 index 00000000..914ef918 --- /dev/null +++ b/app/message.go @@ -0,0 +1,52 @@ +package app + +import ( + "fmt" + + "github.com/TrueBlocks/trueblocks-browse/pkg/messages" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" +) + +func (a *App) emitNavigateMsg(route string) { + messages.EmitMessage(a.ctx, messages.Navigate, &messages.MessageMsg{ + String1: route, + }) +} + +func (a *App) emitErrorMsg(err1, err2 error) { + if err2 != nil { + messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ + String1: fmt.Errorf("%w: %v", err1, err2).Error(), + }) + } else { + messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ + String1: fmt.Errorf("%v", err1).Error(), + }) + } +} + +func (a *App) emitAddressErrorMsg(err error, address base.Address) { + messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ + String1: err.Error(), + Address: address, + }) +} + +func (a *App) emitInfoMsg(str1, str2 string) { + messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{ + String1: str1, + String2: str2, + }) +} + +func (a *App) emitProgressMsg(msg messages.Message, address base.Address, n1, n2 int) { + messages.EmitMessage(a.ctx, msg, &messages.MessageMsg{ + Address: address, + Num1: n1, + Num2: n2, + }) +} + +func (a *App) emitMsg(msg messages.Message, val *messages.MessageMsg) { + messages.EmitMessage(a.ctx, msg, val) +} diff --git a/app/app_modify.go b/app/modify_data.go similarity index 52% rename from app/app_modify.go rename to app/modify_data.go index f46bcaab..84ef5b5f 100644 --- a/app/app_modify.go +++ b/app/modify_data.go @@ -1,15 +1,9 @@ package app -import ( - "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" -) +import "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" type ModifyData struct { Operation string `json:"operation"` Address base.Address `json:"address"` Value string `json:"value"` } - -func (a *App) UnusedFunc(modData *ModifyData) error { - return nil -} diff --git a/app/navigate.go b/app/navigate.go index ad53e7ef..962f6816 100644 --- a/app/navigate.go +++ b/app/navigate.go @@ -1,7 +1,7 @@ package app import ( - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" ) func (a *App) Navigate(route, subRoute string) { @@ -10,14 +10,12 @@ func (a *App) Navigate(route, subRoute string) { sep = "/" } - if route != "/wizard" && !a.IsConfigured() { + if route != "/wizard" && !a.isConfigured() { route, subRoute, sep = "/wizard", "", "" } a.SetRoute(route, subRoute) - debugMsg("Message sent", route, subRoute) - messages.EmitMessage(a.ctx, messages.Navigate, &messages.MessageMsg{ - String1: route + sep + subRoute, - }) + logger.Info("Message sent", route, subRoute) + a.emitNavigateMsg(route + sep + subRoute) } diff --git a/app/proc_ctxs.go b/app/proc_ctxs.go index 9378fa8a..88d9008b 100644 --- a/app/proc_ctxs.go +++ b/app/proc_ctxs.go @@ -28,9 +28,7 @@ func (a *App) cancelContext(address base.Address) (removed bool) { defer ctxMutex.Unlock() if ctxArrays, ok := a.renderCtxs[address]; ok { for _, ctx := range ctxArrays { - messages.EmitMessage(a.ctx, messages.Cancelled, &messages.MessageMsg{ - Address: address, - }) + a.emitMsg(messages.Cancelled, &messages.MessageMsg{Address: address}) ctx.Cancel() } delete(a.renderCtxs, address) diff --git a/app/proc_daemons.go b/app/proc_daemons.go index 8b6adc6f..a5903d4a 100644 --- a/app/proc_daemons.go +++ b/app/proc_daemons.go @@ -5,12 +5,6 @@ import ( "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" ) -func (a *App) startDaemons() { - go a.FreshenController.Run() - go a.ScraperController.Run() - go a.IpfsController.Run() -} - func (a *App) ToggleDaemon(name string) error { d := a.getDaemon(name) if err := d.Toggle(); err != nil { @@ -31,11 +25,11 @@ func (a *App) GetState(name string) string { func (a *App) getDaemon(name string) daemons.Daemoner { switch name { case "freshen": - return a.FreshenController + return a.freshenController case "scraper": - return a.ScraperController + return a.scraperController case "ipfs": - return a.IpfsController + return a.ipfsController default: if len(name) > 0 { logger.Fatal("getDaemon", "should not happen", name) diff --git a/app/proc_export.go b/app/proc_export.go index a9c88017..836ee250 100644 --- a/app/proc_export.go +++ b/app/proc_export.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" @@ -23,7 +22,7 @@ func (a *App) ExportAddress(address base.Address) { fn := fmt.Sprintf("history_%s.csv", address) lines := make([]string, 0, a.txCount(address)+2) - exportLine := func(item coreTypes.Transaction, data any) bool { + exportLine := func(item *coreTypes.Transaction, data any) bool { if len(lines) == 0 { lines = append(lines, fmt.Sprintf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", "BlockNumber", @@ -60,13 +59,11 @@ func (a *App) ExportAddress(address base.Address) { return true } - h, _ := a.HistoryCache.Load(address) + h, _ := a.historyCache.Load(address) completed := h.ForEveryTransaction(exportLine, nil) if !completed { err := fmt.Errorf("export for %s interrupted after %d lines", address.Hex(), len(lines)) - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) return } diff --git a/app/proc_refresh.go b/app/proc_refresh.go index 1b7bf877..77e555b1 100644 --- a/app/proc_refresh.go +++ b/app/proc_refresh.go @@ -7,7 +7,6 @@ import ( "time" "github.com/TrueBlocks/trueblocks-browse/pkg/messages" - "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/colors" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" ) @@ -18,7 +17,7 @@ var freshenMutex sync.Mutex // by extension the frontend to update. We protect against updating too fast... Note // that this routine is called as a goroutine. func (a *App) Refresh() error { - if !a.IsConfigured() { + if !a.isConfigured() { return fmt.Errorf("App not configured") } @@ -30,16 +29,14 @@ func (a *App) Refresh() error { freshenMutex.Lock() defer freshenMutex.Unlock() - if !a.ScraperController.IsRunning() { - logger.Info(colors.Green, "Freshening...", colors.Off) + if !a.scraperController.IsRunning() { + logger.InfoG("Freshening...") } // We always load names first since we need them everywhere err := a.loadNames(nil, nil) if err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) } // And then update everything else in the fullness of time @@ -47,14 +44,14 @@ func (a *App) Refresh() error { errorChan := make(chan error, 5) // Buffered channel to hold up to 5 errors (one from each goroutine) wg.Add(8) - go a.loadAbis(&wg, errorChan) - go a.loadManifests(&wg, errorChan) + go a.loadProjects(&wg, errorChan) go a.loadMonitors(&wg, errorChan) - go a.loadIndexes(&wg, errorChan) - go a.loadStatus(&wg, errorChan) go a.loadSessions(&wg, errorChan) go a.loadSettings(&wg, errorChan) - go a.loadProjects(&wg, errorChan) + go a.loadStatus(&wg, errorChan) + go a.loadAbis(&wg, errorChan) + go a.loadManifests(&wg, errorChan) + go a.loadIndexes(&wg, errorChan) go func() { wg.Wait() @@ -71,16 +68,14 @@ func (a *App) Refresh() error { if len(errors) > 0 { // Handle errors, e.g., wait 1/2 second between each error message for _, err := range errors { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) time.Sleep(500 * time.Millisecond) } } else { - messages.EmitMessage(a.ctx, messages.Daemon, &messages.MessageMsg{ - Name: a.FreshenController.Name, + a.emitMsg(messages.Daemon, &messages.MessageMsg{ + Name: a.freshenController.Name, String1: "Freshening...", - String2: a.FreshenController.Color, + String2: a.freshenController.Color, }) } return nil diff --git a/app/util_address.go b/app/util_address.go index 3d05e412..df823868 100644 --- a/app/util_address.go +++ b/app/util_address.go @@ -3,13 +3,12 @@ package app import ( "strings" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" sdk "github.com/TrueBlocks/trueblocks-sdk/v3" ) func (a *App) ConvertToAddress(addr string) (base.Address, bool) { - if !a.IsConfigured() { + if !a.isConfigured() { return base.ZeroAddr, false } @@ -18,7 +17,7 @@ func (a *App) ConvertToAddress(addr string) (base.Address, bool) { return ret, ret != base.ZeroAddr } - ensAddr, exists := a.EnsCache.Load(addr) + ensAddr, exists := a.ensCache.Load(addr) if exists { return ensAddr.(base.Address), true } @@ -29,14 +28,12 @@ func (a *App) ConvertToAddress(addr string) (base.Address, bool) { Globals: a.toGlobals(), } if names, meta, err := opts.Names(); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) return base.ZeroAddr, false } else { a.meta = *meta if len(names) > 0 { - a.EnsCache.Store(addr, names[0].Address) + a.ensCache.Store(addr, names[0].Address) return names[0].Address, true } else { ret := base.HexToAddress(addr) @@ -46,7 +43,7 @@ func (a *App) ConvertToAddress(addr string) (base.Address, bool) { } func (a *App) AddrToName(address base.Address) string { - if name, exists := a.names.NamesMap[address]; exists { + if name, exists := a.namesMap[address]; exists { return name.Name } return "" diff --git a/app/util_balance.go b/app/util_balance.go index 2ec44f6a..e69c906e 100644 --- a/app/util_balance.go +++ b/app/util_balance.go @@ -6,11 +6,11 @@ import ( ) func (a *App) getBalance(address base.Address) string { - if !a.IsConfigured() { + if !a.isConfigured() { return "0" } - b, exists := a.BalanceCache.Load(address) + b, exists := a.balanceCache.Load(address) if exists { return b.(string) } @@ -28,7 +28,7 @@ func (a *App) getBalance(address base.Address) string { } else { a.meta = *meta value := balances[0].Balance.ToEtherStr(18) - a.BalanceCache.Store(address, value) + a.balanceCache.Store(address, value) return value } } diff --git a/app/util_debug.go b/app/util_debug.go new file mode 100644 index 00000000..afae1431 --- /dev/null +++ b/app/util_debug.go @@ -0,0 +1,17 @@ +package app + +import ( + "os" + + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" +) + +func (a *App) Logger(msg []string) { + logger.Info(msg) +} + +var isTesting bool + +func init() { + isTesting = os.Getenv("TB_TEST_MODE") == "true" +} diff --git a/app/util_explore.go b/app/util_explore.go index 436fc958..4d4dcabd 100644 --- a/app/util_explore.go +++ b/app/util_explore.go @@ -4,7 +4,6 @@ import ( "fmt" "os" - "github.com/TrueBlocks/trueblocks-browse/pkg/messages" sdk "github.com/TrueBlocks/trueblocks-sdk/v3" ) @@ -25,15 +24,11 @@ func (a *App) GetExploreUrl(term string, google, dalle bool) string { // TODO: Expose this to the user and/or put it in trueBlocks.toml os.Setenv("TB_DALLE_SERIES", "five-tone-postal-protozoa") if result, meta, err := opts.Explore(); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) return "" } else if (result == nil) || (len(result) == 0) { err := fmt.Errorf("url not found") - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) return "" } else { a.meta = *meta diff --git a/app/util_okcancel.go b/app/util_okcancel.go new file mode 100644 index 00000000..2a0cf581 --- /dev/null +++ b/app/util_okcancel.go @@ -0,0 +1,40 @@ +package app + +import "github.com/wailsapp/wails/v2/pkg/runtime" + +type OkCancel struct { + title string + msg string + options []string + // output + save bool + canceled bool +} + +var saveFile = OkCancel{ + title: "Unsaved Changes", + msg: "File has changed. Save it?", + options: []string{"Save", "Don't Save", "Cancel"}, +} + +func (a *App) okCancel(in OkCancel) (OkCancel, error) { + if !a.dirty { + return OkCancel{}, nil + } + + a.CancelAllContexts() + if response, err := runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{ + Type: runtime.QuestionDialog, + Title: in.title, + Message: in.msg, + Buttons: in.options, + }); err != nil { + return OkCancel{}, err + } else if response == "Don't Save" { + return OkCancel{}, nil + } else if response == "Cancel" { + return OkCancel{canceled: true}, nil + } else { + return OkCancel{save: true}, nil + } +} diff --git a/app/wizard.go b/app/wizard.go index 126cebe3..8eaadc3b 100644 --- a/app/wizard.go +++ b/app/wizard.go @@ -5,25 +5,23 @@ import ( coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" ) -func (a *App) IsConfigured() bool { - return a.GetWizardState() == coreTypes.Okay +func (a *App) isConfigured() bool { + return a.getWizardState() == coreTypes.Okay } -func (a *App) GetWizardState() coreTypes.WizState { +func (a *App) getWizardState() coreTypes.WizState { return a.session.Wizard.State } func (a *App) StepWizard(step coreTypes.WizStep) coreTypes.WizState { defer func() { - if a.IsConfigured() { + if a.isConfigured() { a.Navigate("/", "") } - messages.EmitMessage(a.ctx, messages.Wizard, &messages.MessageMsg{ - State: a.session.Wizard.State, - }) + a.emitMsg(messages.Wizard, &messages.MessageMsg{State: a.session.Wizard.State}) }() a.session.Wizard.Step(step) a.saveSession() - return a.GetWizardState() + return a.getWizardState() } diff --git a/code_gen/templates/generators/codebase/app_menu+view.go.tmpl b/code_gen/templates/generators/codebase/app_menu+view.go.tmpl index d19f33f8..6747e65f 100644 --- a/code_gen/templates/generators/codebase/app_menu+view.go.tmpl +++ b/code_gen/templates/generators/codebase/app_menu+view.go.tmpl @@ -8,12 +8,12 @@ import ( ) {{range .Structures -}} -func (a *App) {{firstUpper .UiRouteName}}View(cd *menu.CallbackData) { +func (a *App) {{firstUpper .UiRouteName}}View(cb *menu.CallbackData) { {{if .IsHistory -}} - address := a.GetAddress() + address := a.GetSelected() a.Navigate("/{{toLower .UiRouteName}}", address.Hex()) {{- else if .IsWizard -}} - if a.IsConfigured() { + if a.isConfigured() { a.StepWizard(coreTypes.Reset) } else { a.StepWizard(coreTypes.Next) diff --git a/code_gen/templates/generators/types/app_data+type.go.tmpl b/code_gen/templates/generators/types/app_data+type.go.tmpl index 13f02c9b..56c61815 100644 --- a/code_gen/templates/generators/types/app_data+type.go.tmpl +++ b/code_gen/templates/generators/types/app_data+type.go.tmpl @@ -62,13 +62,11 @@ func (a *App) load{{toPlural .Class}}(wg *sync.WaitGroup, errorChan chan error) // EXISTING_CODE {{if .HasSorts -}} if err := sdk.Sort{{toPlural .Class}}(a.{{toLowerPlural .Class}}.Items, a.{{toLowerPlural .Class}}.Sorts); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - }) + a.emitErrorMsg(err, nil) } {{end -}} a.{{toLowerPlural .Class}}.Summarize() - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: "Loaded {{toLowerPlural .Class}}"}) + a.emitInfoMsg("Loaded {{toLowerPlural .Class}}") } return nil @@ -82,10 +80,7 @@ func (a *App) load{{toPlural .Class}}(wg *sync.WaitGroup, errorChan chan error) opts.Globals.Decache = true if _, _, err := opts.{{toPlural .Class}}(); err != nil { - messages.EmitMessage(a.ctx, messages.Error, &messages.MessageMsg{ - String1: err.Error(), - Address: modData.Address, - }) + a.emitAddressErrorMsg(err, modData.Address) return err } else { new{{toPlural .Class}} := make([]{{.ItemType}}, 0, len(a.{{toLowerPlural .Class}}.Items)) @@ -101,7 +96,7 @@ func (a *App) load{{toPlural .Class}}(wg *sync.WaitGroup, errorChan chan error) a.{{toLowerPlural .Class}}.LastUpdate = time.Time{} a.{{toLowerPlural .Class}}.Items = new{{toPlural .Class}} msg := fmt.Sprintf("Modify{{.Class}} delete: %s", modData.Address.Hex()) - messages.EmitMessage(a.ctx, messages.Info, &messages.MessageMsg{String1: msg}) + a.emitInfoMsg(msg) return nil } } diff --git a/code_gen/templates/generators/types/pkg_types_types+containertype.go.tmpl b/code_gen/templates/generators/types/pkg_types_types+containertype.go.tmpl index 161e1a14..f3e89d2f 100644 --- a/code_gen/templates/generators/types/pkg_types_types+containertype.go.tmpl +++ b/code_gen/templates/generators/types/pkg_types_types+containertype.go.tmpl @@ -40,6 +40,7 @@ func (s *{{.Class}}Container) String() string { func (s *{{.Class}}Container) NeedsUpdate(force bool) bool { latest, reload := s.get{{.Class}}Reload() if force || reload { + logger.InfoG("{{.Class}}Container", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } diff --git a/frontend/src/components/buttons/CopyButton.tsx b/frontend/src/components/buttons/CopyButton.tsx index 1929cd3a..3176d946 100644 --- a/frontend/src/components/buttons/CopyButton.tsx +++ b/frontend/src/components/buttons/CopyButton.tsx @@ -1,12 +1,12 @@ import { IconCopy } from "@tabler/icons-react"; -import { BaseButton, ButtonProps, ButtonMouseEvent, notifyCopy } from "@components"; +import { BaseButton, ButtonProps, notifyCopy } from "@components"; import { useUtils } from "@hooks"; import { ClipboardSetText } from "@runtime"; // CopyButton copies the address of the row to the clipboard. export const CopyButton = ({ value, onClose, ...props }: ButtonProps) => { const { ShortenAddr } = useUtils(); - const handleClick = (e: ButtonMouseEvent) => { + const handleClick = () => { ClipboardSetText(value as string).then(() => {}); notifyCopy(ShortenAddr(value as string)); }; diff --git a/frontend/src/components/buttons/ExportButton.tsx b/frontend/src/components/buttons/ExportButton.tsx index 30d4337c..8e438e0a 100644 --- a/frontend/src/components/buttons/ExportButton.tsx +++ b/frontend/src/components/buttons/ExportButton.tsx @@ -5,11 +5,9 @@ import { base } from "@gocode/models"; // ExportButton exports an address's history to a .csv file. export const ExportButton = ({ value, ...props }: ButtonProps) => { - const icon = ; - const handleClick = () => { ExportAddress(value as base.Address); }; - return ; + return } />; }; diff --git a/frontend/src/components/buttons/ViewButton.tsx b/frontend/src/components/buttons/ViewButton.tsx index ce2572b3..a8198972 100644 --- a/frontend/src/components/buttons/ViewButton.tsx +++ b/frontend/src/components/buttons/ViewButton.tsx @@ -1,16 +1,15 @@ import { IconLink } from "@tabler/icons-react"; import { BaseButton, ButtonProps } from "@components"; -import { GoToHistory } from "@gocode/app/App"; +import { GoToAddress } from "@gocode/app/App"; import { base } from "@gocode/models"; // ViewButton opens the history page for a given address. export const ViewButton = ({ value, ...props }: ButtonProps) => { const address = value as base.Address; - const icon = ; const handleClick = () => { - GoToHistory(address).then(() => {}); + GoToAddress(address).then(() => {}); }; - return ; + return } />; }; diff --git a/frontend/src/components/dialogs/AddressDialog.tsx b/frontend/src/components/dialogs/AddressDialog.tsx index 2a20ec94..9ef2c7d4 100644 --- a/frontend/src/components/dialogs/AddressDialog.tsx +++ b/frontend/src/components/dialogs/AddressDialog.tsx @@ -1,13 +1,10 @@ import { useState } from "react"; -import { Logger } from "@gocode/app/App"; function InputDialog({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) { const [inputValue, setInputValue] = useState(""); - const handleSubmit = async () => { + const handleSubmit = () => { try { - // const result = - await Logger(inputValue); // Handle the result as needed onClose(); } catch (error) { diff --git a/frontend/src/components/editors/Validators.tsx b/frontend/src/components/editors/Validators.tsx index d80e5e0d..50cb6c17 100644 --- a/frontend/src/components/editors/Validators.tsx +++ b/frontend/src/components/editors/Validators.tsx @@ -1,5 +1,3 @@ -import React from "react"; - export function validAddress(value: string) { if (!/^0x[a-fA-F0-9]{40}$/.test(value) && !/^[a-zA-Z0-9.-]+\.eth$/.test(value)) { return "Please enter a valid Ethereum address."; diff --git a/frontend/src/components/formatters/AddressFormatter.tsx b/frontend/src/components/formatters/AddressFormatter.tsx index 71a7f30c..3fd1414f 100644 --- a/frontend/src/components/formatters/AddressFormatter.tsx +++ b/frontend/src/components/formatters/AddressFormatter.tsx @@ -27,7 +27,7 @@ export const AddressFormatter = ({ value, value2, className, mode = EdMode.All } const givenAddress = value as unknown as string; useEffect(() => { - const formatAddress = async () => { + const formatAddress = () => { if (!givenAddress || givenAddress === "0x0") { setLine1(givenName); setLine2(""); diff --git a/frontend/src/components/formatters/LoadProgress.tsx b/frontend/src/components/formatters/LoadProgress.tsx index e4c047aa..76884520 100644 --- a/frontend/src/components/formatters/LoadProgress.tsx +++ b/frontend/src/components/formatters/LoadProgress.tsx @@ -1,16 +1,36 @@ +import { useState, useEffect } from "react"; import { Progress, Text } from "@mantine/core"; import { FormatterProps } from "@components"; export const LoadProgress = ({ value, value2 }: Omit) => { - const n2 = value2 as number; - const loadedItems = Math.round((value / 100) * n2); // Calculate X + const [loaded, setLoaded] = useState(0); + const [total, setTotal] = useState(0); - if (value > 98) { + useEffect(() => { + setTotal(value2 as number); + }, [value2]); + + useEffect(() => { + setLoaded(Math.round((value * total) / 100)); + }, [value, total]); + + if (loaded === 0) { return ( <> - {loadedItems} of {n2} + not loaded + + + ); + } + + if (value > 98) { + return ( + <> + + + {loaded} of {total} ); @@ -20,7 +40,7 @@ export const LoadProgress = ({ value, value2 }: Omit) => <> - {loadedItems} of {n2} + {loaded} of {total} ); diff --git a/frontend/src/components/index.tsx b/frontend/src/components/index.tsx index a5aa9dd6..ac5575b9 100644 --- a/frontend/src/components/index.tsx +++ b/frontend/src/components/index.tsx @@ -1,5 +1,4 @@ export * from "./buttons"; -export * from "./dialogs"; export * from "./editors"; export * from "./formatters"; export * from "./help"; diff --git a/frontend/src/components/layout/Menu.tsx b/frontend/src/components/layout/Menu.tsx index 059b6dc0..7ead02a4 100644 --- a/frontend/src/components/layout/Menu.tsx +++ b/frontend/src/components/layout/Menu.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { useLocation } from "wouter"; import { StyledNavLink } from "@components"; -import { GetRoute, GetAddress, SetRoute } from "@gocode/app/App"; +import { GetRoute, GetSelected, SetRoute } from "@gocode/app/App"; import { messages } from "@gocode/models"; import { routeItems, RouteItem } from "@layout"; import { EventsOn, EventsOff } from "@runtime"; @@ -39,11 +39,11 @@ export const Menu = () => { const handleRouteChange = (route: string) => { setActiveRoute(route); if (route.startsWith("/history")) { - GetAddress().then((address) => { + GetSelected().then((address) => { const addr = address as unknown as string; route = route.replace(":address", addr); setLocation(route); - SetRoute(route, addr); + SetRoute("/history", addr); }); setActiveRoute("/history/:address"); } else { diff --git a/frontend/src/components/view/ViewStatus.tsx b/frontend/src/components/view/ViewStatus.tsx index 09cc098d..28bb8c7e 100644 --- a/frontend/src/components/view/ViewStatus.tsx +++ b/frontend/src/components/view/ViewStatus.tsx @@ -11,17 +11,6 @@ export const ViewStatus = () => { const [color, setColor] = useState("green"); useEffect(() => { - const handleDocument = (msg: messages.MessageMsg) => { - setStatusMessage(`${msg.string2} ${msg.string1}`); - setColor("green"); - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - } - timeoutRef.current = setTimeout(() => { - setStatusMessage(""); - }, 2000); - }; - const handleProgress = (msg: messages.MessageMsg) => { setStatusMessage(`Progress (${msg.address}): ${msg.num1}/${msg.num2}`); setColor("green"); @@ -63,7 +52,7 @@ export const ViewStatus = () => { }; const handleInfo = (msg: messages.MessageMsg) => { - setStatusMessage(`Info [${new Date().toLocaleString()}]: ${msg.string1}`); + setStatusMessage(`Info ${msg.string2} ${msg.string1}`); setColor("blue"); if (timeoutRef.current) { clearTimeout(timeoutRef.current); @@ -76,7 +65,6 @@ export const ViewStatus = () => { const { Message } = messages; EventsOn(Message.CANCELLED, handleCancelled); EventsOn(Message.COMPLETED, handleCompleted); - EventsOn(Message.DOCUMENT, handleDocument); EventsOn(Message.ERROR, handleError); EventsOn(Message.INFO, handleInfo); EventsOn(Message.PROGRESS, handleProgress); @@ -85,7 +73,6 @@ export const ViewStatus = () => { return () => { EventsOff(Message.CANCELLED); EventsOff(Message.COMPLETED); - EventsOff(Message.DOCUMENT); EventsOff(Message.ERROR); EventsOff(Message.INFO); EventsOff(Message.PROGRESS); diff --git a/frontend/src/hooks/useEnvironment.tsx b/frontend/src/hooks/useEnvironment.tsx index 8460a29b..d42569f5 100644 --- a/frontend/src/hooks/useEnvironment.tsx +++ b/frontend/src/hooks/useEnvironment.tsx @@ -5,7 +5,7 @@ export const useEnvironment = (key: string): string => { const [envMap, setEnvMap] = useState>(new Map()); useEffect(() => { - const fetchEnvValue = async () => { + const fetchEnvValue = () => { if (!envMap.has(key)) { GetEnv(key).then((val: string) => { setEnvMap((prevMap) => new Map(prevMap).set(key, val)); diff --git a/frontend/src/hooks/useKeyboardPaging.tsx b/frontend/src/hooks/useKeyboardPaging.tsx index e2f4969f..53be590b 100644 --- a/frontend/src/hooks/useKeyboardPaging.tsx +++ b/frontend/src/hooks/useKeyboardPaging.tsx @@ -10,12 +10,7 @@ export type Page = { getOffset: () => number; }; -export function useKeyboardPaging( - nItems: number, - perPage: number = 20, - onEnter: (page: Page) => void -): Pager { - const { address } = useAppState(); +export function useKeyboardPaging(nItems: number, perPage: number = 20, onEnter: (page: Page) => void): Pager { const [pageNumber, setPageNumber] = useState(1); const [lastPage, setLastPage] = useState(1); const [selected, setSelected] = useState(0); @@ -80,9 +75,10 @@ export function useKeyboardPaging( e.preventDefault(); CancelAllContexts(); }); + useHotkeys("mod+r", (e) => { e.preventDefault(); - Reload(address).then(() => {}); + Reload().then(() => {}); }); useHotkeys( diff --git a/frontend/src/layout/Footer.tsx b/frontend/src/layout/Footer.tsx index 49c4d574..824233eb 100644 --- a/frontend/src/layout/Footer.tsx +++ b/frontend/src/layout/Footer.tsx @@ -2,10 +2,22 @@ import { Text } from "@mantine/core"; import { useAppState } from "@state"; export const Footer = () => { - const { chain, status, filename } = useAppState(); + const { chain, status, info } = useAppState(); + + const fn = () => { + if (info.filename) { + if (info.dirty) { + return {info.filename} (dirty); + } + return <>{info.filename}; + } + return <>{"no file loaded"}; + }; + return ( - {`${status.clientVersion} / ${chain} / ${"not loaded"} / ${status.lastUpdate} / file: ${filename}`} + + {`${status.clientVersion} / ${chain} / not loaded / ${status.lastUpdate} file: `} + {fn()} + ); }; diff --git a/frontend/src/state/AppState.tsx b/frontend/src/state/AppState.tsx index 45cd6659..a7122af8 100644 --- a/frontend/src/state/AppState.tsx +++ b/frontend/src/state/AppState.tsx @@ -8,15 +8,12 @@ import { IndexPage, ManifestPage, SettingsPage, - GetChain, SetChain, - GetMeta, - GetWizardState, StatusPage, SessionPage, - Filename, + GetAppInfo, } from "@gocode/app/App"; -import { base, messages, types } from "@gocode/models"; +import { app, base, messages, types } from "@gocode/models"; import { EventsOff, EventsOn } from "@runtime"; interface AppStateProps { @@ -54,15 +51,13 @@ interface AppStateProps { address: base.Address; setAddress: (address: base.Address) => void; + info: app.AppInfo; chain: string; - filename: string; - selectChain: (newChain: string) => void; - meta: types.MetaData; - setMeta: (meta: types.MetaData) => void; - isConfigured: boolean; wizardState: types.WizState; + selectChain: (newChain: string) => void; + setMeta: (meta: types.MetaData) => void; setWizardState: (state: types.WizState) => void; } @@ -82,12 +77,12 @@ export const AppStateProvider: React.FC<{ children: ReactNode }> = ({ children } // TODO BOGUS: The daemon state should be in the AppState const [address, setAddress] = useState("0x0" as unknown as base.Address); - const [chain, setChain] = useState("mainnet"); - const [filename, setFilename] = useState("mainnet"); - const [meta, setMeta] = useState({} as types.MetaData); + const [chain, setChain] = useState("mainnet"); const [isConfigured, setIsConfigured] = useState(false); const [wizardState, setWizardState] = useState(types.WizState.WELCOME); + const [meta, setMeta] = useState({} as types.MetaData); + const [info, setInfo] = useState({} as app.AppInfo); const fetchProject = async (currentItem: number, itemsPerPage: number) => { ProjectPage(currentItem, itemsPerPage).then((item: types.ProjectContainer) => { @@ -165,28 +160,13 @@ export const AppStateProvider: React.FC<{ children: ReactNode }> = ({ children } }); }; - const fetchChain = async () => { - GetChain().then((chain) => { - setChain(chain); - }); - Filename().then((name) => { - setFilename(name); - }); - }; - - const fetchMeta = async () => { - GetMeta().then((meta) => { - setMeta(meta); - }); - }; - - useEffect(() => { - setIsConfigured(wizardState == types.WizState.OKAY); - }, [wizardState]); - - const fetchWizard = async () => { - GetWizardState().then((state) => { - setWizardState(state); + const fetchAppInfo = () => { + GetAppInfo().then((info) => { + setChain(info.chain); + setMeta(info.meta); + setWizardState(info.state); + setIsConfigured(info.isConfigured); + setInfo(info); }); }; @@ -206,46 +186,24 @@ export const AppStateProvider: React.FC<{ children: ReactNode }> = ({ children } }); }, []); // eslint-disable-line react-hooks/exhaustive-deps - useEffect(() => { - fetchChain(); - fetchMeta(); - fetchWizard(); - fetchStatus(0, 100); - }, []); - useEffect(() => { const handleRefresh = () => { - fetchChain(); - fetchMeta(); - fetchWizard(); + fetchAppInfo(); fetchStatus(0, 100); }; + handleRefresh(); // first load + // when messaged const { Message } = messages; EventsOn(Message.DAEMON, handleRefresh); - EventsOn(Message.DOCUMENT, handleRefresh); + EventsOn(Message.WIZARD, handleRefresh); return () => { EventsOff(Message.DAEMON); - EventsOff(Message.DOCUMENT); - }; - }, []); - - useEffect(() => { - const handleWizard = () => { - fetchWizard(); - }; - - const { Message } = messages; - EventsOn(Message.WIZARD, handleWizard); - return () => { EventsOff(Message.WIZARD); }; }, []); const state = { - address, - chain, - filename, project, fetchProject, history, @@ -267,12 +225,15 @@ export const AppStateProvider: React.FC<{ children: ReactNode }> = ({ children } fetchStatus, session, fetchSession, - setAddress, - selectChain, + address, + info, + chain, meta, - setMeta, isConfigured, wizardState, + setAddress, + selectChain, + setMeta, setWizardState, }; diff --git a/frontend/src/state/ViewState.tsx b/frontend/src/state/ViewState.tsx index c50c64d4..1635646e 100644 --- a/frontend/src/state/ViewState.tsx +++ b/frontend/src/state/ViewState.tsx @@ -53,7 +53,7 @@ export const ViewStateProvider = ({ route, nItems = -1, fetchFn, modifyFn, onEnt }, [route]); useEffect(() => { - const handleToggle = (msg: messages.MessageMsg) => { + const handleAccordion = (msg: messages.MessageMsg) => { const cmp = route === "" ? "project" : route; if (msg.string2 === "" && cmp === msg.string1) { IsShowing(cmp).then((onOff) => { @@ -65,9 +65,9 @@ export const ViewStateProvider = ({ route, nItems = -1, fetchFn, modifyFn, onEnt }; const { Message } = messages; - EventsOn(Message.TOGGLEHEADER, handleToggle); + EventsOn(Message.TOGGLEACCORDION, handleAccordion); return () => { - EventsOff(Message.TOGGLEHEADER); + EventsOff(Message.TOGGLEACCORDION); }; }, [route]); diff --git a/frontend/src/views/Monitors/MonitorsView.tsx b/frontend/src/views/Monitors/MonitorsView.tsx index 517abc27..ecf6a0f4 100644 --- a/frontend/src/views/Monitors/MonitorsView.tsx +++ b/frontend/src/views/Monitors/MonitorsView.tsx @@ -1,6 +1,6 @@ import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { View, FormTable, ViewForm } from "@components"; -import { GoToHistory, ModifyMonitors } from "@gocode/app/App"; +import { GoToAddress, ModifyMonitors } from "@gocode/app/App"; import { Page } from "@hooks"; import { useAppState, ViewStateProvider } from "@state"; import { MonitorsTableDef, MonitorFormDef } from "."; @@ -10,7 +10,7 @@ export const MonitorsView = () => { const handleEnter = (page: Page) => { const address = monitors.items[page.getRecord()].address; - GoToHistory(address).then(() => {}); + GoToAddress(address).then(() => {}); }; const table = useReactTable({ diff --git a/frontend/src/views/Names/NamesView.tsx b/frontend/src/views/Names/NamesView.tsx index 1089d802..301505dc 100644 --- a/frontend/src/views/Names/NamesView.tsx +++ b/frontend/src/views/Names/NamesView.tsx @@ -1,6 +1,6 @@ import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { View, FormTable, ViewForm } from "@components"; -import { GoToHistory, ModifyName } from "@gocode/app/App"; +import { GoToAddress, ModifyName } from "@gocode/app/App"; import { Page } from "@hooks"; import { useAppState, ViewStateProvider } from "@state"; import { NamesFormDef, NamesTableDef } from "."; @@ -10,7 +10,7 @@ export const NamesView = () => { const handleEnter = (page: Page) => { const address = names.items[page.getRecord()].address; - GoToHistory(address).then(() => {}); + GoToAddress(address).then(() => {}); }; const table = useReactTable({ diff --git a/frontend/src/views/Project/ProjectFormDef.tsx b/frontend/src/views/Project/ProjectFormDef.tsx index 28d0f90e..55ed3978 100644 --- a/frontend/src/views/Project/ProjectFormDef.tsx +++ b/frontend/src/views/Project/ProjectFormDef.tsx @@ -1,13 +1,15 @@ import { DataTable, FieldGroup, AddButton } from "@components"; import { types } from "@gocode/models"; +import { useAppState } from "../../state"; export const ProjectFormDef = (table: any): FieldGroup[] => { + const { info } = useAppState(); return [ { label: "Data 1", colSpan: 4, fields: [ - // { label: "fileName", type: "text", accessor: "filename" }, + // { label: "fileName", type: "text", accessor: "lastFile" }, { label: "nHistories", type: "int", accessor: "nItems" }, { label: "historySize", type: "bytes", accessor: "historySize" }, // { label: "dirty", type: "boolean", accessor: "dirty" }, @@ -36,7 +38,7 @@ export const ProjectFormDef = (table: any): FieldGroup[] buttons: [], }, { - label: "Histories", + label: info.filename, fields: [], collapsable: false, components: [ key={"dataTable"} table={table} loading={false} />], diff --git a/frontend/src/views/Project/ProjectView.tsx b/frontend/src/views/Project/ProjectView.tsx index 1381c4a1..8ea738bd 100644 --- a/frontend/src/views/Project/ProjectView.tsx +++ b/frontend/src/views/Project/ProjectView.tsx @@ -1,23 +1,23 @@ import { useEffect } from "react"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { View, FormTable, ViewForm } from "@components"; -import { GoToHistory, ModifyProject } from "@gocode/app/App"; +import { GoToAddress, DeleteAddress } from "@gocode/app/App"; import { Page } from "@hooks"; import { useAppState, ViewStateProvider } from "@state"; import { ProjectTableDefNoDelete, ProjectTableDef, ProjectFormDef } from "."; export const ProjectView = () => { - const { project, fetchProject, filename } = useAppState(); + const { project, fetchProject, info } = useAppState(); useEffect(() => { fetchProject(0, 100); - }, [filename, fetchProject]); + }, [info.filename, fetchProject]); const handleEnter = (page: Page) => { if (project && project.items) { const history = project.items[page.getRecord()]; if (history && history.address) { - GoToHistory(history.address).then(() => {}); + GoToAddress(history.address).then(() => {}); } } }; @@ -45,7 +45,7 @@ export const ProjectView = () => { nItems={project.nItems} fetchFn={fetchProject} onEnter={handleEnter} - modifyFn={ModifyProject} + modifyFn={DeleteAddress} > diff --git a/frontend/wailsjs/go/app/App.d.ts b/frontend/wailsjs/go/app/App.d.ts index 23b371a5..7e9e38ae 100755 --- a/frontend/wailsjs/go/app/App.d.ts +++ b/frontend/wailsjs/go/app/App.d.ts @@ -4,11 +4,10 @@ import {types} from '../models'; import {menu} from '../models'; import {base} from '../models'; import {sdk} from '../models'; +import {app} from '../models'; import {configtypes} from '../models'; import {context} from '../models'; import {editors} from '../models'; -import {app} from '../models'; -import {output} from '../models'; export function AbiPage(arg1:number,arg2:number):Promise; @@ -22,14 +21,14 @@ export function Caching(arg1:sdk.CacheOp):Promise; export function CancelAllContexts():Promise; -export function CancelContext(arg1:base.Address):Promise; - -export function ConfigPage(arg1:number,arg2:number):Promise; +export function ConfigView(arg1:menu.CallbackData):Promise; export function ConvertToAddress(arg1:string):Promise; export function DaemonsView(arg1:menu.CallbackData):Promise; +export function DeleteAddress(arg1:app.ModifyData):Promise; + export function ExportAddress(arg1:base.Address):Promise; export function FileNew(arg1:menu.CallbackData):Promise; @@ -42,12 +41,10 @@ export function FileSaveAs(arg1:menu.CallbackData):Promise; export function FooterToggle(arg1:menu.CallbackData):Promise; -export function GetAddress():Promise; +export function GetAppInfo():Promise; export function GetAppTitle():Promise; -export function GetChain():Promise; - export function GetChainInfo(arg1:string):Promise; export function GetChains():Promise>; @@ -64,19 +61,17 @@ export function GetExploreUrl(arg1:string,arg2:boolean,arg3:boolean):Promise; -export function GetMeta():Promise; - export function GetRoute():Promise; +export function GetSelected():Promise; + export function GetSession():Promise; export function GetState(arg1:string):Promise; export function GetWindow():Promise; -export function GetWizardState():Promise; - -export function GoToHistory(arg1:base.Address):Promise; +export function GoToAddress(arg1:base.Address):Promise; export function HeaderToggle(arg1:menu.CallbackData):Promise; @@ -90,13 +85,11 @@ export function IndexPage(arg1:number,arg2:number):Promise export function IndexesView(arg1:menu.CallbackData):Promise; -export function IsConfigured():Promise; - export function IsShowing(arg1:string):Promise; export function LoadName(arg1:string):Promise; -export function Logger(arg1:string):Promise; +export function Logger(arg1:Array):Promise; export function ManifestPage(arg1:number,arg2:number):Promise; @@ -110,8 +103,6 @@ export function ModifyMonitors(arg1:app.ModifyData):Promise; export function ModifyName(arg1:app.ModifyData):Promise; -export function ModifyProject(arg1:app.ModifyData):Promise; - export function MonitorPage(arg1:number,arg2:number):Promise; export function MonitorsView(arg1:menu.CallbackData):Promise; @@ -128,14 +119,14 @@ export function ProjectView(arg1:menu.CallbackData):Promise; export function Refresh():Promise; -export function RegisterCtx(arg1:base.Address):Promise; - -export function Reload(arg1:base.Address):Promise; +export function Reload():Promise; export function SaveName(arg1:editors.Name):Promise; export function SessionPage(arg1:number,arg2:number):Promise; +export function SessionView(arg1:menu.CallbackData):Promise; + export function SetChain(arg1:string,arg2:base.Address):Promise; export function SetEnv(arg1:string,arg2:string):Promise; @@ -166,6 +157,4 @@ export function SystemQuit(arg1:menu.CallbackData):Promise; export function ToggleDaemon(arg1:string):Promise; -export function UnusedFunc(arg1:app.ModifyData):Promise; - export function WizardView(arg1:menu.CallbackData):Promise; diff --git a/frontend/wailsjs/go/app/App.js b/frontend/wailsjs/go/app/App.js index c9630dcb..fbc5683d 100755 --- a/frontend/wailsjs/go/app/App.js +++ b/frontend/wailsjs/go/app/App.js @@ -26,12 +26,8 @@ export function CancelAllContexts() { return window['go']['app']['App']['CancelAllContexts'](); } -export function CancelContext(arg1) { - return window['go']['app']['App']['CancelContext'](arg1); -} - -export function ConfigPage(arg1, arg2) { - return window['go']['app']['App']['ConfigPage'](arg1, arg2); +export function ConfigView(arg1) { + return window['go']['app']['App']['ConfigView'](arg1); } export function ConvertToAddress(arg1) { @@ -42,6 +38,10 @@ export function DaemonsView(arg1) { return window['go']['app']['App']['DaemonsView'](arg1); } +export function DeleteAddress(arg1) { + return window['go']['app']['App']['DeleteAddress'](arg1); +} + export function ExportAddress(arg1) { return window['go']['app']['App']['ExportAddress'](arg1); } @@ -66,18 +66,14 @@ export function FooterToggle(arg1) { return window['go']['app']['App']['FooterToggle'](arg1); } -export function GetAddress() { - return window['go']['app']['App']['GetAddress'](); +export function GetAppInfo() { + return window['go']['app']['App']['GetAppInfo'](); } export function GetAppTitle() { return window['go']['app']['App']['GetAppTitle'](); } -export function GetChain() { - return window['go']['app']['App']['GetChain'](); -} - export function GetChainInfo(arg1) { return window['go']['app']['App']['GetChainInfo'](arg1); } @@ -110,14 +106,14 @@ export function GetMenus() { return window['go']['app']['App']['GetMenus'](); } -export function GetMeta() { - return window['go']['app']['App']['GetMeta'](); -} - export function GetRoute() { return window['go']['app']['App']['GetRoute'](); } +export function GetSelected() { + return window['go']['app']['App']['GetSelected'](); +} + export function GetSession() { return window['go']['app']['App']['GetSession'](); } @@ -130,12 +126,8 @@ export function GetWindow() { return window['go']['app']['App']['GetWindow'](); } -export function GetWizardState() { - return window['go']['app']['App']['GetWizardState'](); -} - -export function GoToHistory(arg1) { - return window['go']['app']['App']['GoToHistory'](arg1); +export function GoToAddress(arg1) { + return window['go']['app']['App']['GoToAddress'](arg1); } export function HeaderToggle(arg1) { @@ -162,10 +154,6 @@ export function IndexesView(arg1) { return window['go']['app']['App']['IndexesView'](arg1); } -export function IsConfigured() { - return window['go']['app']['App']['IsConfigured'](); -} - export function IsShowing(arg1) { return window['go']['app']['App']['IsShowing'](arg1); } @@ -202,10 +190,6 @@ export function ModifyName(arg1) { return window['go']['app']['App']['ModifyName'](arg1); } -export function ModifyProject(arg1) { - return window['go']['app']['App']['ModifyProject'](arg1); -} - export function MonitorPage(arg1, arg2) { return window['go']['app']['App']['MonitorPage'](arg1, arg2); } @@ -238,12 +222,8 @@ export function Refresh() { return window['go']['app']['App']['Refresh'](); } -export function RegisterCtx(arg1) { - return window['go']['app']['App']['RegisterCtx'](arg1); -} - -export function Reload(arg1) { - return window['go']['app']['App']['Reload'](arg1); +export function Reload() { + return window['go']['app']['App']['Reload'](); } export function SaveName(arg1) { @@ -254,6 +234,10 @@ export function SessionPage(arg1, arg2) { return window['go']['app']['App']['SessionPage'](arg1, arg2); } +export function SessionView(arg1) { + return window['go']['app']['App']['SessionView'](arg1); +} + export function SetChain(arg1, arg2) { return window['go']['app']['App']['SetChain'](arg1, arg2); } @@ -314,10 +298,6 @@ export function ToggleDaemon(arg1) { return window['go']['app']['App']['ToggleDaemon'](arg1); } -export function UnusedFunc(arg1) { - return window['go']['app']['App']['UnusedFunc'](arg1); -} - export function WizardView(arg1) { return window['go']['app']['App']['WizardView'](arg1); } diff --git a/frontend/wailsjs/go/configtypes/Config.d.ts b/frontend/wailsjs/go/configtypes/Config.d.ts index 0a74528f..205f0888 100755 --- a/frontend/wailsjs/go/configtypes/Config.d.ts +++ b/frontend/wailsjs/go/configtypes/Config.d.ts @@ -1,7 +1,10 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +import {configtypes} from '../models'; import {version} from '../models'; +export function ShallowCopy():Promise; + export function String():Promise; export function WriteFile(arg1:string,arg2:version.Version):Promise; diff --git a/frontend/wailsjs/go/configtypes/Config.js b/frontend/wailsjs/go/configtypes/Config.js index a21a21cd..64598f4c 100755 --- a/frontend/wailsjs/go/configtypes/Config.js +++ b/frontend/wailsjs/go/configtypes/Config.js @@ -2,6 +2,10 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +export function ShallowCopy() { + return window['go']['configtypes']['Config']['ShallowCopy'](); +} + export function String() { return window['go']['configtypes']['Config']['String'](); } diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts index f777d3e3..6a43c491 100755 --- a/frontend/wailsjs/go/models.ts +++ b/frontend/wailsjs/go/models.ts @@ -1,5 +1,45 @@ export namespace app { + export class AppInfo { + chain: string; + filename: string; + dirty: boolean; + meta: types.MetaData; + state: types.WizState; + isConfigured: boolean; + + static createFrom(source: any = {}) { + return new AppInfo(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.chain = source["chain"]; + this.filename = source["filename"]; + this.dirty = source["dirty"]; + this.meta = this.convertValues(source["meta"], types.MetaData); + this.state = source["state"]; + this.isConfigured = source["isConfigured"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } export class ModifyData { operation: string; address: base.Address; @@ -380,19 +420,17 @@ export namespace editors { export namespace messages { export enum Message { + PROGRESS = "Progress", COMPLETED = "Completed", CANCELLED = "Cancelled", ERROR = "Error", WARNING = "Warn", INFO = "Info", SWITCHTAB = "SwitchTab", - PROGRESS = "Progress", + TOGGLELAYOUT = "ToggleLayout", + TOGGLEACCORDION = "ToggleAccordion", DAEMON = "Daemon", - DOCUMENT = "Document", NAVIGATE = "Navigate", - RELOAD = "Reload", - TOGGLELAYOUT = "ToggleLayout", - TOGGLEHEADER = "ToggleHeader", WIZARD = "Wizard", } export class MessageMsg { @@ -440,23 +478,6 @@ export namespace messages { } -export namespace output { - - export class RenderCtx { - - - static createFrom(source: any = {}) { - return new RenderCtx(source); - } - - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - - } - } - -} - export namespace sdk { export class SortSpec { @@ -484,6 +505,7 @@ export namespace types { RPCOKAY = "rpcOkay", BLOOMSOKAY = "bloomsOkay", INDEXOKAY = "indexOkay", + ERROR = "error", OKAY = "okay", } export enum WizStep { @@ -865,17 +887,15 @@ export namespace types { } export class ConfigContainer { nChains: number; - items: configtypes.Config[]; - nItems: number; - chain: string; - // Go type: time - lastUpdate: any; version: configtypes.VersionGroup; settings: configtypes.SettingsGroup; keys: {[key: string]: configtypes.KeyGroup}; pinning: configtypes.PinningGroup; unchained: configtypes.UnchainedGroup; chains: {[key: string]: configtypes.ChainGroup}; + chain: string; + // Go type: time + lastUpdate: any; static createFrom(source: any = {}) { return new ConfigContainer(source); @@ -884,16 +904,14 @@ export namespace types { constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); this.nChains = source["nChains"]; - this.items = this.convertValues(source["items"], configtypes.Config); - this.nItems = source["nItems"]; - this.chain = source["chain"]; - this.lastUpdate = this.convertValues(source["lastUpdate"], null); this.version = this.convertValues(source["version"], configtypes.VersionGroup); this.settings = this.convertValues(source["settings"], configtypes.SettingsGroup); this.keys = this.convertValues(source["keys"], configtypes.KeyGroup, true); this.pinning = this.convertValues(source["pinning"], configtypes.PinningGroup); this.unchained = this.convertValues(source["unchained"], configtypes.UnchainedGroup); this.chains = this.convertValues(source["chains"], configtypes.ChainGroup, true); + this.chain = source["chain"]; + this.lastUpdate = this.convertValues(source["lastUpdate"], null); } convertValues(a: any, classs: any, asMap: boolean = false): any { @@ -1765,7 +1783,6 @@ export namespace types { chain: string; // Go type: time lastUpdate: any; - namesMap: {[key: string]: Name}; static createFrom(source: any = {}) { return new NameContainer(source); @@ -1786,7 +1803,6 @@ export namespace types { this.nItems = source["nItems"]; this.chain = source["chain"]; this.lastUpdate = this.convertValues(source["lastUpdate"], null); - this.namesMap = this.convertValues(source["namesMap"], Name, true); } convertValues(a: any, classs: any, asMap: boolean = false): any { @@ -1808,20 +1824,37 @@ export namespace types { } } - export class Toggles { - layout: Layout; - headers: Headers; - daemons: Daemons; + export class ProjectContainer { + historySize: number; + nAbis: number; + nCaches: number; + nIndexes: number; + nManifests: number; + nMonitors: number; + nNames: number; + items: HistoryContainer[]; + nItems: number; + chain: string; + // Go type: time + lastUpdate: any; static createFrom(source: any = {}) { - return new Toggles(source); + return new ProjectContainer(source); } constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); - this.layout = this.convertValues(source["layout"], Layout); - this.headers = this.convertValues(source["headers"], Headers); - this.daemons = this.convertValues(source["daemons"], Daemons); + this.historySize = source["historySize"]; + this.nAbis = source["nAbis"]; + this.nCaches = source["nCaches"]; + this.nIndexes = source["nIndexes"]; + this.nManifests = source["nManifests"]; + this.nMonitors = source["nMonitors"]; + this.nNames = source["nNames"]; + this.items = this.convertValues(source["items"], HistoryContainer); + this.nItems = source["nItems"]; + this.chain = source["chain"]; + this.lastUpdate = this.convertValues(source["lastUpdate"], null); } convertValues(a: any, classs: any, asMap: boolean = false): any { @@ -1842,62 +1875,64 @@ export namespace types { return a; } } - export class Wizard { - state: WizState; + export class ProjectFile { + version: string; + dateSaved: string; + selected: base.Address; + addresses: base.Address[]; static createFrom(source: any = {}) { - return new Wizard(source); + return new ProjectFile(source); } constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); - this.state = source["state"]; + this.version = source["version"]; + this.dateSaved = source["dateSaved"]; + this.selected = this.convertValues(source["selected"], base.Address); + this.addresses = this.convertValues(source["addresses"], base.Address); } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } } - export class Window { - x: number; - y: number; - width: number; - height: number; - title: string; - static createFrom(source: any = {}) { - return new Window(source); - } - constructor(source: any = {}) { - if ('string' === typeof source) source = JSON.parse(source); - this.x = source["x"]; - this.y = source["y"]; - this.width = source["width"]; - this.height = source["height"]; - this.title = source["title"]; - } - } - export class Session { - lastChain: string; - lastFile: string; - lastFolder: string; - lastRoute: string; - lastSub: {[key: string]: string}; - window: Window; - wizard: Wizard; - toggles: Toggles; + export class Rewards { + // Go type: base + block: any; + // Go type: base + nephew: any; + // Go type: base + txFee: any; + // Go type: base + uncle: any; static createFrom(source: any = {}) { - return new Session(source); + return new Rewards(source); } constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); - this.lastChain = source["lastChain"]; - this.lastFile = source["lastFile"]; - this.lastFolder = source["lastFolder"]; - this.lastRoute = source["lastRoute"]; - this.lastSub = source["lastSub"]; - this.window = this.convertValues(source["window"], Window); - this.wizard = this.convertValues(source["wizard"], Wizard); - this.toggles = this.convertValues(source["toggles"], Toggles); + this.block = this.convertValues(source["block"], null); + this.nephew = this.convertValues(source["nephew"], null); + this.txFee = this.convertValues(source["txFee"], null); + this.uncle = this.convertValues(source["uncle"], null); } convertValues(a: any, classs: any, asMap: boolean = false): any { @@ -1918,47 +1953,20 @@ export namespace types { return a; } } - export class ProjectContainer { - nMonitors: number; - nNames: number; - nAbis: number; - nIndexes: number; - nManifests: number; - nCaches: number; - historySize: number; - dirty: boolean; - filename: string; - nItems: number; - items: HistoryContainer[]; - session: Session; - // Go type: HistoryMap - historyMap?: any; - // Go type: sync - balanceMap?: any; - // Go type: sync - ensMap?: any; + export class Toggles { + layout: Layout; + headers: Headers; + daemons: Daemons; static createFrom(source: any = {}) { - return new ProjectContainer(source); + return new Toggles(source); } constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); - this.nMonitors = source["nMonitors"]; - this.nNames = source["nNames"]; - this.nAbis = source["nAbis"]; - this.nIndexes = source["nIndexes"]; - this.nManifests = source["nManifests"]; - this.nCaches = source["nCaches"]; - this.historySize = source["historySize"]; - this.dirty = source["dirty"]; - this.filename = source["filename"]; - this.nItems = source["nItems"]; - this.items = this.convertValues(source["items"], HistoryContainer); - this.session = this.convertValues(source["session"], Session); - this.historyMap = this.convertValues(source["historyMap"], null); - this.balanceMap = this.convertValues(source["balanceMap"], null); - this.ensMap = this.convertValues(source["ensMap"], null); + this.layout = this.convertValues(source["layout"], Layout); + this.headers = this.convertValues(source["headers"], Headers); + this.daemons = this.convertValues(source["daemons"], Daemons); } convertValues(a: any, classs: any, asMap: boolean = false): any { @@ -1979,28 +1987,62 @@ export namespace types { return a; } } + export class Wizard { + state: WizState; + static createFrom(source: any = {}) { + return new Wizard(source); + } - export class Rewards { - // Go type: base - block: any; - // Go type: base - nephew: any; - // Go type: base - txFee: any; - // Go type: base - uncle: any; + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.state = source["state"]; + } + } + export class Window { + x: number; + y: number; + width: number; + height: number; + title: string; static createFrom(source: any = {}) { - return new Rewards(source); + return new Window(source); } constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); - this.block = this.convertValues(source["block"], null); - this.nephew = this.convertValues(source["nephew"], null); - this.txFee = this.convertValues(source["txFee"], null); - this.uncle = this.convertValues(source["uncle"], null); + this.x = source["x"]; + this.y = source["y"]; + this.width = source["width"]; + this.height = source["height"]; + this.title = source["title"]; + } + } + export class Session { + lastChain: string; + lastFile: string; + lastFolder: string; + lastRoute: string; + lastSub: {[key: string]: string}; + window: Window; + wizard: Wizard; + toggles: Toggles; + + static createFrom(source: any = {}) { + return new Session(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.lastChain = source["lastChain"]; + this.lastFile = source["lastFile"]; + this.lastFolder = source["lastFolder"]; + this.lastRoute = source["lastRoute"]; + this.lastSub = source["lastSub"]; + this.window = this.convertValues(source["window"], Window); + this.wizard = this.convertValues(source["wizard"], Wizard); + this.toggles = this.convertValues(source["toggles"], Toggles); } convertValues(a: any, classs: any, asMap: boolean = false): any { @@ -2021,7 +2063,6 @@ export namespace types { return a; } } - export class SessionContainer { lastChain: string; lastFile: string; diff --git a/frontend/wailsjs/go/types/AbiContainer.d.ts b/frontend/wailsjs/go/types/AbiContainer.d.ts index 9c5f9c0c..68687ea0 100755 --- a/frontend/wailsjs/go/types/AbiContainer.d.ts +++ b/frontend/wailsjs/go/types/AbiContainer.d.ts @@ -4,6 +4,8 @@ import {types} from '../models'; export function FinishUnmarshal():Promise; +export function ForEveryAbi(arg1:types.EveryAbiFn,arg2:any):Promise; + export function Model(arg1:string,arg2:string,arg3:boolean,arg4:{[key: string]: any}):Promise; export function NeedsUpdate(arg1:boolean):Promise; diff --git a/frontend/wailsjs/go/types/AbiContainer.js b/frontend/wailsjs/go/types/AbiContainer.js index c6044a63..ada4a359 100755 --- a/frontend/wailsjs/go/types/AbiContainer.js +++ b/frontend/wailsjs/go/types/AbiContainer.js @@ -6,6 +6,10 @@ export function FinishUnmarshal() { return window['go']['types']['AbiContainer']['FinishUnmarshal'](); } +export function ForEveryAbi(arg1, arg2) { + return window['go']['types']['AbiContainer']['ForEveryAbi'](arg1, arg2); +} + export function Model(arg1, arg2, arg3, arg4) { return window['go']['types']['AbiContainer']['Model'](arg1, arg2, arg3, arg4); } diff --git a/frontend/wailsjs/go/types/IndexContainer.d.ts b/frontend/wailsjs/go/types/IndexContainer.d.ts index 9c5f9c0c..36983b0d 100755 --- a/frontend/wailsjs/go/types/IndexContainer.d.ts +++ b/frontend/wailsjs/go/types/IndexContainer.d.ts @@ -4,6 +4,8 @@ import {types} from '../models'; export function FinishUnmarshal():Promise; +export function ForEveryChunkStats(arg1:types.EveryChunkStatsFn,arg2:any):Promise; + export function Model(arg1:string,arg2:string,arg3:boolean,arg4:{[key: string]: any}):Promise; export function NeedsUpdate(arg1:boolean):Promise; diff --git a/frontend/wailsjs/go/types/IndexContainer.js b/frontend/wailsjs/go/types/IndexContainer.js index d9acff7e..30e5c6d3 100755 --- a/frontend/wailsjs/go/types/IndexContainer.js +++ b/frontend/wailsjs/go/types/IndexContainer.js @@ -6,6 +6,10 @@ export function FinishUnmarshal() { return window['go']['types']['IndexContainer']['FinishUnmarshal'](); } +export function ForEveryChunkStats(arg1, arg2) { + return window['go']['types']['IndexContainer']['ForEveryChunkStats'](arg1, arg2); +} + export function Model(arg1, arg2, arg3, arg4) { return window['go']['types']['IndexContainer']['Model'](arg1, arg2, arg3, arg4); } diff --git a/frontend/wailsjs/go/types/ManifestContainer.d.ts b/frontend/wailsjs/go/types/ManifestContainer.d.ts index c1fe9f03..39e84388 100755 --- a/frontend/wailsjs/go/types/ManifestContainer.d.ts +++ b/frontend/wailsjs/go/types/ManifestContainer.d.ts @@ -2,6 +2,8 @@ // This file is automatically generated. DO NOT EDIT import {types} from '../models'; +export function ForEveryChunkRecord(arg1:types.EveryChunkRecordFn,arg2:any):Promise; + export function NeedsUpdate(arg1:boolean):Promise; export function ShallowCopy():Promise; diff --git a/frontend/wailsjs/go/types/ManifestContainer.js b/frontend/wailsjs/go/types/ManifestContainer.js index b721f3ee..9f16f72c 100755 --- a/frontend/wailsjs/go/types/ManifestContainer.js +++ b/frontend/wailsjs/go/types/ManifestContainer.js @@ -2,6 +2,10 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +export function ForEveryChunkRecord(arg1, arg2) { + return window['go']['types']['ManifestContainer']['ForEveryChunkRecord'](arg1, arg2); +} + export function NeedsUpdate(arg1) { return window['go']['types']['ManifestContainer']['NeedsUpdate'](arg1); } diff --git a/frontend/wailsjs/go/types/MonitorContainer.d.ts b/frontend/wailsjs/go/types/MonitorContainer.d.ts index c1fe9f03..9676aca5 100755 --- a/frontend/wailsjs/go/types/MonitorContainer.d.ts +++ b/frontend/wailsjs/go/types/MonitorContainer.d.ts @@ -2,6 +2,8 @@ // This file is automatically generated. DO NOT EDIT import {types} from '../models'; +export function ForEveryMonitor(arg1:types.EveryMonitorFn,arg2:any):Promise; + export function NeedsUpdate(arg1:boolean):Promise; export function ShallowCopy():Promise; diff --git a/frontend/wailsjs/go/types/MonitorContainer.js b/frontend/wailsjs/go/types/MonitorContainer.js index fcb913c6..26c8f921 100755 --- a/frontend/wailsjs/go/types/MonitorContainer.js +++ b/frontend/wailsjs/go/types/MonitorContainer.js @@ -2,6 +2,10 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +export function ForEveryMonitor(arg1, arg2) { + return window['go']['types']['MonitorContainer']['ForEveryMonitor'](arg1, arg2); +} + export function NeedsUpdate(arg1) { return window['go']['types']['MonitorContainer']['NeedsUpdate'](arg1); } diff --git a/frontend/wailsjs/go/types/NameContainer.d.ts b/frontend/wailsjs/go/types/NameContainer.d.ts index c1fe9f03..0acc0f37 100755 --- a/frontend/wailsjs/go/types/NameContainer.d.ts +++ b/frontend/wailsjs/go/types/NameContainer.d.ts @@ -2,6 +2,8 @@ // This file is automatically generated. DO NOT EDIT import {types} from '../models'; +export function ForEveryName(arg1:types.EveryNameFn,arg2:any):Promise; + export function NeedsUpdate(arg1:boolean):Promise; export function ShallowCopy():Promise; diff --git a/frontend/wailsjs/go/types/NameContainer.js b/frontend/wailsjs/go/types/NameContainer.js index ed66ea64..2f83ff70 100755 --- a/frontend/wailsjs/go/types/NameContainer.js +++ b/frontend/wailsjs/go/types/NameContainer.js @@ -2,6 +2,10 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +export function ForEveryName(arg1, arg2) { + return window['go']['types']['NameContainer']['ForEveryName'](arg1, arg2); +} + export function NeedsUpdate(arg1) { return window['go']['types']['NameContainer']['NeedsUpdate'](arg1); } diff --git a/frontend/wailsjs/go/types/ProjectContainer.d.ts b/frontend/wailsjs/go/types/ProjectContainer.d.ts index 168ae758..44803e8b 100755 --- a/frontend/wailsjs/go/types/ProjectContainer.d.ts +++ b/frontend/wailsjs/go/types/ProjectContainer.d.ts @@ -1,12 +1,17 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +import {base} from '../models'; import {types} from '../models'; -export function Load():Promise; +export function Clean(arg1:base.Address):Promise|base.Address>; + +export function ForEveryHistory(arg1:types.EveryAddressFn,arg2:any):Promise; + +export function Load(arg1:string):Promise; export function NeedsUpdate(arg1:boolean):Promise; -export function Save():Promise; +export function Save(arg1:string,arg2:base.Address):Promise; export function ShallowCopy():Promise; diff --git a/frontend/wailsjs/go/types/ProjectContainer.js b/frontend/wailsjs/go/types/ProjectContainer.js index 181919d2..3e634c7b 100755 --- a/frontend/wailsjs/go/types/ProjectContainer.js +++ b/frontend/wailsjs/go/types/ProjectContainer.js @@ -2,16 +2,24 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -export function Load() { - return window['go']['types']['ProjectContainer']['Load'](); +export function Clean(arg1) { + return window['go']['types']['ProjectContainer']['Clean'](arg1); +} + +export function ForEveryHistory(arg1, arg2) { + return window['go']['types']['ProjectContainer']['ForEveryHistory'](arg1, arg2); +} + +export function Load(arg1) { + return window['go']['types']['ProjectContainer']['Load'](arg1); } export function NeedsUpdate(arg1) { return window['go']['types']['ProjectContainer']['NeedsUpdate'](arg1); } -export function Save() { - return window['go']['types']['ProjectContainer']['Save'](); +export function Save(arg1, arg2) { + return window['go']['types']['ProjectContainer']['Save'](arg1, arg2); } export function ShallowCopy() { diff --git a/main.go b/main.go index 0800d755..72ce5cd1 100644 --- a/main.go +++ b/main.go @@ -34,7 +34,7 @@ func main() { Menu: a.GetMenus(), Bind: []interface{}{ a, - &messages.MessageMsg{}, + &app.AppInfo{}, &types.ProjectContainer{}, &types.NameContainer{}, &types.AbiContainer{}, @@ -44,11 +44,12 @@ func main() { // &types.DaemonContainer{}, &types.SettingsGroup{}, &types.StatusContainer{}, + &messages.MessageMsg{}, &coreTypes.Wizard{}, - &editors.Name{}, - &daemons.Daemon{}, &coreTypes.Transaction{}, &configTypes.Config{}, + &editors.Name{}, + &daemons.Daemon{}, }, EnumBind: []interface{}{ daemons.AllStates, diff --git a/pkg/messages/messages.go b/pkg/messages/messages.go index cf185d58..385ad9c5 100644 --- a/pkg/messages/messages.go +++ b/pkg/messages/messages.go @@ -11,39 +11,39 @@ import ( type Message string const ( - Completed Message = "Completed" - Cancelled Message = "Cancelled" - Error Message = "Error" - Warn Message = "Warn" - Info Message = "Info" - SwitchTab Message = "SwitchTab" - Progress Message = "Progress" - Daemon Message = "Daemon" - Document Message = "Document" - Navigate Message = "Navigate" - Reload Message = "Reload" - ToggleLayout Message = "ToggleLayout" - ToggleHeader Message = "ToggleHeader" - Wizard Message = "Wizard" + Progress Message = "Progress" + Completed Message = "Completed" + Cancelled Message = "Cancelled" + Error Message = "Error" + Warn Message = "Warn" + Info Message = "Info" + SwitchTab Message = "SwitchTab" + ToggleLayout Message = "ToggleLayout" + ToggleAccordion Message = "ToggleAccordion" + Daemon Message = "Daemon" + Navigate Message = "Navigate" + Wizard Message = "Wizard" ) var AllMessages = []struct { Value Message `json:"value"` TSName string `json:"tsname"` }{ + {Progress, "PROGRESS"}, {Completed, "COMPLETED"}, {Cancelled, "CANCELLED"}, + {Error, "ERROR"}, {Warn, "WARNING"}, {Info, "INFO"}, + {SwitchTab, "SWITCHTAB"}, - {Progress, "PROGRESS"}, + {ToggleLayout, "TOGGLELAYOUT"}, + {ToggleAccordion, "TOGGLEACCORDION"}, + {Daemon, "DAEMON"}, - {Document, "DOCUMENT"}, {Navigate, "NAVIGATE"}, - {Reload, "RELOAD"}, - {ToggleLayout, "TOGGLELAYOUT"}, - {ToggleHeader, "TOGGLEHEADER"}, + {Wizard, "WIZARD"}, } diff --git a/pkg/types/types_containerabi.go b/pkg/types/types_containerabi.go index df363d7d..f4c660ea 100644 --- a/pkg/types/types_containerabi.go +++ b/pkg/types/types_containerabi.go @@ -11,6 +11,7 @@ import ( coreConfig "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" sdk "github.com/TrueBlocks/trueblocks-sdk/v3" ) @@ -55,6 +56,7 @@ func (s *AbiContainer) String() string { func (s *AbiContainer) NeedsUpdate(force bool) bool { latest, reload := s.getAbiReload() if force || reload { + logger.InfoG("AbiContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } @@ -106,7 +108,7 @@ func (s *AbiContainer) getAbiReload() (ret time.Time, reload bool) { type EveryAbiFn func(item *coreTypes.Abi, data any) bool func (s *AbiContainer) ForEveryAbi(process EveryAbiFn, data any) bool { - for i := 0 ; i < len(s.Items) ; i++ { + for i := 0; i < len(s.Items); i++ { if !process(&s.Items[i], data) { return false } diff --git a/pkg/types/types_containerconfig.go b/pkg/types/types_containerconfig.go index abc669c1..94bef8a4 100644 --- a/pkg/types/types_containerconfig.go +++ b/pkg/types/types_containerconfig.go @@ -5,11 +5,15 @@ package types // EXISTING_CODE import ( "encoding/json" + "errors" + "fmt" + "path/filepath" "time" coreConfig "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" configTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/configtypes" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" ) // EXISTING_CODE @@ -42,6 +46,7 @@ func (s *ConfigContainer) String() string { func (s *ConfigContainer) NeedsUpdate(force bool) bool { latest, reload := s.getConfigReload() if force || reload { + logger.InfoG("ConfigContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } @@ -83,4 +88,36 @@ func (s *ConfigContainer) getConfigReload() (ret time.Time, reload bool) { } // EXISTING_CODE +var ErrNoConfigFolder = errors.New("core config folder not found") +var ErrNoConfigFile = errors.New("trueBlocks.toml file not found") + +func (s *ConfigContainer) Load() error { + path := coreConfig.PathToRootConfig() + if !file.FolderExists(path) { + return ErrNoConfigFolder + } + + fn := filepath.Join(path, "trueBlocks.toml") + if !file.FileExists(path) { + return ErrNoConfigFolder + } + + if err := coreConfig.ReadToml(fn, &s.Config); err != nil { + return fmt.Errorf("%w: %v", ErrNoConfigFile, err) + } + + return nil +} + +var ErrChainNotConfigured = errors.New("chain not configured") + +func (s *ConfigContainer) IsValidChain(chain string) (string, error) { + for _, ch := range s.Chains { + if ch.Chain == chain { + return chain, nil + } + } + return "mainnet", fmt.Errorf("%w: %s", ErrChainNotConfigured, chain) +} + // EXISTING_CODE diff --git a/pkg/types/types_containerdaemon.go b/pkg/types/types_containerdaemon.go index eaae66a7..620e5f45 100644 --- a/pkg/types/types_containerdaemon.go +++ b/pkg/types/types_containerdaemon.go @@ -8,6 +8,7 @@ import ( "time" "github.com/TrueBlocks/trueblocks-browse/pkg/daemons" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" ) // EXISTING_CODE @@ -37,6 +38,7 @@ func (s *DaemonContainer) String() string { func (s *DaemonContainer) NeedsUpdate(force bool) bool { latest, reload := s.getDaemonReload() if force || reload { + logger.InfoG("DaemonContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } diff --git a/pkg/types/types_containerhistory.go b/pkg/types/types_containerhistory.go index b06587fc..57601607 100644 --- a/pkg/types/types_containerhistory.go +++ b/pkg/types/types_containerhistory.go @@ -5,13 +5,13 @@ package types // EXISTING_CODE import ( "encoding/json" - "path/filepath" "time" "unsafe" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" - coreConfig "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" + coreMonitors "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/monitor" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" ) @@ -41,7 +41,7 @@ func NewHistoryContainer(chain string, itemsIn []coreTypes.Transaction, address ret.LastUpdate, _ = ret.getHistoryReload() // EXISTING_CODE ret.Address = address - ret.LastUpdate, _ = ret.getHistoryReload() // it requires address + ret.LastUpdate, _ = ret.getHistoryReload() // DO NOT REMOVE (needs address) // EXISTING_CODE return ret } @@ -54,6 +54,7 @@ func (s *HistoryContainer) String() string { func (s *HistoryContainer) NeedsUpdate(force bool) bool { latest, reload := s.getHistoryReload() if force || reload { + logger.InfoB("HistoryContainer", s.Address.Hex(), s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } @@ -96,10 +97,14 @@ func (s *HistoryContainer) Summarize() { func (s *HistoryContainer) getHistoryReload() (ret time.Time, reload bool) { // EXISTING_CODE - cache := coreConfig.PathToCache(s.Chain) - path := filepath.Join(cache, "monitors", s.Address.Hex()+".mon.bin") - ret = file.MustGetLatestFileTime(path) - reload = ret != s.LastUpdate + if s.Address == base.ZeroAddr { + logger.Error("getHistoryReload called with zero address") + return + } + fn := coreMonitors.PathToMonitorFile(s.Chain, s.Address) + ret, _ = file.GetModTime(fn) + reload = ret.After(s.LastUpdate) + logger.InfoY("getHistoryReload", s.Address.Hex(), s.LastUpdate.String(), ret.String(), reload) // EXISTING_CODE return } @@ -107,7 +112,7 @@ func (s *HistoryContainer) getHistoryReload() (ret time.Time, reload bool) { type EveryTransactionFn func(item *coreTypes.Transaction, data any) bool func (s *HistoryContainer) ForEveryTransaction(process EveryTransactionFn, data any) bool { - for i := 0 ; i < len(s.Items) ; i++ { + for i := 0; i < len(s.Items); i++ { if !process(&s.Items[i], data) { return false } diff --git a/pkg/types/types_containerindex.go b/pkg/types/types_containerindex.go index d358f31d..a768ecfd 100644 --- a/pkg/types/types_containerindex.go +++ b/pkg/types/types_containerindex.go @@ -9,6 +9,7 @@ import ( coreConfig "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" sdk "github.com/TrueBlocks/trueblocks-sdk/v3" ) @@ -50,6 +51,7 @@ func (s *IndexContainer) String() string { func (s *IndexContainer) NeedsUpdate(force bool) bool { latest, reload := s.getIndexReload() if force || reload { + logger.InfoG("IndexContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } @@ -102,7 +104,7 @@ func (s *IndexContainer) getIndexReload() (ret time.Time, reload bool) { type EveryChunkStatsFn func(item *coreTypes.ChunkStats, data any) bool func (s *IndexContainer) ForEveryChunkStats(process EveryChunkStatsFn, data any) bool { - for i := 0 ; i < len(s.Items) ; i++ { + for i := 0; i < len(s.Items); i++ { if !process(&s.Items[i], data) { return false } diff --git a/pkg/types/types_containermanifest.go b/pkg/types/types_containermanifest.go index 620be42f..047db235 100644 --- a/pkg/types/types_containermanifest.go +++ b/pkg/types/types_containermanifest.go @@ -9,6 +9,7 @@ import ( coreConfig "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" sdk "github.com/TrueBlocks/trueblocks-sdk/v3" ) @@ -58,6 +59,7 @@ func (s *ManifestContainer) String() string { func (s *ManifestContainer) NeedsUpdate(force bool) bool { latest, reload := s.getManifestReload() if force || reload { + logger.InfoG("ManifestContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } @@ -103,7 +105,7 @@ func (s *ManifestContainer) getManifestReload() (ret time.Time, reload bool) { type EveryChunkRecordFn func(item *coreTypes.ChunkRecord, data any) bool func (s *ManifestContainer) ForEveryChunkRecord(process EveryChunkRecordFn, data any) bool { - for i := 0 ; i < len(s.Items) ; i++ { + for i := 0; i < len(s.Items); i++ { if !process(&s.Items[i], data) { return false } diff --git a/pkg/types/types_containermonitor.go b/pkg/types/types_containermonitor.go index 332b2845..dec56976 100644 --- a/pkg/types/types_containermonitor.go +++ b/pkg/types/types_containermonitor.go @@ -10,6 +10,7 @@ import ( coreConfig "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" ) @@ -82,6 +83,7 @@ func (s *MonitorContainer) String() string { func (s *MonitorContainer) NeedsUpdate(force bool) bool { latest, reload := s.getMonitorReload() if force || reload { + logger.InfoG("MonitorContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } @@ -137,8 +139,8 @@ func (s *MonitorContainer) getMonitorReload() (ret time.Time, reload bool) { type EveryMonitorFn func(item *coreTypes.Monitor, data any) bool func (s *MonitorContainer) ForEveryMonitor(process EveryMonitorFn, data any) bool { - for i := 0 ; i < len(s.Items) ; i++ { - if !process(&item[i], data) { + for i := 0; i < len(s.Items); i++ { + if !process(&s.Items[i], data) { return false } } diff --git a/pkg/types/types_containername.go b/pkg/types/types_containername.go index f3fc357d..e26113c0 100644 --- a/pkg/types/types_containername.go +++ b/pkg/types/types_containername.go @@ -9,9 +9,9 @@ import ( "sort" "time" - "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" coreConfig "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/names" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" ) @@ -33,7 +33,6 @@ type NameContainer struct { Chain string `json:"chain"` LastUpdate time.Time `json:"lastUpdate"` // EXISTING_CODE - NamesMap map[base.Address]coreTypes.Name `json:"namesMap"` // EXISTING_CODE } @@ -45,10 +44,6 @@ func NewNameContainer(chain string, itemsIn []coreTypes.Name) NameContainer { } ret.LastUpdate, _ = ret.getNameReload() // EXISTING_CODE - ret.NamesMap = make(map[base.Address]coreTypes.Name) - for _, name := range ret.Items { - ret.NamesMap[name.Address] = name - } sort.Slice(ret.Items, func(i, j int) bool { return compare(ret.Items[i], ret.Items[j]) }) @@ -64,6 +59,7 @@ func (s *NameContainer) String() string { func (s *NameContainer) NeedsUpdate(force bool) bool { latest, reload := s.getNameReload() if force || reload { + logger.InfoG("NameContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } @@ -137,7 +133,7 @@ func (s *NameContainer) getNameReload() (ret time.Time, reload bool) { type EveryNameFn func(item *coreTypes.Name, data any) bool func (s *NameContainer) ForEveryName(process EveryNameFn, data any) bool { - for i := 0 ; i < len(s.Items) ; i++ { + for i := 0; i < len(s.Items); i++ { if !process(&s.Items[i], data) { return false } diff --git a/pkg/types/types_containerproject.go b/pkg/types/types_containerproject.go index fcb24ff7..b1ee9497 100644 --- a/pkg/types/types_containerproject.go +++ b/pkg/types/types_containerproject.go @@ -7,9 +7,9 @@ import ( "encoding/json" "time" - "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" + coreMonitor "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/monitor" ) // EXISTING_CODE @@ -43,13 +43,19 @@ func NewProjectContainer(chain string, itemsIn []HistoryContainer) ProjectContai } func (s *ProjectContainer) String() string { + if s.Items == nil { + s.Items = []HistoryContainer{} + } bytes, _ := json.Marshal(s) return string(bytes) } func (s *ProjectContainer) NeedsUpdate(force bool) bool { + logger.Info() + logger.InfoW("ProjectContainer::NeedsUpdate") latest, reload := s.getProjectReload() if force || reload { + logger.InfoG("ProjectContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } @@ -83,14 +89,16 @@ func (s *ProjectContainer) Summarize() { func (s *ProjectContainer) getProjectReload() (ret time.Time, reload bool) { // EXISTING_CODE _ = s.ForEveryHistory(func(item *HistoryContainer, data any) bool { - if item.NeedsUpdate(false) { - ret = item.LastUpdate - reload = true // we can stop - return false + fn := coreMonitor.PathToMonitorFile(s.Chain, item.Address) + t, _ := file.GetModTime(fn) + if t.After(item.LastUpdate) { + logger.InfoBG("ProjectContainer::getHistoryReload", item.Address.Hex(), s.LastUpdate.String(), ret.String(), reload) + reload = true + ret = t + return false // all we need is one } return true }, nil) - // return ret, needs // EXISTING_CODE return } @@ -107,34 +115,4 @@ func (s *ProjectContainer) ForEveryHistory(process EveryAddressFn, data any) boo } // EXISTING_CODE -type ProjectFile struct { - DateSaved string `json:"dateSaved"` - Selected base.Address `json:"selected"` - Addresses []base.Address `json:"addresses"` -} - -func (p *ProjectFile) String() string { - bytes, _ := json.Marshal(p) - return string(bytes) -} - -func (s *ProjectContainer) Load(fn string) (*ProjectFile, error) { - projectFile := &ProjectFile{} - str := file.AsciiFileToString(fn) - err := json.Unmarshal([]byte(str), projectFile) - return projectFile, err -} - -func (s *ProjectContainer) Save(fn string, selected base.Address) error { - projectFile := ProjectFile{DateSaved: time.Now().String(), Selected: selected} - s.ForEveryHistory(func(history *HistoryContainer, data any) bool { - projectFile.Addresses = append(projectFile.Addresses, history.Address) - return true - }, nil) - logger.Info("ProjectContainer:Save:", projectFile.String()) - bytes, _ := json.MarshalIndent(projectFile, "", " ") - file.StringToAsciiFile(fn, string(bytes)) - return nil -} - // EXISTING_CODE diff --git a/pkg/types/types_containersession.go b/pkg/types/types_containersession.go index 77285615..40f41dd3 100644 --- a/pkg/types/types_containersession.go +++ b/pkg/types/types_containersession.go @@ -8,6 +8,7 @@ import ( "time" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils" ) @@ -25,7 +26,6 @@ func NewSessionContainer(chain string, session *coreTypes.Session) SessionContai ret := SessionContainer{ Session: *session, } - ret.Chain = chain ret.LastUpdate, _ = ret.getSessionReload() // EXISTING_CODE // EXISTING_CODE @@ -40,6 +40,7 @@ func (s *SessionContainer) String() string { func (s *SessionContainer) NeedsUpdate(force bool) bool { latest, reload := s.getSessionReload() if force || reload { + logger.InfoG("SessionContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } diff --git a/pkg/types/types_containerstatus.go b/pkg/types/types_containerstatus.go index 17de296a..f21fa41e 100644 --- a/pkg/types/types_containerstatus.go +++ b/pkg/types/types_containerstatus.go @@ -7,6 +7,7 @@ import ( "encoding/json" "time" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" coreTypes "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" ) @@ -46,6 +47,7 @@ func (s *StatusContainer) String() string { func (s *StatusContainer) NeedsUpdate(force bool) bool { latest, reload := s.getStatusReload() if force || reload { + logger.InfoG("StatusContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } diff --git a/pkg/types/types_containerwizard.go b/pkg/types/types_containerwizard.go index 67b281c3..7f50e096 100644 --- a/pkg/types/types_containerwizard.go +++ b/pkg/types/types_containerwizard.go @@ -6,21 +6,20 @@ package types import ( "encoding/json" "time" + + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" ) // EXISTING_CODE type WizardContainer struct { - Chain string `json:"chain"` LastUpdate time.Time `json:"lastUpdate"` // EXISTING_CODE // EXISTING_CODE } func NewWizardContainer(chain string) WizardContainer { - ret := WizardContainer{ - Chain: chain, - } + ret := WizardContainer{} ret.LastUpdate, _ = ret.getWizardReload() // EXISTING_CODE // EXISTING_CODE @@ -35,6 +34,7 @@ func (s *WizardContainer) String() string { func (s *WizardContainer) NeedsUpdate(force bool) bool { latest, reload := s.getWizardReload() if force || reload { + logger.InfoG("WizardContainer", s.LastUpdate.String(), latest.String()) s.LastUpdate = latest return true } @@ -43,7 +43,6 @@ func (s *WizardContainer) NeedsUpdate(force bool) bool { func (s *WizardContainer) ShallowCopy() Containerer { return &WizardContainer{ - Chain: s.Chain, LastUpdate: s.LastUpdate, // EXISTING_CODE // EXISTING_CODE diff --git a/pkg/types/types_projectfile.go b/pkg/types/types_projectfile.go new file mode 100644 index 00000000..a1d2de62 --- /dev/null +++ b/pkg/types/types_projectfile.go @@ -0,0 +1,74 @@ +// This file is auto-generated. Edit only code inside +// of ExistingCode markers (if any). +package types + +import ( + "encoding/json" + "sort" + "time" + + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" + "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" + sdk "github.com/TrueBlocks/trueblocks-sdk/v3" +) + +type ProjectFile struct { + Version string `json:"version"` + DateSaved string `json:"dateSaved"` + Selected base.Address `json:"selected"` + Addresses []base.Address `json:"addresses"` +} + +func (p *ProjectFile) String() string { + if p.Addresses == nil { + p.Addresses = []base.Address{} + } + bytes, _ := json.Marshal(p) + return string(bytes) +} + +// Save saves a project file to disk (after cleaning it) +func (s *ProjectContainer) Save(fn string, selected base.Address) error { + cleaned, selected := s.Clean(selected) + projectFile := ProjectFile{ + Version: sdk.Version(), + DateSaved: time.Now().String(), + Selected: selected, + Addresses: cleaned, + } + bytes, _ := json.MarshalIndent(projectFile, "", " ") + file.StringToAsciiFile(fn, string(bytes)) + return nil +} + +// Load loads a project file from disk +func (s *ProjectContainer) Load(fn string) (*ProjectFile, error) { + projectFile := &ProjectFile{} + contents := file.AsciiFileToString(fn) + err := json.Unmarshal([]byte(contents), projectFile) + return projectFile, err +} + +// Clean makes sure no zero addresses are stored and also that "selected" is correct. +func (s *ProjectContainer) Clean(selected base.Address) ([]base.Address, base.Address) { + found := false + ret := []base.Address{} + s.ForEveryHistory(func(history *HistoryContainer, data any) bool { + if history.Address.Hex() != base.ZeroAddr.Hex() { + ret = append(ret, history.Address) + if history.Address.Hex() == selected.Hex() { + found = true + } + } + return true + }, nil) + sort.Slice(ret, func(i, j int) bool { return ret[i].Hex() < ret[j].Hex() }) + + if !found && len(ret) > 0 { + selected = ret[0] + } else { + // should never happen, but still we silently fail here + } + + return ret, selected +}