package gui // toolsGui.go implements pages to use Seekia tools // Tools are various utilities to perform tasks such as generating identity hashes, verifying memos, and more import "fyne.io/fyne/v2" import "fyne.io/fyne/v2/container" import "fyne.io/fyne/v2/data/binding" import "fyne.io/fyne/v2/dialog" import "fyne.io/fyne/v2/layout" import "fyne.io/fyne/v2/theme" import "fyne.io/fyne/v2/widget" import "seekia/internal/cryptocurrency/cardanoAddress" import "seekia/internal/cryptocurrency/ethereumAddress" import "seekia/internal/encoding" import "seekia/internal/helpers" import "seekia/internal/identity" import "seekia/internal/memos/createMemos" import "seekia/internal/memos/readMemos" import "seekia/internal/myIdentity" import "seekia/internal/mySettings" import "seekia/internal/seedPhrase" import "errors" func setToolsPage(window fyne.Window, previousPage func()){ currentPage := func(){setToolsPage(window, previousPage)} title := getPageTitleCentered("Tools") backButton := getBackButtonCentered(previousPage) moderateButton := widget.NewButton("Moderate", func(){ setModeratePage(window, true, currentPage) }) hostButton := widget.NewButton(translate("Host"), func(){ setHostPage(window, true, currentPage) }) networkStatisticsButton := widget.NewButton(translate("Network Statistics"), func(){ setNetworkStatisticsPage(window, currentPage) }) generateCustomIdentityHashButton := widget.NewButton("Generate Custom Identity Hash", func(){ setChooseIdentityTypeForNewIdentityHashPage(window, currentPage) }) deriveIdentityScoreAddressButton := widget.NewButton("Derive Identity Score Addresses", func(){ setDeriveIdentityScoreAddressesPage(window, currentPage) }) createMemoButton := widget.NewButton("Create Memo", func(){ setCreateMemoPage(window, false, "", currentPage) }) verifyMemoButton := widget.NewButton("Verify Memo", func(){ setVerifyMemoPage(window, currentPage) }) buttonsGrid := container.NewGridWithColumns(1, moderateButton, hostButton, networkStatisticsButton, generateCustomIdentityHashButton, deriveIdentityScoreAddressButton, createMemoButton, verifyMemoButton) buttonsGridCentered := getContainerCentered(buttonsGrid) page := container.NewVBox(title, backButton, widget.NewSeparator(), buttonsGridCentered) setPageContent(page, window) } // This page is used to generate a new identity hash from the tools menu, not for the user's own identity // The identity hash that is generated will not be saved to the disk when the user exits the utility. func setChooseIdentityTypeForNewIdentityHashPage(window fyne.Window, previousPage func()){ currentPage := func(){setChooseIdentityTypeForNewIdentityHashPage(window, previousPage)} title := getPageTitleCentered("Create New Identity Hash") backButton := getBackButtonCentered(previousPage) description1 := getLabelCentered("Choose the identity type for your new identity hash.") description2 := getLabelCentered("The last character of the identity hash will be different for each identity type.") description3 := getLabelCentered("Mate = m, Host = h, Moderator = r") description4 := getLabelCentered("You can change this later by importing the seed phrase as a different identity type.") getChooseIdentityTypeButtonWithIcon := func(identityType string)(*fyne.Container, error){ identityTypeIcon, err := getIdentityTypeIcon(identityType, -2) if (err != nil) { return nil, err } submitPage := func(seedPhrase string, previousPageFunction func()){ setViewNewSeedPhrasePage(window, identityType, seedPhrase, previousPageFunction, currentPage) } chooseIdentityTypeButton := widget.NewButton(identityType, func(){ setCreateCustomIdentityHashPage(window, identityType, false, 0, 0, currentPage, submitPage) }) buttonWithIcon := container.NewGridWithColumns(1, identityTypeIcon, chooseIdentityTypeButton) return buttonWithIcon, nil } mateButtonWithIcon, err := getChooseIdentityTypeButtonWithIcon("Mate") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } hostButtonWithIcon, err := getChooseIdentityTypeButtonWithIcon("Host") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } moderatorButtonWithIcon, err := getChooseIdentityTypeButtonWithIcon("Moderator") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } chooseIdentityTypeButtonsRow := getContainerCentered(container.NewGridWithRows(1, mateButtonWithIcon, hostButtonWithIcon, moderatorButtonWithIcon)) page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, widget.NewSeparator(), chooseIdentityTypeButtonsRow) setPageContent(page, window) } // This page is used to view a newly generated seed phrase from the generation tool. // The seed phrase is not saved anywhere func setViewNewSeedPhrasePage(window fyne.Window, identityType string, newSeedPhrase string, previousPage func(), nextPage func()){ title := getPageTitleCentered("View New Identity Hash") backButton := getBackButtonCentered(previousPage) description1 := getLabelCentered("Here is your generated identity hash.") newSeedPhraseHash, err := seedPhrase.ConvertSeedPhraseToSeedPhraseHash(newSeedPhrase) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } newIdentityHash, err := identity.GetIdentityHashFromSeedPhraseHash(newSeedPhraseHash, identityType) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } newIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(newIdentityHash) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } identityHashBox := getContainerCentered(getWidgetBoxed(getBoldLabel(newIdentityHashString))) doneButton := getWidgetCentered(widget.NewButtonWithIcon("Done", theme.ConfirmIcon(), nextPage)) description2 := getBoldLabelCentered("When you press Done, this identity hash will be deleted.") description3 := getLabelCentered("Write down this seed phrase to save this identity.") seedPhraseLabel := widget.NewMultiLineEntry() seedPhraseLabel.Wrapping = 3 seedPhraseLabel.SetText(newSeedPhrase) seedPhraseLabel.OnChanged = func(_ string){ seedPhraseLabel.SetText(newSeedPhrase) } seedPhraseLabelBoxed := getWidgetBoxed(seedPhraseLabel) widener := widget.NewLabel(" ") seedPhraseLabelWidened := getContainerCentered(container.NewGridWithColumns(1, seedPhraseLabelBoxed, widener)) page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, identityHashBox, doneButton, widget.NewSeparator(), description2, description3, seedPhraseLabelWidened) setPageContent(page, window) } func setDeriveIdentityScoreAddressesPage(window fyne.Window, previousPage func()){ title := getPageTitleCentered("Derive Identity Score Addresses") backButton := getBackButtonCentered(previousPage) description1 := getLabelCentered("Convert a moderator's identity hash to their identity score cryptocurrency addresses.") description2 := getLabelCentered("Send cryptocurrency to these addresses to increase the user's identity score.") enterIdentityHashLabel := getBoldLabelCentered(" Enter Identity Hash: ") identityHashEntry := widget.NewEntry() identityHashEntry.SetPlaceHolder("Enter Identity Hash.") identityHashEntryBoxed := getWidgetBoxed(identityHashEntry) enterIdentityHashLabelWithEntry := getContainerCentered(container.NewGridWithColumns(1, enterIdentityHashLabel, identityHashEntryBoxed)) ethereumLabel := getBoldLabelCentered(" Ethereum: ") cardanoLabel := getBoldLabelCentered("Cardano:") addressBinding_Ethereum := binding.NewString() addressBinding_Cardano := binding.NewString() addressEntry_Ethereum := widget.NewEntry() addressEntry_Cardano := widget.NewEntry() addressEntryOnChangedFunction_Ethereum := func(_ string){ currentAddress, err := addressBinding_Ethereum.Get() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } addressEntry_Ethereum.SetText(currentAddress) } addressEntryOnChangedFunction_Cardano := func(_ string){ currentAddress, err := addressBinding_Cardano.Get() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } addressEntry_Cardano.SetText(currentAddress) } addressEntry_Ethereum.OnChanged = addressEntryOnChangedFunction_Ethereum addressEntry_Ethereum.SetPlaceHolder("The Ethereum identity score address will display here.") addressEntryBoxed_Ethereum := getWidgetBoxed(addressEntry_Ethereum) addressEntry_Cardano.OnChanged = addressEntryOnChangedFunction_Cardano addressEntry_Cardano.SetPlaceHolder("The Cardano identity score address will display here.") addressEntryBoxed_Cardano := getWidgetBoxed(addressEntry_Cardano) deriveButton := getWidgetCentered(widget.NewButtonWithIcon("Derive", theme.MoveDownIcon(), func(){ inputIdentityHashString := identityHashEntry.Text if (inputIdentityHashString == "Admin"){ setAdminToolsPage(window) return } if (inputIdentityHashString == ""){ addressBinding_Ethereum.Set("") addressBinding_Cardano.Set("") addressEntry_Ethereum.SetText("") addressEntry_Cardano.SetText("") dialogTitle := translate("No Identity Hash Provided") dialogMessage := getLabelCentered("You must enter an identity hash.") dialogContent := container.NewVBox(dialogMessage) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) return } inputIdentityHash, identityType, err := identity.ReadIdentityHashString(inputIdentityHashString) if (err != nil){ addressBinding_Ethereum.Set("") addressBinding_Cardano.Set("") addressEntry_Ethereum.SetText("") addressEntry_Cardano.SetText("") dialogTitle := translate("Identity Hash Is Invalid") dialogMessage1 := getLabelCentered("The identity hash you have entered is invalid.") dialogMessage2 := getLabelCentered("Identity hashes are 27 characters long.") dialogContent := container.NewVBox(dialogMessage1, dialogMessage2) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) return } if (identityType != "Moderator"){ addressBinding_Ethereum.Set("") addressBinding_Cardano.Set("") addressEntry_Ethereum.SetText("") addressEntry_Cardano.SetText("") dialogTitle := translate("Identity Hash Is Invalid") dialogMessage1 := getLabelCentered("The identity hash you have entered is not a moderator identity.") dialogMessage2 := getLabelCentered("Moderator identity hashes end with an r character.") dialogContent := container.NewVBox(dialogMessage1, dialogMessage2) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) return } identityScoreAddress_Ethereum, err := ethereumAddress.GetIdentityScoreEthereumAddressFromIdentityHash(inputIdentityHash) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } err = addressBinding_Ethereum.Set(identityScoreAddress_Ethereum) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } addressEntry_Ethereum.SetText(identityScoreAddress_Ethereum) identityScoreAddress_Cardano, err := cardanoAddress.GetIdentityScoreCardanoAddressFromIdentityHash(inputIdentityHash) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } err = addressBinding_Cardano.Set(identityScoreAddress_Cardano) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } addressEntry_Cardano.SetText(identityScoreAddress_Cardano) })) resultAddressesSection := getContainerCentered(container.NewGridWithColumns(1, ethereumLabel, addressEntryBoxed_Ethereum, cardanoLabel, addressEntryBoxed_Cardano)) page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), enterIdentityHashLabelWithEntry, deriveButton, widget.NewSeparator(), resultAddressesSection) setPageContent(page, window) } func setCreateMemoPage(window fyne.Window, messageExists bool, messageText string, previousPage func()){ currentPage := func(){setCreateMemoPage(window, messageExists, messageText, previousPage)} title := getPageTitleCentered("Create Memo") backButton := getBackButtonCentered(previousPage) description := widget.NewLabel("This page allows you to create a Seekia memo.") memoHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setMemoExplainerPage(window, currentPage) }) descriptionRow := container.NewHBox(layout.NewSpacer(), description, memoHelpButton, layout.NewSpacer()) identityTypesList := []string{"Mate", "Host", "Moderator"} myIdentityHashesList := make([]string, 0) for _, identityType := range identityTypesList{ myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(identityType) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } if (myIdentityExists == false){ continue } myIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(myIdentityHash) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } myIdentityHashesList = append(myIdentityHashesList, myIdentityHashString) } if (len(myIdentityHashesList) == 0){ description2 := getBoldLabelCentered("No identities exist.") description3 := getLabelCentered("You must create a Seekia identity to create a memo.") description4 := getLabelCentered("Create your identity on the Settings - My Data - My Identity Hashes page.") page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionRow, widget.NewSeparator(), description2, description3, description4) setPageContent(page, window) return } chooseIdentityLabel := getBoldLabelCentered("Choose Identity:") chooseIdentitySelector := widget.NewSelect(myIdentityHashesList, nil) chooseIdentitySelector.Selected = myIdentityHashesList[0] chooseIdentitySelectorCentered := getWidgetCentered(chooseIdentitySelector) chooseDecorationLabel := getBoldLabelCentered("Choose Decoration:") decorationOptionsList := []string{ "«« Seekia Memo »»", "⁕ Seekia Memo ⁕", "⁂ Seekia Memo ⁂", "※ Seekia Memo ※", } chooseDecorationSelector := widget.NewSelect(decorationOptionsList, func(newDecoration string){ err := mySettings.SetSetting("MemoDecoration", newDecoration) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } }) getCurrentMemoDecoration := func()(string, error){ exists, memoDecoration, err := mySettings.GetSetting("MemoDecoration") if (err != nil) { return "", err } if (exists == false){ return "«« Seekia Memo »»", nil } return memoDecoration, nil } currentMemoDecoration, err := getCurrentMemoDecoration() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } chooseDecorationSelector.Selected = currentMemoDecoration chooseDecorationSelectorCentered := getWidgetCentered(chooseDecorationSelector) chooseMessageOptionsGrid := getContainerCentered(container.NewGridWithColumns(2, chooseIdentityLabel, chooseDecorationLabel, chooseIdentitySelectorCentered, chooseDecorationSelectorCentered)) enterTextLabel := getBoldLabelCentered("Enter Message:") header := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionRow, widget.NewSeparator(), chooseMessageOptionsGrid, widget.NewSeparator(), enterTextLabel) enterMessageEntry := widget.NewMultiLineEntry() if (messageExists == true){ enterMessageEntry.SetText(messageText) } else { enterMessageEntry.SetPlaceHolder("Enter Message...") } createMemoButton := getWidgetCentered(widget.NewButtonWithIcon("Create Memo", theme.ConfirmIcon(), func(){ newMemoMessage := enterMessageEntry.Text if (newMemoMessage == ""){ title := translate("No Message Provided") dialogMessage := getLabelCentered(translate("You must enter a message to create a memo.")) dialogContent := container.NewVBox(dialogMessage) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } myIdentityHashString := chooseIdentitySelector.Selected myIdentityHash, _, err := identity.ReadIdentityHashString(myIdentityHashString) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } identityFound, myPublicIdentityKey, myPrivateIdentityKey, err := myIdentity.GetMyPublicPrivateIdentityKeysFromIdentityHash(myIdentityHash) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } if (identityFound == false){ setErrorEncounteredPage(window, errors.New("My identity not found after being found already."), currentPage) return } myIdentityType, err := identity.GetIdentityTypeFromIdentityHash(myIdentityHash) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } getNewMemoDecorations := func()(string, string, error){ exists, myMemoDecoration, err := mySettings.GetSetting("MemoDecoration") if (err != nil) { return "", "", err } if (exists == false){ return "««", "»»", nil } if (myMemoDecoration == "«« Seekia Memo »»"){ return "««", "»»", nil } if (myMemoDecoration == "⁕ Seekia Memo ⁕"){ return "⁕", "⁕", nil } if (myMemoDecoration == "⁂ Seekia Memo ⁂"){ return "⁂", "⁂", nil } if (myMemoDecoration == "※ Seekia Memo ※"){ return "※", "※", nil } return "", "", errors.New("MySettings contains unknown MemoDecoration: " + myMemoDecoration) } newMemoDecorationPrefix, newMemoDecorationSuffix, err := getNewMemoDecorations() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } newMemoString, err := createMemos.CreateMemo(myPublicIdentityKey, myPrivateIdentityKey, myIdentityType, newMemoDecorationPrefix, newMemoDecorationSuffix, newMemoMessage) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } // We use this function so that the current memo is not lost if the user goes back nextPagePreviousPage := func(){ setCreateMemoPage(window, true, newMemoMessage, previousPage) } setViewCreatedMemoButton(window, newMemoString, nextPagePreviousPage) })) emptyLabel := widget.NewLabel("") createMemoButtonWithSpacer := container.NewVBox(createMemoButton, emptyLabel) page := container.NewBorder(header, createMemoButtonWithSpacer, nil, nil, enterMessageEntry) setPageContent(page, window) } func setViewCreatedMemoButton(window fyne.Window, memoString string, previousPage func()){ currentPage := func(){setViewCreatedMemoButton(window, memoString, previousPage)} title := getPageTitleCentered(translate("View Memo")) backButton := getBackButtonCentered(previousPage) description1 := getLabelCentered("Here is your new Memo.") description2 := getLabelCentered("Send funds to the crypto addresses to timestamp your memo.") memoIsValid, memoHash, _, _, err := readMemos.ReadMemo(memoString) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (memoIsValid == false){ setErrorEncounteredPage(window, errors.New("setViewCreatedMemoButton called with invalid memo."), previousPage) return } viewCryptocurrencyAddressesButton := getWidgetCentered(widget.NewButtonWithIcon("View Cryptocurrency Addresses", theme.VisibilityIcon(), func(){ setViewMemoCryptocurrencyAddressesPage(window, memoHash, "Ethereum", currentPage) })) memoHashTitle := widget.NewLabel("Memo Hash:") memoHashHex := encoding.EncodeBytesToHexString(memoHash[:]) memoHashTrimmed, _, err := helpers.TrimAndFlattenString(memoHashHex, 15) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } memoHashLabel := getBoldLabel(memoHashTrimmed) viewMemoHashButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ setViewContentHashPage(window, "Memo", memoHash[:], currentPage) }) memoHashRow := container.NewHBox(layout.NewSpacer(), memoHashTitle, memoHashLabel, viewMemoHashButton, layout.NewSpacer()) header := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), viewCryptocurrencyAddressesButton, widget.NewSeparator(), memoHashRow, widget.NewSeparator()) memoTextBox := widget.NewEntry() memoTextBox.SetText(memoString) memoTextBox.OnChanged = func(_ string){ memoTextBox.SetText(memoString) } page := container.NewBorder(header, nil, nil, nil, memoTextBox) setPageContent(page, window) } func setViewMemoCryptocurrencyAddressesPage(window fyne.Window, memoHash [32]byte, cryptocurrencyName string, previousPage func()){ if (cryptocurrencyName != "Ethereum" && cryptocurrencyName != "Cardano"){ setErrorEncounteredPage(window, errors.New("setViewMemoCryptocurrencyAddressesPage called with invalid cryptocurrencyName: " + cryptocurrencyName), previousPage) } currentPage := func(){setViewMemoCryptocurrencyAddressesPage(window, memoHash, cryptocurrencyName, previousPage)} title := getPageTitleCentered("View Memo Cryptocurrency Addresses") backButton := getBackButtonCentered(previousPage) description1 := getLabelCentered("Below is the " + cryptocurrencyName + " address for this memo.") description2 := getLabelCentered("Send funds to this address to timestamp the memo.") description3 := getLabelCentered("Anyone can verify that the memo existed at the time of the earliest transaction to the address.") cryptocurrencyIcon, err := getFyneImageIcon(cryptocurrencyName) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } switchCryptocurrencyButton := widget.NewButton(cryptocurrencyName, func(){ if (cryptocurrencyName == "Ethereum"){ setViewMemoCryptocurrencyAddressesPage(window, memoHash, "Cardano", previousPage) } else { setViewMemoCryptocurrencyAddressesPage(window, memoHash, "Ethereum", previousPage) } }) switchCryptocurrencyButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, cryptocurrencyIcon, switchCryptocurrencyButton)) cryptocurrencyAddress, err := readMemos.GetBlockchainAddressFromMemoHash(cryptocurrencyName, memoHash) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } addressWithButtonsRow, err := getCryptocurrencyAddressLabelWithCopyAndQRButtons(window, cryptocurrencyName, cryptocurrencyAddress, currentPage) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), switchCryptocurrencyButtonWithIcon, widget.NewSeparator(), addressWithButtonsRow) setPageContent(page, window) } func setVerifyMemoPage(window fyne.Window, previousPage func()){ currentPage := func(){setVerifyMemoPage(window, previousPage)} title := getPageTitleCentered("Verify Memo") backButton := getBackButtonCentered(previousPage) description := widget.NewLabel("This page can be used to verify a Seekia memo.") memoHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setMemoExplainerPage(window, currentPage) }) descriptionRow := container.NewHBox(layout.NewSpacer(), description, memoHelpButton, layout.NewSpacer()) memoEntry := widget.NewMultiLineEntry() verifyButton := getWidgetCentered(widget.NewButtonWithIcon("Verify", theme.NavigateNextIcon(), func(){ memoToVerify := memoEntry.Text if (memoToVerify == ""){ dialogTitle := translate("No Memo Provided") dialogMessage := getLabelCentered("The must enter a memo to verify.") dialogContent := container.NewVBox(dialogMessage) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) return } memoIsValid, _, _, _, err := readMemos.ReadMemo(memoToVerify) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } if (memoIsValid == false){ dialogTitle := translate("Memo Is Invalid") dialogMessageA := getBoldLabelCentered("The memo you have entered is invalid.") dialogMessageB := getLabelCentered("You cannot trust that it was written by its alleged author.") dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) return } setViewVerifiedMemoInfoPage(window, memoToVerify, currentPage) })) emptyLabel := widget.NewLabel("") verifyButtonWithSpacer := container.NewVBox(verifyButton, emptyLabel) header := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionRow) page := container.NewBorder(header, verifyButtonWithSpacer, nil, nil, memoEntry) setPageContent(page, window) } func setViewVerifiedMemoInfoPage(window fyne.Window, memoString string, previousPage func()){ currentPage := func(){setViewVerifiedMemoInfoPage(window, memoString, previousPage)} title := getPageTitleCentered("View Memo Info") backButton := getBackButtonCentered(previousPage) description := getBoldLabelCentered("The memo is valid!") memoIsValid, memoHash, authorIdentityHash, memoUnarmoredContents, err := readMemos.ReadMemo(memoString) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (memoIsValid == false){ setErrorEncounteredPage(window, errors.New("setViewVerifiedMemoInfoPage called with invalid memo: " + memoString), previousPage) return } memoHashTitle := widget.NewLabel("Memo Hash:") memoHashHex := encoding.EncodeBytesToHexString(memoHash[:]) memoHashTrimmed, _, err := helpers.TrimAndFlattenString(memoHashHex, 15) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } memoHashLabel := getBoldLabel(memoHashTrimmed) viewMemoHashButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ setViewContentHashPage(window, "Memo", memoHash[:], currentPage) }) memoHashRow := container.NewHBox(layout.NewSpacer(), memoHashTitle, memoHashLabel, viewMemoHashButton, layout.NewSpacer()) authorLabel := widget.NewLabel("Author:") authorIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(authorIdentityHash) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } authorIdentityHashTrimmed, _, err := helpers.TrimAndFlattenString(authorIdentityHashString, 15) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } authorIdentityHashLabel := getBoldLabel(authorIdentityHashTrimmed) viewIdentityHashButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){ setViewIdentityHashPage(window, authorIdentityHash, currentPage) }) authorRow := container.NewHBox(layout.NewSpacer(), authorLabel, authorIdentityHashLabel, viewIdentityHashButton, layout.NewSpacer()) viewCryptocurrencyAddressesButton := getWidgetCentered(widget.NewButtonWithIcon("View Cryptocurrency Addresses", theme.VisibilityIcon(), func(){ setViewMemoCryptocurrencyAddressesPage(window, memoHash, "Ethereum", currentPage) })) viewUnarmoredContentsButton := getWidgetCentered(widget.NewButtonWithIcon("View Unarmored Contents", theme.VisibilityIcon(), func(){ setViewTextPage(window, "Viewing Memo Unarmored Contents", memoUnarmoredContents, true, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), memoHashRow, authorRow, viewCryptocurrencyAddressesButton, viewUnarmoredContentsButton) setPageContent(page, window) }