1827 lines
65 KiB
Go
1827 lines
65 KiB
Go
package gui
|
|
|
|
//broadcastGui.go implements pages for a user to broadcast, disable, and enable their profiles, and to monitor manual broadcasts
|
|
|
|
//TODO: Add review replacement functionality
|
|
// When broadcasting a review, check if the existing conflicting review exists for the same reviewedhash
|
|
// If the review is reviewing an identity that the user has already reviewed and broadcast, we should show this in the GUI
|
|
// The user should confirm to overwrite their previous verdict
|
|
// Once they agree, the application should delete their old broadcasted review
|
|
// We should also add the ability to update existing identity reviews with more errant profiles, if more unruleful profiles are discovered
|
|
|
|
import "fyne.io/fyne/v2"
|
|
import "fyne.io/fyne/v2/canvas"
|
|
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/resources/currencies"
|
|
|
|
import "seekia/internal/appMemory"
|
|
import "seekia/internal/encoding"
|
|
import "seekia/internal/genetics/myPeople"
|
|
import "seekia/internal/globalSettings"
|
|
import "seekia/internal/helpers"
|
|
import "seekia/internal/imagery"
|
|
import "seekia/internal/mateQuestionnaire"
|
|
import "seekia/internal/moderation/myIdentityScore"
|
|
import "seekia/internal/myIdentity"
|
|
import "seekia/internal/network/appNetworkType/getAppNetworkType"
|
|
import "seekia/internal/network/manualBroadcasts"
|
|
import "seekia/internal/network/myBroadcasts"
|
|
import "seekia/internal/network/myIdentityBalance"
|
|
import "seekia/internal/parameters/getParameters"
|
|
import "seekia/internal/profiles/attributeDisplay"
|
|
import "seekia/internal/profiles/myLocalProfiles"
|
|
import "seekia/internal/profiles/myProfileExports"
|
|
import "seekia/internal/profiles/myProfileStatus"
|
|
import "seekia/internal/profiles/profileFormat"
|
|
import "seekia/internal/profiles/readProfiles"
|
|
|
|
import "strings"
|
|
import "image"
|
|
import "time"
|
|
import "errors"
|
|
|
|
func setBroadcastPage(window fyne.Window, profileType string, previousPage func()){
|
|
|
|
setLoadingScreen(window, "Broadcast " + profileType + " Profile", "Loading Broadcast Page...")
|
|
|
|
currentPage := func(){setBroadcastPage(window, profileType, previousPage)}
|
|
|
|
appMemory.SetMemoryEntry("CurrentViewedPage", "Broadcast")
|
|
|
|
if (profileType != "Mate" && profileType != "Moderator"){
|
|
setErrorEncounteredPage(window, errors.New("setBroadcastPage called with invalid profile type: " + profileType), previousPage)
|
|
return
|
|
}
|
|
|
|
title := getPageTitleCentered("Broadcast " + profileType + " Profile")
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(profileType)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (myIdentityExists == false){
|
|
|
|
profileIcon, err := getFyneImageIcon("Profile")
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
iconSize := getCustomFyneSize(10)
|
|
profileIcon.SetMinSize(iconSize)
|
|
profileIconCentered := container.NewHBox(layout.NewSpacer(), profileIcon, layout.NewSpacer())
|
|
|
|
description1 := getBoldLabelCentered("Your " + profileType + " identity does not exist.")
|
|
description2 := getLabelCentered("You must create your user identity hash.")
|
|
description3 := getLabelCentered("This hash is how other users will identify you.")
|
|
|
|
createButton := getWidgetCentered(widget.NewButtonWithIcon("Create Identity", theme.NavigateNextIcon(), func(){
|
|
setChooseNewIdentityHashPage(window, profileType, currentPage, currentPage)
|
|
}))
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), profileIconCentered, description1, description2, description3, createButton)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
description := getLabelCentered("Broadcast your " + profileType + " profile to the world.")
|
|
|
|
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
identityExists, myProfileIsActiveStatus, err := myProfileStatus.GetMyProfileIsActiveStatus(myIdentityHash, appNetworkType)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (identityExists == false) {
|
|
setErrorEncounteredPage(window, errors.New("My identity not found after being found already."), previousPage)
|
|
return
|
|
}
|
|
|
|
myProfileStatusLabel := getBoldLabelCentered("My Profile Status:")
|
|
|
|
getProfileStatusSection := func()(*fyne.Container, error){
|
|
|
|
if (myProfileIsActiveStatus == true){
|
|
|
|
activeIcon, err := getFyneImageIcon("ToggleOn")
|
|
if (err != nil) { return nil, err }
|
|
|
|
activeLabel := getBoldLabel("Active")
|
|
activeLabelWithIcon := container.NewGridWithRows(2, activeIcon, activeLabel)
|
|
|
|
updateProfileButton := widget.NewButtonWithIcon("Update", theme.ViewRefreshIcon(), func(){
|
|
setBroadcastMyProfilePage(window, profileType, currentPage, currentPage)
|
|
})
|
|
|
|
disableButton := widget.NewButtonWithIcon("Disable", theme.CancelIcon(), func(){
|
|
setDisableMyProfilePage(window, profileType, currentPage)
|
|
})
|
|
|
|
actionButtonsGrid := getContainerCentered(container.NewGridWithColumns(1, updateProfileButton, disableButton))
|
|
|
|
profileStatusSection := container.NewVBox(activeLabelWithIcon, widget.NewSeparator(), actionButtonsGrid)
|
|
|
|
return profileStatusSection, nil
|
|
}
|
|
|
|
// myProfileIsActiveStatus == false
|
|
|
|
// Profile is not active.
|
|
// Possible reasons:
|
|
// -Profile was never broadcast
|
|
// -Identity is not funded
|
|
// -Profile is disabled
|
|
// -Profile has expired from network (For Mate profiles only)
|
|
|
|
inactiveIcon, err := getFyneImageIcon("ToggleOff")
|
|
if (err != nil) { return nil, err }
|
|
|
|
inactiveLabel := getBoldLabel("Inactive")
|
|
inactiveLabelWithIcon := container.NewGridWithRows(2, inactiveIcon, inactiveLabel)
|
|
|
|
exists, localIsDisabled, err := myLocalProfiles.GetProfileData(profileType, "Disabled")
|
|
if (err != nil) { return nil, err }
|
|
if (exists == true && localIsDisabled == "Yes"){
|
|
|
|
enableProfileButton := getWidgetCentered(widget.NewButton("Enable", func(){
|
|
setEnableMyProfilePage(window, profileType, currentPage, currentPage)
|
|
}))
|
|
|
|
profileStatusSection := container.NewVBox(inactiveLabelWithIcon, widget.NewSeparator(), enableProfileButton)
|
|
|
|
return profileStatusSection, nil
|
|
}
|
|
|
|
broadcastProfileFunction := func()error{
|
|
|
|
if (profileType == "Mate"){
|
|
|
|
myIdentityFound, myIdentityIsActivated, myBalanceIsSufficient, _, _, err := myIdentityBalance.GetMyIdentityBalanceStatus(myIdentityHash, appNetworkType)
|
|
if (err != nil){ return err }
|
|
if (myIdentityFound == false){
|
|
return errors.New("My identity not found after being found already.")
|
|
}
|
|
|
|
if (myIdentityIsActivated == false || myBalanceIsSufficient == false) {
|
|
dialogTitle := translate("Identity Balance Is Insufficient.")
|
|
dialogMessageA := getLabelCentered(translate("Your identity balance is insufficient."))
|
|
dialogMessageB := getLabelCentered(translate("You must spend credit to fund your identity and broadcast a profile."))
|
|
dialogMessageC := getLabelCentered(translate("Visit the Manage Identity Balance page to fund your identity."))
|
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
|
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
|
return nil
|
|
}
|
|
|
|
} else if (profileType == "Moderator"){
|
|
|
|
myIdentityFound, _, scoreIsSufficient, _, _, err := myIdentityScore.GetMyIdentityScore()
|
|
if (err != nil) { return err }
|
|
if (myIdentityFound == false){
|
|
return errors.New("My moderator identity not found after being found already.")
|
|
}
|
|
if (scoreIsSufficient == false){
|
|
|
|
dialogTitle := translate("Identity Score Is Insufficient.")
|
|
dialogMessageA := getLabelCentered(translate("Your identity score is insufficient."))
|
|
dialogMessageB := getLabelCentered(translate("You must send cryptocurrency to fund your identity."))
|
|
dialogMessageC := getLabelCentered(translate("Visit the Manage Identity Score page to fund your identity."))
|
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
|
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
setBroadcastMyProfilePage(window, profileType, currentPage, currentPage)
|
|
return nil
|
|
}
|
|
|
|
broadcastButton := getWidgetCentered(widget.NewButtonWithIcon("Broadcast", theme.RadioButtonCheckedIcon(), func(){
|
|
err := broadcastProfileFunction()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
}))
|
|
|
|
profileStatusSection := container.NewVBox(inactiveLabelWithIcon, broadcastButton)
|
|
|
|
return profileStatusSection, nil
|
|
}
|
|
|
|
profileStatusSection, err := getProfileStatusSection()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), myProfileStatusLabel, widget.NewSeparator(), profileStatusSection, widget.NewSeparator())
|
|
|
|
if (myProfileIsActiveStatus == true && profileType == "Mate"){
|
|
|
|
addProfileExpirationTimeSection := func()error{
|
|
|
|
profileExpirationTimeLabel := getItalicLabelCentered("My Profile Expiration Time:")
|
|
|
|
page.Add(profileExpirationTimeLabel)
|
|
|
|
// All mate profiles will expire after an expiration duration, which is defined in the network parameters
|
|
// They must be updated, or else they will be dropped by the network
|
|
// We will determine how long this current profile has until it is expired
|
|
// The user has to broadcast a profile again to reset this countdown
|
|
|
|
myIdentityExists, myProfileExists, _, myProfileAttributeExists, myProfileBroadcastTime, err := myBroadcasts.GetAnyAttributeFromMyBroadcastProfile(myIdentityHash, appNetworkType, "BroadcastTime")
|
|
if (err != nil) { return err }
|
|
if (myIdentityExists == false) {
|
|
return errors.New("My identity not found after being found already.")
|
|
}
|
|
if (myProfileExists == false){
|
|
return errors.New("My broadcast profile not found after myProfileStatus has already been determined to be active.")
|
|
}
|
|
if (myProfileAttributeExists == false){
|
|
return errors.New("My Broadcast profile malformed: Missing BroadcastTime")
|
|
}
|
|
|
|
myProfileBroadcastTimeInt64, err := helpers.ConvertBroadcastTimeStringToInt64(myProfileBroadcastTime)
|
|
if (err != nil){
|
|
return errors.New("My Broadcast profile malformed: Contains invalid broadcastTime: " + myProfileBroadcastTime)
|
|
}
|
|
|
|
_, mateProfileMaximumExistenceDuration, err := getParameters.GetMateProfileMaximumExistenceDuration(appNetworkType)
|
|
if (err != nil) { return err }
|
|
|
|
currentTime := time.Now().Unix()
|
|
profileExistenceTime := currentTime - myProfileBroadcastTimeInt64
|
|
|
|
if (profileExistenceTime >= mateProfileMaximumExistenceDuration){
|
|
|
|
return errors.New("My Broadcast profile is expired, but myProfileStatus says it is active")
|
|
}
|
|
|
|
timeUntilExpiration := mateProfileMaximumExistenceDuration - profileExistenceTime
|
|
|
|
timeUntilExpirationString, err := helpers.ConvertUnixTimeDurationToUnitsTimeTranslated(timeUntilExpiration, true)
|
|
if (err != nil) { return err }
|
|
|
|
timeUntilExpirationLabel := getBoldLabel("Expires in " + timeUntilExpirationString)
|
|
|
|
profileExpirationTimeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
|
setMateProfileExpirationExplainerPage(window, currentPage)
|
|
})
|
|
|
|
timeUntilExpirationRow := container.NewHBox(layout.NewSpacer(), timeUntilExpirationLabel, profileExpirationTimeHelpButton, layout.NewSpacer())
|
|
|
|
page.Add(timeUntilExpirationRow)
|
|
page.Add(widget.NewSeparator())
|
|
|
|
return nil
|
|
}
|
|
|
|
err := addProfileExpirationTimeSection()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
}
|
|
|
|
if (profileType == "Mate"){
|
|
|
|
getMyIdentityBalanceSection := func()(*fyne.Container, error){
|
|
|
|
myIdentityBalanceLabel := getItalicLabelCentered("My Identity Balance:")
|
|
|
|
getMyIdentityBalanceStatus := func()(string, error){
|
|
|
|
myIdentityFound, myIdentityIsActivated, myBalanceIsSufficient, _, myBalanceExpirationTime, err := myIdentityBalance.GetMyIdentityBalanceStatus(myIdentityHash, appNetworkType)
|
|
if (err != nil){ return "", err }
|
|
if (myIdentityFound == false){
|
|
return "", errors.New("My identity not found after being found already.")
|
|
}
|
|
|
|
if (myIdentityIsActivated == false || myBalanceIsSufficient == false){
|
|
result := translate("Insufficient")
|
|
return result, nil
|
|
}
|
|
|
|
currentTime := time.Now().Unix()
|
|
|
|
timeLeft := myBalanceExpirationTime - currentTime
|
|
|
|
timeTranslated, err := helpers.ConvertUnixTimeDurationToUnitsTimeTranslated(timeLeft, true)
|
|
if (err != nil) { return "", err }
|
|
|
|
identityBalanceExpirationTime := "Expires in " + timeTranslated
|
|
|
|
return identityBalanceExpirationTime, nil
|
|
}
|
|
|
|
myIdentityBalanceStatus, err := getMyIdentityBalanceStatus()
|
|
if (err != nil){ return nil, err }
|
|
|
|
myIdentityBalanceStatusLabel := getBoldLabelCentered(myIdentityBalanceStatus)
|
|
|
|
manageBalanceButton := getWidgetCentered(widget.NewButtonWithIcon("Manage", theme.VisibilityIcon(), func(){
|
|
setViewMyIdentityBalancePage(window, profileType, currentPage)
|
|
}))
|
|
|
|
identityBalanceSection := container.NewVBox(myIdentityBalanceLabel, myIdentityBalanceStatusLabel, manageBalanceButton)
|
|
|
|
return identityBalanceSection, nil
|
|
}
|
|
|
|
myIdentityBalanceSection, err := getMyIdentityBalanceSection()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page.Add(myIdentityBalanceSection)
|
|
|
|
} else if (profileType == "Moderator"){
|
|
|
|
getIdentityScoreSection := func()(*fyne.Container, error){
|
|
|
|
myIdentityScoreLabel := getItalicLabelCentered("My Identity Score:")
|
|
|
|
myIdentityFound, _, scoreIsSufficient, _, _, err := myIdentityScore.GetMyIdentityScore()
|
|
if (err != nil) { return nil, err }
|
|
if (myIdentityFound == false){
|
|
return nil, errors.New("My identity not found after being found already.")
|
|
}
|
|
|
|
getIdentityScoreStatus := func()string{
|
|
if (scoreIsSufficient == false){
|
|
result := translate("Insufficient")
|
|
return result
|
|
}
|
|
|
|
result := translate("Sufficient")
|
|
return result
|
|
}
|
|
|
|
identityScoreStatus := getIdentityScoreStatus()
|
|
|
|
identityScoreStatusLabel := getBoldLabelCentered(identityScoreStatus)
|
|
|
|
manageIdentityScoreButton := getWidgetCentered(widget.NewButtonWithIcon("View Identity Score", theme.VisibilityIcon(), func(){
|
|
setViewMyModeratorScorePage(window, currentPage)
|
|
}))
|
|
|
|
identityScoreSection := container.NewVBox(myIdentityScoreLabel, identityScoreStatusLabel, manageIdentityScoreButton)
|
|
|
|
return identityScoreSection, nil
|
|
}
|
|
|
|
identityScoreSection, err := getIdentityScoreSection()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page.Add(identityScoreSection)
|
|
}
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setBroadcastMyProfilePage(window fyne.Window, profileType string, previousPage func(), pageToVisitAfter func()) {
|
|
|
|
currentPage := func(){setBroadcastMyProfilePage(window, profileType, previousPage, pageToVisitAfter)}
|
|
|
|
title := getPageTitleCentered(translate("Broadcast " + profileType + " Profile"))
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
if (profileType == "Mate"){
|
|
|
|
myGenomePersonIdentifierExists, myGenomePersonIdentifier, err := myLocalProfiles.GetProfileData("Mate", "GenomePersonIdentifier")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (myGenomePersonIdentifierExists == true){
|
|
|
|
anyGenomesExist, personAnalysisIsReady, _, err := myPeople.CheckIfPersonAnalysisIsReady(myGenomePersonIdentifier)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (anyGenomesExist == true && personAnalysisIsReady == false){
|
|
|
|
description1 := getBoldLabelCentered(translate("Your profile contains a linked genome person."))
|
|
description2 := getLabelCentered(translate("You need to perform your genetic analysis."))
|
|
description3 := getLabelCentered(translate("Only the information you choose will be shared in your profile."))
|
|
|
|
performAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon(translate("Perform Analysis"), theme.NavigateNextIcon(), func(){
|
|
setConfirmPerformPersonAnalysisPage(window, myGenomePersonIdentifier, currentPage, currentPage)
|
|
}))
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, performAnalysisButton)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
err = myProfileExports.UpdateMyExportedProfile(profileType, appNetworkType)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(profileType)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (myIdentityExists == false){
|
|
setErrorEncounteredPage(window, errors.New("My identity not found after being found already."), previousPage)
|
|
return
|
|
}
|
|
|
|
existingBroadcastProfileExists, _, _, _, existingBroadcastProfileRawMap, err := myBroadcasts.GetMyNewestBroadcastProfile(myIdentityHash, appNetworkType)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator())
|
|
|
|
if (existingBroadcastProfileExists == true){
|
|
description1 := getBoldLabelCentered("Are you sure you want to update your " + profileType + " profile?")
|
|
page.Add(description1)
|
|
} else {
|
|
description1 := getBoldLabelCentered("Are you sure you want to broadcast your " + profileType + " profile?")
|
|
page.Add(description1)
|
|
}
|
|
|
|
viewProfileButton := getWidgetCentered(widget.NewButtonWithIcon("View Profile", theme.VisibilityIcon(), func(){
|
|
setViewMyProfilePage(window, profileType, "Local", currentPage)
|
|
}))
|
|
page.Add(viewProfileButton)
|
|
|
|
page.Add(widget.NewSeparator())
|
|
|
|
if (existingBroadcastProfileExists == true){
|
|
|
|
getLastUpdatedTimeAgo := func()(string, error){
|
|
|
|
exists, lastBroadcastTimeString, err := readProfiles.GetFormattedProfileAttributeFromRawProfileMap(existingBroadcastProfileRawMap, "BroadcastTime")
|
|
if (err != nil){ return "", err }
|
|
if (exists == false){
|
|
return "", errors.New("My broadcast profile missing BroadcastTime")
|
|
}
|
|
|
|
lastBroadcastTimeInt64, err := helpers.ConvertStringToInt64(lastBroadcastTimeString)
|
|
if (err != nil){
|
|
return "", errors.New("My broadcast profile contains invalid BroadcastTime: " + lastBroadcastTimeString)
|
|
}
|
|
|
|
lastUpdatedTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(lastBroadcastTimeInt64, true)
|
|
if (err != nil){ return "", err }
|
|
|
|
return lastUpdatedTimeAgo, nil
|
|
}
|
|
|
|
lastUpdatedTimeAgo, err := getLastUpdatedTimeAgo()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
lastUpdatedTitle := widget.NewLabel("Last Updated:")
|
|
lastUpdatedLabel := getBoldLabel(lastUpdatedTimeAgo)
|
|
|
|
lastUpdatedRow := container.NewHBox(layout.NewSpacer(), lastUpdatedTitle, lastUpdatedLabel, layout.NewSpacer())
|
|
|
|
page.Add(lastUpdatedRow)
|
|
|
|
getNumberOfChangesMade := func()(int, error){
|
|
|
|
profileFound, _, _, exportProfileRawMap, err := myProfileExports.GetMyExportedProfile(profileType, appNetworkType)
|
|
if (err != nil) { return 0, err }
|
|
if (profileFound == false){
|
|
return 0, errors.New("My exported profile not found after profile was exported.")
|
|
}
|
|
|
|
allAttributesMap := make(map[int]struct{})
|
|
|
|
for attributeIdentifier, _ := range existingBroadcastProfileRawMap{
|
|
allAttributesMap[attributeIdentifier] = struct{}{}
|
|
}
|
|
|
|
for attributeIdentifier, _ := range exportProfileRawMap{
|
|
allAttributesMap[attributeIdentifier] = struct{}{}
|
|
}
|
|
|
|
changesMade := 0
|
|
|
|
// This will add the number of changed fields
|
|
for attributeIdentifier, _ := range allAttributesMap{
|
|
|
|
attributeName, err := profileFormat.GetAttributeNameFromAttributeIdentifier(attributeIdentifier)
|
|
if (err != nil) { return 0, err }
|
|
|
|
if (attributeName == "BroadcastTime" || attributeName == "ChatKeysLatestUpdateTime" || attributeName == "NaclKey" || attributeName == "KyberKey"){
|
|
continue
|
|
}
|
|
|
|
existingValueExists, existingValue, err := readProfiles.GetFormattedProfileAttributeFromRawProfileMap(existingBroadcastProfileRawMap, attributeName)
|
|
if (err != nil) { return 0, err }
|
|
|
|
exportValueExists, exportValue, err := readProfiles.GetFormattedProfileAttributeFromRawProfileMap(exportProfileRawMap, attributeName)
|
|
if (err != nil) { return 0, err }
|
|
|
|
if (existingValueExists != exportValueExists || existingValue != exportValue){
|
|
|
|
// The value has changed between the old and new profile
|
|
|
|
changesMade += 1
|
|
}
|
|
}
|
|
|
|
return changesMade, nil
|
|
}
|
|
|
|
numberOfChangesMade, err := getNumberOfChangesMade()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
numberOfChangesMadeString := helpers.ConvertIntToString(numberOfChangesMade)
|
|
|
|
changesMadeTitle := getItalicLabel("Changes Made:")
|
|
|
|
numberOfChangesMadeLabel := getBoldLabel(numberOfChangesMadeString)
|
|
|
|
numberOfChangesMadeRow := container.NewHBox(layout.NewSpacer(), changesMadeTitle, numberOfChangesMadeLabel, layout.NewSpacer())
|
|
page.Add(numberOfChangesMadeRow)
|
|
|
|
if (numberOfChangesMade != 0){
|
|
|
|
viewChangesButton := getWidgetCentered(widget.NewButtonWithIcon("View Changes", theme.VisibilityIcon(), func(){
|
|
setViewNewProfileChangesPage(window, profileType, currentPage)
|
|
}))
|
|
|
|
page.Add(viewChangesButton)
|
|
}
|
|
|
|
page.Add(widget.NewSeparator())
|
|
}
|
|
|
|
if (profileType == "Mate"){
|
|
|
|
// Mate profiles must be funded with each broadcast
|
|
|
|
getCurrentAppCurrency := func()(string, error){
|
|
exists, currentAppCurrency, err := globalSettings.GetSetting("Currency")
|
|
if (err != nil) { return "", err }
|
|
if (exists == false){
|
|
return "USD", nil
|
|
}
|
|
return currentAppCurrency, nil
|
|
}
|
|
|
|
currentAppCurrencyCode, err := getCurrentAppCurrency()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
_, appCurrencySymbol, err := currencies.GetCurrencyInfoFromCurrencyCode(currentAppCurrencyCode)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
costTitle := widget.NewLabel("Cost:")
|
|
|
|
appCurrencySymbolButton := widget.NewButton(appCurrencySymbol, func(){
|
|
setChangeAppCurrencyPage(window, currentPage)
|
|
})
|
|
|
|
//TODO: Fix this to actually calculate cost (get cost from parameters)
|
|
costLabel := getBoldLabel("0.1 " + currentAppCurrencyCode)
|
|
|
|
costRow := container.NewHBox(layout.NewSpacer(), costTitle, appCurrencySymbolButton, costLabel, layout.NewSpacer())
|
|
page.Add(costRow)
|
|
|
|
page.Add(widget.NewSeparator())
|
|
}
|
|
|
|
broadcastIcon, err := getFyneImageIcon("Broadcast")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
confirmButton := widget.NewButtonWithIcon("Broadcast", theme.ConfirmIcon(), func(){
|
|
|
|
//TODO: Make sure credit is available
|
|
|
|
setConfirmBroadcastMyProfilePage(window, profileType, currentPage)
|
|
})
|
|
|
|
confirmButtonWithIcon := getContainerCentered(container.NewGridWithRows(2, broadcastIcon, confirmButton))
|
|
|
|
page.Add(confirmButtonWithIcon)
|
|
page.Add(widget.NewSeparator())
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setConfirmBroadcastMyProfilePage(window fyne.Window, profileType string, previousPage func()){
|
|
|
|
if (profileType != "Mate" && profileType != "Moderator"){
|
|
setErrorEncounteredPage(window, errors.New("setConfirmBroadcastMyProfilePage called with invalid profileType: " + profileType), previousPage)
|
|
return
|
|
}
|
|
|
|
title := getPageTitleCentered("Confirm Broadcast Profile")
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
//TODO: Improve wording of below:
|
|
|
|
description1 := getBoldLabelCentered("Confirm broadcast your " + profileType + " profile?")
|
|
description2 := getLabelCentered("This will upload your profile to the Seekia network.")
|
|
description3 := getLabelCentered("The whole world will be able to see it.")
|
|
description4 := getLabelCentered("Make sure you are comfortable sharing your profile with the world.")
|
|
|
|
confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Confirm", theme.ConfirmIcon(), func(){
|
|
|
|
profilePage := func(){setProfilePage(window, true, profileType, false, nil)}
|
|
afterCompletionPage := func(){setBroadcastPage(window, profileType, profilePage)}
|
|
|
|
if (profileType == "Mate"){
|
|
|
|
setStartAndMonitorMateProfileFundingAndBroadcastPage(window, afterCompletionPage)
|
|
|
|
} else if (profileType == "Moderator"){
|
|
|
|
setStartAndMonitorMyModeratorProfileBroadcastPage(window, afterCompletionPage)
|
|
}
|
|
}))
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, confirmButton)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
// This function will show the changes between the existing broadcast profile and the new exported profile
|
|
func setViewNewProfileChangesPage(window fyne.Window, myProfileType string, previousPage func()){
|
|
|
|
setLoadingScreen(window, "View Profile Changes", "Loading profile changes...")
|
|
|
|
currentPage := func(){setViewNewProfileChangesPage(window, myProfileType, previousPage)}
|
|
|
|
title := getPageTitleCentered("View Profile Changes")
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
description := getLabelCentered("Below are the changes between your old and new profile.")
|
|
|
|
getChangesGrid := func()(*fyne.Container, error){
|
|
|
|
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(myProfileType)
|
|
if (err != nil){ return nil, err }
|
|
if (myIdentityExists == false){
|
|
return nil, errors.New("setViewNewProfileChangesPage called with missing identity.")
|
|
}
|
|
|
|
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
|
|
if (err != nil) { return nil, err }
|
|
|
|
oldProfileExists, _, _, _, oldProfileRawMap, err := myBroadcasts.GetMyNewestBroadcastProfile(myIdentityHash, appNetworkType)
|
|
if (err != nil) { return nil, err }
|
|
if (oldProfileExists == false){
|
|
return nil, errors.New("setViewNewProfileChangesPage called when no broadcast profile exists.")
|
|
}
|
|
|
|
profileFound, _, _, newProfileRawMap, err := myProfileExports.GetMyExportedProfile(myProfileType, appNetworkType)
|
|
if (err != nil) { return nil, err }
|
|
if (profileFound == false){
|
|
return nil, errors.New("setViewNewProfileChangesPage called when exportedProfile is missing.")
|
|
}
|
|
|
|
// We use this map to avoid duplicates
|
|
allAttributesIdentifiersMap := make(map[int]struct{})
|
|
|
|
for attributeIdentifier, _ := range oldProfileRawMap{
|
|
allAttributesIdentifiersMap[attributeIdentifier] = struct{}{}
|
|
}
|
|
|
|
for attributeIdentifier, _ := range newProfileRawMap{
|
|
allAttributesIdentifiersMap[attributeIdentifier] = struct{}{}
|
|
}
|
|
|
|
allAttributeNamesList := make([]string, 0, len(allAttributesIdentifiersMap))
|
|
|
|
for attributeIdentifier, _ := range allAttributesIdentifiersMap{
|
|
|
|
attributeName, err := profileFormat.GetAttributeNameFromAttributeIdentifier(attributeIdentifier)
|
|
if (err != nil) { return nil, err }
|
|
|
|
allAttributeNamesList = append(allAttributeNamesList, attributeName)
|
|
}
|
|
|
|
// We sort attributes so they show up in the same order each time
|
|
helpers.SortStringListToUnicodeOrder(allAttributeNamesList)
|
|
|
|
attributeLabel := getItalicLabelCentered("Attribute")
|
|
oldProfileTitle := getItalicLabelCentered("Old Profile")
|
|
newProfileTitle := getItalicLabelCentered("New Profile")
|
|
|
|
attributeTitleColumn := container.NewVBox(attributeLabel, widget.NewSeparator())
|
|
oldProfileColumn := container.NewVBox(oldProfileTitle, widget.NewSeparator())
|
|
newProfileColumn := container.NewVBox(newProfileTitle, widget.NewSeparator())
|
|
|
|
for _, attributeName := range allAttributeNamesList{
|
|
|
|
if (attributeName == "BroadcastTime" || attributeName == "Disabled" || attributeName == "ChatKeysLatestUpdateTime" || attributeName == "NaclKey" || attributeName == "KyberKey"){
|
|
continue
|
|
}
|
|
|
|
oldValueExists, oldValue, err := readProfiles.GetFormattedProfileAttributeFromRawProfileMap(oldProfileRawMap, attributeName)
|
|
if (err != nil) { return nil, err }
|
|
|
|
newValueExists, newValue, err := readProfiles.GetFormattedProfileAttributeFromRawProfileMap(newProfileRawMap, attributeName)
|
|
if (err != nil) { return nil, err }
|
|
|
|
if (oldValueExists == false && newValueExists == false){
|
|
// This should never happen. Probably cosmic ray bit flip or faulty hardware.
|
|
return nil, errors.New("oldProfileRawMap and newProfileRawMap are missing the attribute.")
|
|
}
|
|
if (oldValueExists == true && newValueExists == true && oldValue == newValue){
|
|
// The value is the same between both profiles
|
|
continue
|
|
}
|
|
|
|
attributeTitle, _, formatValueFunction, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName)
|
|
if (err != nil){ return nil, err }
|
|
|
|
attributeTitleLabel := getBoldLabelCentered(attributeTitle)
|
|
|
|
getProfileValueCell := func(valueExists bool, profileValue string)(*fyne.Container, error){
|
|
|
|
if (valueExists == false){
|
|
noneLabel := getItalicLabelCentered(translate("None"))
|
|
return noneLabel, nil
|
|
}
|
|
|
|
switch attributeName{
|
|
|
|
case "Photos":{
|
|
|
|
photosBase64List := strings.Split(profileValue, "+")
|
|
|
|
photosList := make([]image.Image, 0, len(photosBase64List))
|
|
|
|
for _, photoBase64 := range photosBase64List{
|
|
|
|
goImage, err := imagery.ConvertWEBPBase64StringToCroppedDownsizedImageObject(photoBase64)
|
|
if (err != nil){
|
|
return nil, errors.New("setViewNewProfileChangesPage called with profile containing invalid Photos attribute: " + profileValue)
|
|
}
|
|
|
|
photosList = append(photosList, goImage)
|
|
}
|
|
|
|
if (len(photosList) > 5){
|
|
return nil, errors.New("setViewNewProfileChangesPage called with profile containing invalid Photos attribute: Too many photos: " + profileValue)
|
|
}
|
|
|
|
viewAttributeButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
|
setViewFullpageImagesWithNavigationPage(window, photosList, 0, currentPage)
|
|
}))
|
|
|
|
return viewAttributeButton, nil
|
|
}
|
|
|
|
case "Avatar":{
|
|
|
|
avatarIdentifier, err := helpers.ConvertStringToInt(profileValue)
|
|
if (err != nil) {
|
|
return nil, errors.New("setViewNewProfileChangesPage called with profile containing invalid Avatar attribute: " + profileValue)
|
|
}
|
|
|
|
emojiImage, err := getEmojiImageObject(avatarIdentifier)
|
|
if (err != nil){
|
|
return nil, errors.New("setViewNewProfileChangesPage called with profile containing invalid Avatar attribute: " + profileValue)
|
|
}
|
|
|
|
viewAttributeButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
|
setViewFullpageImagePage(window, emojiImage, currentPage)
|
|
}))
|
|
|
|
return viewAttributeButton, nil
|
|
}
|
|
|
|
case "23andMe_AncestryComposition":{
|
|
|
|
viewAttributeButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
|
setViewUser23andMeAncestryCompositionPage(window, profileValue, currentPage)
|
|
}))
|
|
|
|
return viewAttributeButton, nil
|
|
}
|
|
|
|
case "Questionnaire":{
|
|
|
|
questionnaireObject, err := mateQuestionnaire.ReadQuestionnaireString(profileValue)
|
|
if (err != nil) {
|
|
return nil, errors.New("setViewNewProfileChangesPage called with profile containing invalid Questionnaire attribute: " + profileValue + ". Reason: " + err.Error())
|
|
}
|
|
|
|
myResponsesMap := make(map[string]string)
|
|
|
|
submitQuestionnairePage := func(_ string, _ func()){
|
|
currentPage()
|
|
}
|
|
|
|
viewAttributeButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
|
setTakeQuestionnairePage(window, questionnaireObject, 0, myResponsesMap, currentPage, submitQuestionnairePage)
|
|
}))
|
|
|
|
return viewAttributeButton, nil
|
|
}
|
|
|
|
//TODO: Format more values that cannot be displayed as a string in their raw form (Tags, Location, Language, etc...)
|
|
}
|
|
|
|
valueFormatted, err := formatValueFunction(profileValue)
|
|
if (err != nil) { return nil, err }
|
|
|
|
valueTrimmed, anyChangesOccurred, err := helpers.TrimAndFlattenString(valueFormatted, 20)
|
|
if (err != nil) { return nil, err }
|
|
if (anyChangesOccurred == false){
|
|
|
|
valueLabel := getBoldLabelCentered(valueFormatted)
|
|
|
|
return valueLabel, nil
|
|
}
|
|
|
|
trimmedValueLabel := getBoldLabel(valueTrimmed)
|
|
|
|
viewFullValueButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
|
setViewTextPage(window, "Viewing Attribute", valueFormatted, false, currentPage)
|
|
})
|
|
|
|
profileValueCell := container.NewHBox(layout.NewSpacer(), trimmedValueLabel, viewFullValueButton, layout.NewSpacer())
|
|
|
|
return profileValueCell, nil
|
|
}
|
|
|
|
oldValueCell, err := getProfileValueCell(oldValueExists, oldValue)
|
|
if (err != nil) { return nil, err }
|
|
|
|
newValueCell, err := getProfileValueCell(newValueExists, newValue)
|
|
if (err != nil) { return nil, err }
|
|
|
|
attributeTitleColumn.Add(attributeTitleLabel)
|
|
oldProfileColumn.Add(oldValueCell)
|
|
newProfileColumn.Add(newValueCell)
|
|
|
|
attributeTitleColumn.Add(widget.NewSeparator())
|
|
oldProfileColumn.Add(widget.NewSeparator())
|
|
newProfileColumn.Add(widget.NewSeparator())
|
|
}
|
|
|
|
displayGrid := container.NewHBox(layout.NewSpacer(), attributeTitleColumn, oldProfileColumn, newProfileColumn, layout.NewSpacer())
|
|
|
|
return displayGrid, nil
|
|
}
|
|
|
|
displayGrid, err := getChangesGrid()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), displayGrid)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
|
|
func setEnableMyProfilePage(window fyne.Window, myProfileType string, previousPage func(), pageToVisitAfter func()){
|
|
|
|
if (myProfileType != "Mate" && myProfileType != "Moderator"){
|
|
setErrorEncounteredPage(window, errors.New("setEnableMyProfilePage called with invalid profileType: " + myProfileType), previousPage)
|
|
return
|
|
}
|
|
|
|
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(myProfileType)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (myIdentityExists == false){
|
|
// This should not happen, this page should only be reached if identity exists
|
|
setErrorEncounteredPage(window, errors.New("My identity not found after being found already."), previousPage)
|
|
return
|
|
}
|
|
|
|
attributeExists, localProfileIsDisabled, err := myLocalProfiles.GetProfileData(myProfileType, "Disabled")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (attributeExists == false || localProfileIsDisabled != "Yes"){
|
|
// This should not happen, as this page should only be viewed if profile is disabled
|
|
setErrorEncounteredPage(window, errors.New("setEnableMyProfilePage called when your profile is not disabled."), previousPage)
|
|
return
|
|
}
|
|
|
|
title := getPageTitleCentered("Enable " + myProfileType + " Profile")
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
description1 := getBoldLabelCentered("Are you sure you want to re-enable your profile?")
|
|
description2 := getLabelCentered("You must broadcast your profile after this step.")
|
|
|
|
confirmFunction := func(){
|
|
|
|
err = myBroadcasts.DeleteMyBroadcastProfiles(myIdentityHash)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
err := myLocalProfiles.SetProfileData(myProfileType, "Disabled", "No")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
pageToVisitAfter()
|
|
}
|
|
|
|
confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Confirm", theme.ConfirmIcon(), confirmFunction))
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, confirmButton)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
func setDisableMyProfilePage(window fyne.Window, myProfileType string, previousPage func()){
|
|
|
|
if (myProfileType != "Mate" && myProfileType != "Moderator"){
|
|
setErrorEncounteredPage(window, errors.New("setDisableMyProfilePage called with invalid profile type: " + myProfileType), previousPage)
|
|
return
|
|
}
|
|
|
|
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(myProfileType)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (myIdentityExists == false){
|
|
// This should not happen, this page should only be reached if identity exists
|
|
setErrorEncounteredPage(window, errors.New("My identity not found on setDisableMyProfilePage."), previousPage)
|
|
return
|
|
}
|
|
|
|
currentPage := func(){setDisableMyProfilePage(window, myProfileType, previousPage)}
|
|
|
|
title := getPageTitleCentered("Disable My " + myProfileType + " Profile")
|
|
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
getLocalProfileIsDisabledStatus := func()(bool, error){
|
|
|
|
attributeExists, localProfileIsDisabled, err := myLocalProfiles.GetProfileData(myProfileType, "Disabled")
|
|
if (err != nil){ return false, err }
|
|
if (attributeExists == true && localProfileIsDisabled == "Yes"){
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
getBroadcastProfileIsDisabledStatus := func()(bool, error){
|
|
|
|
identityExists, profileExists, _, attributeExists, broadcastProfileIsDisabled, err := myBroadcasts.GetAnyAttributeFromMyBroadcastProfile(myIdentityHash, appNetworkType, "Disabled")
|
|
if (err != nil) { return false, err }
|
|
if (identityExists == false) {
|
|
return false, errors.New("My identity not found after being found already.")
|
|
}
|
|
if (profileExists == false){
|
|
return false, nil
|
|
}
|
|
if (attributeExists == true && broadcastProfileIsDisabled == "Yes"){
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
localProfileIsDisabled, err := getLocalProfileIsDisabledStatus()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
broadcastProfileIsDisabled, err := getBroadcastProfileIsDisabledStatus()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
if (localProfileIsDisabled == true && broadcastProfileIsDisabled == true){
|
|
// This should not happen, as this page should only be viewed if profile is enabled
|
|
setErrorEncounteredPage(window, errors.New("setDisableMyProfilePage accessed with enabled/missing profile."), previousPage)
|
|
return
|
|
}
|
|
|
|
description1 := getBoldLabelCentered("Confirm to disable your " + myProfileType + " Profile?")
|
|
description2 := getLabelCentered("Other users will see your profile as disabled.")
|
|
description3 := getLabelCentered("You will stop receiving " + myProfileType + " messages.")
|
|
description4 := getLabelCentered("You can always enable your profile later.")
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4)
|
|
|
|
if (myProfileType == "Mate"){
|
|
description5 := getLabelCentered("Your identity balance will be retained, but will continue to expire.")
|
|
page.Add(description5)
|
|
} else if (myProfileType == "Moderator"){
|
|
description5 := getLabelCentered("Your identity score will be retained.")
|
|
page.Add(description5)
|
|
}
|
|
|
|
if (myProfileType == "Mate"){
|
|
|
|
// Mate profiles must be funded with each broadcast
|
|
|
|
page.Add(widget.NewSeparator())
|
|
|
|
getCurrentAppCurrency := func()(string, error){
|
|
exists, currentAppCurrency, err := globalSettings.GetSetting("Currency")
|
|
if (err != nil) { return "", err }
|
|
if (exists == false){
|
|
return "USD", nil
|
|
}
|
|
return currentAppCurrency, nil
|
|
}
|
|
|
|
currentAppCurrencyCode, err := getCurrentAppCurrency()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
_, appCurrencySymbol, err := currencies.GetCurrencyInfoFromCurrencyCode(currentAppCurrencyCode)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
costTitle := widget.NewLabel("Cost:")
|
|
|
|
appCurrencySymbolButton := widget.NewButton(appCurrencySymbol, func(){
|
|
setChangeAppCurrencyPage(window, currentPage)
|
|
})
|
|
|
|
//TODO: Fix this to actually calculate cost (get cost from parameters)
|
|
costLabel := getBoldLabel("0.10 " + currentAppCurrencyCode)
|
|
|
|
costRow := container.NewHBox(layout.NewSpacer(), costTitle, appCurrencySymbolButton, costLabel, layout.NewSpacer())
|
|
page.Add(costRow)
|
|
|
|
page.Add(widget.NewSeparator())
|
|
}
|
|
|
|
confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Confirm", theme.ConfirmIcon(), func(){
|
|
|
|
//TODO: Make sure credit is sufficient (if profileType == "Mate")
|
|
|
|
err := myLocalProfiles.SetProfileData(myProfileType, "Disabled", "Yes")
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
err = myProfileExports.UpdateMyExportedProfile(myProfileType, appNetworkType)
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (myProfileType == "Mate"){
|
|
|
|
setStartAndMonitorMateProfileFundingAndBroadcastPage(window, previousPage)
|
|
|
|
} else if (myProfileType == "Moderator"){
|
|
|
|
setStartAndMonitorMyModeratorProfileBroadcastPage(window, previousPage)
|
|
}
|
|
}))
|
|
|
|
// TODO: Add a warning that you should not disable multiple identity's profiles at the same time
|
|
// Correlation between the identities is possible
|
|
|
|
page.Add(confirmButton)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
// This page will first attempt to fund a mate profile, and if it succeeds, it will initiate a manual profile broadcast
|
|
func setStartAndMonitorMateProfileFundingAndBroadcastPage(window fyne.Window, afterCompletionPage func()){
|
|
|
|
pageIdentifier, err := helpers.GetNewRandomHexString(16)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
|
|
appMemory.SetMemoryEntry("CurrentViewedPage", pageIdentifier)
|
|
|
|
checkIfPageHasChangedFunction := func()bool{
|
|
exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage")
|
|
if (exists == true && currentViewedPage == pageIdentifier){
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
title := getPageTitleCentered("Funding Mate Profile")
|
|
|
|
progressBinding := binding.NewString()
|
|
|
|
updateProgressBindings := func(){
|
|
|
|
startTime := time.Now().Unix()
|
|
|
|
for {
|
|
|
|
//TODO: Add details that describe the steps (Example: contacting server, making transaction)
|
|
|
|
currentTime := time.Now().Unix()
|
|
secondsElapsed := currentTime - startTime
|
|
if (secondsElapsed % 3 == 0){
|
|
progressBinding.Set("Funding profile.")
|
|
} else if (secondsElapsed % 3 == 1){
|
|
progressBinding.Set("Funding profile..")
|
|
} else {
|
|
progressBinding.Set("Funding profile...")
|
|
}
|
|
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
|
|
time.Sleep(time.Second/2)
|
|
}
|
|
}
|
|
|
|
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
|
|
fundProfileFunction := func(){
|
|
|
|
// We fund the export profile before we broadcast it
|
|
newProfileFound, newProfileHash, _, _, err := myProfileExports.GetMyExportedProfile("Mate", appNetworkType)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
if (newProfileFound == false){
|
|
setErrorEncounteredPage(window, errors.New("setStartAndMonitorMateProfileFundingAndBroadcastPage called when export profile is missing."), afterCompletionPage)
|
|
return
|
|
}
|
|
|
|
//Outputs:
|
|
// -bool: Fund successful
|
|
// -error
|
|
fundProfile := func(profileHashToFund [28]byte)(bool, error){
|
|
//TODO: Add function to fund profile
|
|
time.Sleep(time.Second * 3)
|
|
|
|
return true, nil
|
|
}
|
|
|
|
fundSuccessful, err := fundProfile(newProfileHash)
|
|
if (err != nil){
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
|
|
setErrorEncounteredPage(window, errors.New("Profile fund encountered error: " + err.Error()), afterCompletionPage)
|
|
return
|
|
}
|
|
if (fundSuccessful == false){
|
|
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
|
|
description1 := getBoldLabelCentered("Seekia failed to fund the profile.")
|
|
description2 := getLabelCentered("The account credit server we contacted may be down.")
|
|
description3 := getLabelCentered("Your internet connection may also be broken.")
|
|
|
|
retryFunction := func(){setStartAndMonitorMateProfileFundingAndBroadcastPage(window, afterCompletionPage)}
|
|
|
|
retryButton := getWidgetCentered(widget.NewButtonWithIcon("Retry", theme.ViewRefreshIcon(), retryFunction))
|
|
exitButton := getWidgetCentered(widget.NewButtonWithIcon("Exit", theme.CancelIcon(), afterCompletionPage))
|
|
|
|
manageConnectionDescription := getLabelCentered("Check if your internet connection is working below.")
|
|
|
|
manageConnectionButton := getWidgetCentered(widget.NewButtonWithIcon("Manage Connection", theme.DownloadIcon(), func(){
|
|
setManageNetworkConnectionPage(window, retryFunction)
|
|
}))
|
|
|
|
page := container.NewVBox(title, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), retryButton, exitButton, widget.NewSeparator(), manageConnectionDescription, manageConnectionButton)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
// Fund is complete. We update the broadcast profile
|
|
|
|
myIdentityExists, newBroadcastProfileHash, err := myBroadcasts.UpdateMyBroadcastProfile("Mate", appNetworkType)
|
|
if (err != nil) {
|
|
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
if (myIdentityExists == false){
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
|
|
setErrorEncounteredPage(window, errors.New("My identity not found after being found already."), afterCompletionPage)
|
|
return
|
|
}
|
|
if (newBroadcastProfileHash != newProfileHash){
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
|
|
// This should not happen. This means we funded the wrong profile.
|
|
// MyBroadcasts broadcasts the newest exported profile
|
|
setErrorEncounteredPage(window, errors.New("Exported profile is not the same as broadcasted profile"), afterCompletionPage)
|
|
return
|
|
}
|
|
|
|
// Now we start a new broadcast
|
|
|
|
//Outputs:
|
|
// -bool: Any hosts found
|
|
// -[22]byte: New process identifier
|
|
// -error
|
|
startNewBroadcastFunction := func()(bool, [22]byte, error){
|
|
|
|
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Mate")
|
|
if (err != nil){ return false, [22]byte{}, err }
|
|
if (myIdentityExists == false){
|
|
return false, [22]byte{}, errors.New("My identity not found after being found already.")
|
|
}
|
|
|
|
// We get the user's newest profile each time
|
|
// It is unlikely, but the user could update their broadcast profile before this manual broadcast process completes
|
|
// In this case, we will just be broadcasting the user's newest profile in multiple manual processes
|
|
|
|
profileExists, _, profileHash, profileBytes, _, err := myBroadcasts.GetMyNewestBroadcastProfile(myIdentityHash, appNetworkType)
|
|
if (err != nil) { return false, [22]byte{}, err }
|
|
if (profileExists == false){
|
|
return false, [22]byte{}, errors.New("Cannot get profile to broadcast on setStartAndMonitorMateProfileFundingAndBroadcastPage")
|
|
}
|
|
if (profileHash != newProfileHash){
|
|
return false, [22]byte{}, errors.New("GetMyNewestBroadcastProfile returning different profile during Mate profile broadcast")
|
|
}
|
|
|
|
profileToBroadcastList := [][]byte{profileBytes}
|
|
|
|
anyHostsFound, processIdentifier, err := manualBroadcasts.StartContentBroadcast("Profile", appNetworkType, profileToBroadcastList, 3)
|
|
if (err != nil) { return false, [22]byte{}, err }
|
|
if (anyHostsFound == false){
|
|
return false, [22]byte{}, nil
|
|
}
|
|
|
|
return true, processIdentifier, nil
|
|
}
|
|
|
|
anyHostsFound, newProcessIdentifier, err := startNewBroadcastFunction()
|
|
if (err != nil){
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
|
|
// Whether the process started or not, we will not show the user in the GUI
|
|
// If no hosts were found, their profile should still be broadcast in the background
|
|
// Seekia will automatically download enough hosts to contact
|
|
return
|
|
}
|
|
|
|
noHostsFound := !anyHostsFound
|
|
|
|
nextPageTitle := "Broadcasting Mate Profile"
|
|
nextPageDescription := "Seekia is broadcasting your Mate profile."
|
|
|
|
setMonitorManualBroadcastPage(window, nextPageTitle, "Profile", nextPageDescription, noHostsFound, newProcessIdentifier, startNewBroadcastFunction, afterCompletionPage)
|
|
}
|
|
|
|
description1 := getBoldLabelCentered("Seekia is funding your new Mate profile.")
|
|
description2 := getLabelCentered("You can leave this page.")
|
|
|
|
progressLabel := widget.NewLabelWithData(progressBinding)
|
|
progressLabel.TextStyle = getFyneTextStyle_Bold()
|
|
|
|
progressLabelCentered := getWidgetCentered(progressLabel)
|
|
|
|
exitButton := getWidgetCentered(widget.NewButtonWithIcon("Exit", theme.CancelIcon(), afterCompletionPage))
|
|
|
|
page := container.NewVBox(title, widget.NewSeparator(), description1, description2, widget.NewSeparator(), progressLabelCentered, widget.NewSeparator(), exitButton)
|
|
|
|
setPageContent(page, window)
|
|
|
|
go updateProgressBindings()
|
|
go fundProfileFunction()
|
|
}
|
|
|
|
// Starts broadcast and allows user to monitor progress
|
|
func setStartAndMonitorMyModeratorProfileBroadcastPage(window fyne.Window, afterCompletionPage func()){
|
|
|
|
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Moderator")
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
if (myIdentityExists == false){
|
|
setErrorEncounteredPage(window, errors.New("Identity does not exist on setStartAndMonitorMyModeratorProfileBroadcastPage"), afterCompletionPage)
|
|
return
|
|
}
|
|
|
|
// We have to update the broadcast profile
|
|
|
|
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
|
|
identityExists, _, err := myBroadcasts.UpdateMyBroadcastProfile("Moderator", appNetworkType)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
if (identityExists == false){
|
|
setErrorEncounteredPage(window, errors.New("Identity not found after being found already."), afterCompletionPage)
|
|
return
|
|
}
|
|
|
|
//Outputs:
|
|
// -bool: Any hosts found
|
|
// -[22]byte: New process identifier
|
|
// -error
|
|
startNewBroadcastFunction := func()(bool, [22]byte, error){
|
|
|
|
// We get the user's newest profile each time
|
|
// It is unlikely, but the user could update their broadcast profile before this manual broadcast process completes
|
|
// In this case, we will just be broadcasting the user's newest profile in multiple manual processes
|
|
|
|
profileExists, _, _, profileBytes, _, err := myBroadcasts.GetMyNewestBroadcastProfile(myIdentityHash, appNetworkType)
|
|
if (err != nil) { return false, [22]byte{}, err }
|
|
if (profileExists == false){
|
|
return false, [22]byte{}, errors.New("Cannot get profile to broadcast on setStartAndMonitorMyModeratorProfileBroadcastPage")
|
|
}
|
|
|
|
profileToBroadcastList := [][]byte{profileBytes}
|
|
|
|
anyHostsFound, processIdentifier, err := manualBroadcasts.StartContentBroadcast("Profile", appNetworkType, profileToBroadcastList, 3)
|
|
if (err != nil) { return false, [22]byte{}, err }
|
|
if (anyHostsFound == false){
|
|
return false, [22]byte{}, nil
|
|
}
|
|
|
|
return true, processIdentifier, nil
|
|
}
|
|
|
|
anyHostsFound, newProcessIdentifier, err := startNewBroadcastFunction()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
|
|
noHostsFound := !anyHostsFound
|
|
|
|
nextPageTitle := "Broadcasting Moderator Profile"
|
|
nextPageDescription := "Seekia is broadcasting your Moderator profile."
|
|
|
|
setMonitorManualBroadcastPage(window, nextPageTitle, "Profile", nextPageDescription, noHostsFound, newProcessIdentifier, startNewBroadcastFunction, afterCompletionPage)
|
|
}
|
|
|
|
|
|
func setMonitorManualBroadcastPage(window fyne.Window, pageTitleText string, broadcastType string, description1Text string, noHostsFound bool, processIdentifier [22]byte, startNewBroadcastFunction func()(bool, [22]byte, error), afterCompletionPage func()){
|
|
|
|
currentPage := func(){setMonitorManualBroadcastPage(window, pageTitleText, broadcastType, description1Text, noHostsFound, processIdentifier, startNewBroadcastFunction, afterCompletionPage)}
|
|
|
|
pageIdentifier, err := helpers.GetNewRandomHexString(16)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
|
|
appMemory.SetMemoryEntry("CurrentViewedPage", pageIdentifier)
|
|
|
|
checkIfPageHasChangedFunction := func()bool{
|
|
exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage")
|
|
if (exists == true && currentViewedPage == pageIdentifier){
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
title := getPageTitleCentered(pageTitleText)
|
|
|
|
retryFunction := func(){
|
|
newBroadcastAnyHostsFound, newProcessIdentifier, err := startNewBroadcastFunction()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, afterCompletionPage)
|
|
return
|
|
}
|
|
|
|
newBroadcastNoHostsFound := !newBroadcastAnyHostsFound
|
|
|
|
setMonitorManualBroadcastPage(window, pageTitleText, broadcastType, description1Text, newBroadcastNoHostsFound, newProcessIdentifier, startNewBroadcastFunction, afterCompletionPage)
|
|
}
|
|
|
|
if (noHostsFound == true){
|
|
|
|
description1 := getLabelCentered("No available hosts were found.")
|
|
description2 := getLabelCentered("Please wait for Seekia to find more hosts.")
|
|
description3 := getLabelCentered("This should take less than 1 minute.")
|
|
description4 := getLabelCentered("You can leave this page and the broadcast will still happen automatically.")
|
|
|
|
retryingInSecondsBinding := binding.NewString()
|
|
|
|
startRetryCountdownFunction := func(){
|
|
|
|
secondsRemaining := 30
|
|
for {
|
|
|
|
secondsRemainingString := helpers.ConvertIntToString(secondsRemaining)
|
|
|
|
if (secondsRemaining != 1){
|
|
retryingInSecondsBinding.Set("Retrying in " + secondsRemainingString + " seconds...")
|
|
} else {
|
|
retryingInSecondsBinding.Set("Retrying in " + secondsRemainingString + " second...")
|
|
}
|
|
|
|
time.Sleep(time.Second)
|
|
secondsRemaining -= 1
|
|
|
|
if (secondsRemaining <= 0){
|
|
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
retryFunction()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
retryingInLabel := widget.NewLabelWithData(retryingInSecondsBinding)
|
|
retryingInLabel.TextStyle = getFyneTextStyle_Bold()
|
|
retryingInLabelCentered := getWidgetCentered(retryingInLabel)
|
|
|
|
retryButton := getWidgetCentered(widget.NewButtonWithIcon("Retry", theme.ViewRefreshIcon(), retryFunction))
|
|
exitButton := getWidgetCentered(widget.NewButtonWithIcon("Exit", theme.CancelIcon(), afterCompletionPage))
|
|
|
|
descriptionD := getLabelCentered("Check if your internet connection is working below.")
|
|
|
|
manageConnectionButton := getWidgetCentered(widget.NewButtonWithIcon("Manage Connection", theme.DownloadIcon(), func(){
|
|
setManageNetworkConnectionPage(window, currentPage)
|
|
}))
|
|
|
|
page := container.NewVBox(title, widget.NewSeparator(), description1, description2, description3, description4, widget.NewSeparator(), retryingInLabelCentered, widget.NewSeparator(), retryButton, exitButton, widget.NewSeparator(), descriptionD, manageConnectionButton)
|
|
|
|
setPageContent(page, window)
|
|
|
|
go startRetryCountdownFunction()
|
|
|
|
return
|
|
}
|
|
|
|
broadcastProgressStatusBinding := binding.NewString()
|
|
broadcastProgressDetailsBinding := binding.NewString()
|
|
|
|
updateBindingsFunction := func(){
|
|
|
|
startTime := time.Now().Unix()
|
|
|
|
setBroadcastProgressStatus := func(processComplete bool, newStatus string){
|
|
|
|
getProgressEllipsis := func()string{
|
|
if (processComplete == true){
|
|
return ""
|
|
}
|
|
|
|
currentTime := time.Now().Unix()
|
|
secondsElapsed := currentTime - startTime
|
|
if (secondsElapsed % 3 == 0){
|
|
return "."
|
|
}
|
|
if (secondsElapsed % 3 == 1){
|
|
return ".."
|
|
}
|
|
return "..."
|
|
}
|
|
progressEllipsis := getProgressEllipsis()
|
|
broadcastProgressStatusBinding.Set(newStatus + progressEllipsis)
|
|
}
|
|
|
|
for {
|
|
|
|
processFound, processIsCompleteBool, processEncounteredError, processError, numberOfCompletedBroadcasts, processProgressDetails := manualBroadcasts.GetProcessInfo(processIdentifier)
|
|
if (processFound == false){
|
|
// This should not happen
|
|
processIdentifierHex := encoding.EncodeBytesToHexString(processIdentifier[:])
|
|
setBroadcastProgressStatus(true, "ERROR: manualBroadcasts process not found: " + processIdentifierHex)
|
|
broadcastProgressDetailsBinding.Set("Report this error to the Seekia developers.")
|
|
|
|
return
|
|
}
|
|
|
|
numberOfCompletedBroadcastsString := helpers.ConvertIntToString(numberOfCompletedBroadcasts)
|
|
|
|
if (processIsCompleteBool == true){
|
|
|
|
if (processEncounteredError == true){
|
|
setBroadcastProgressStatus(true, "ERROR:" + processError.Error())
|
|
broadcastProgressDetailsBinding.Set("Report this error to the Seekia developers.")
|
|
return
|
|
}
|
|
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
|
|
if (numberOfCompletedBroadcasts == 3){
|
|
// We broadcasted to all 3 hosts. Broadcast is complete.
|
|
afterCompletionPage()
|
|
return
|
|
}
|
|
|
|
// Broadcast did not complete all required hosts.
|
|
// We will show user option to retry.
|
|
|
|
retryButton := getWidgetCentered(widget.NewButtonWithIcon("Retry", theme.ViewRefreshIcon(), retryFunction))
|
|
|
|
exitButton := getWidgetCentered(widget.NewButtonWithIcon("Exit", theme.CancelIcon(), afterCompletionPage))
|
|
|
|
if (numberOfCompletedBroadcasts != 0){
|
|
// Process is complete, and at least 1 host was broadcasted to, but not all hosts.
|
|
// This means that we ran out of hosts.
|
|
// Seekia will keep broadcasting the content(s) in the background, so nothing needs to be done by the user.
|
|
|
|
description1 := getBoldLabelCentered("The broadcast was successful to " + numberOfCompletedBroadcastsString + "/3 hosts.")
|
|
description2 := getLabelCentered("We ran out of hosts to contact.")
|
|
description3 := getLabelCentered("You can exit or wait for more hosts to be found and retry.")
|
|
description4 := getLabelCentered("Seekia will broadcast the " + broadcastType + " in the background either way.")
|
|
|
|
page := container.NewVBox(title, widget.NewSeparator(), description1, description2, description3, description4, retryButton, exitButton)
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
// Broadcast completed, but 0 hosts were successfully contacted
|
|
// Now we will show them a "Broadcast Failed" page and an option to retry.
|
|
|
|
description1 := getBoldLabelCentered("The broadcast was unsuccessful.")
|
|
description2 := getLabelCentered("Retry the broadcast?")
|
|
|
|
descriptionC := getLabelCentered("Check if your internet connection is working below.")
|
|
|
|
manageConnectionButton := getWidgetCentered(widget.NewButtonWithIcon("Manage Connection", theme.DownloadIcon(), func(){
|
|
setManageNetworkConnectionPage(window, currentPage)
|
|
}))
|
|
|
|
page := container.NewVBox(title, widget.NewSeparator(), description1, description2, retryButton, exitButton, widget.NewSeparator(), descriptionC, manageConnectionButton)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
// Broadcast is not complete
|
|
|
|
progressProgressStatusString := "Broadcasted to " + numberOfCompletedBroadcastsString + "/3 hosts."
|
|
setBroadcastProgressStatus(processIsCompleteBool, progressProgressStatusString)
|
|
broadcastProgressDetailsBinding.Set(processProgressDetails)
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
description1 := getBoldLabelCentered(description1Text)
|
|
description2 := getLabelCentered("This process will run in the background.")
|
|
description3 := getLabelCentered("You can leave this page.")
|
|
|
|
broadcastProgressStatusLabel := widget.NewLabelWithData(broadcastProgressStatusBinding)
|
|
broadcastProgressStatusLabel.TextStyle = getFyneTextStyle_Bold()
|
|
broadcastProgressStatusLabelCentered := getWidgetCentered(broadcastProgressStatusLabel)
|
|
|
|
broadcastProgressDetailsLabel := getWidgetCentered(widget.NewLabelWithData(broadcastProgressDetailsBinding))
|
|
|
|
exitPageButton := getWidgetCentered(widget.NewButtonWithIcon("Exit", theme.MediaSkipNextIcon(), func(){
|
|
appMemory.DeleteMemoryEntry("CurrentViewedPage")
|
|
afterCompletionPage()
|
|
}))
|
|
|
|
page := container.NewVBox(title, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), broadcastProgressStatusLabelCentered, broadcastProgressDetailsLabel, widget.NewSeparator(), exitPageButton)
|
|
|
|
setPageContent(page, window)
|
|
|
|
go updateBindingsFunction()
|
|
}
|
|
|
|
func setViewMyIdentityBalancePage(window fyne.Window, myIdentityType string, previousPage func()){
|
|
|
|
if (myIdentityType != "Mate" && myIdentityType != "Host"){
|
|
setErrorEncounteredPage(window, errors.New("setViewMyIdentityBalancePage called with invalid identity type: " + myIdentityType), previousPage)
|
|
return
|
|
}
|
|
|
|
currentPage := func(){setViewMyIdentityBalancePage(window, myIdentityType, previousPage)}
|
|
|
|
title := getPageTitleCentered("My " + myIdentityType + " Identity Balance")
|
|
backButton := getBackButtonCentered(previousPage)
|
|
|
|
myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(myIdentityType)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
if (myIdentityExists == false){
|
|
// This should not occur, this page should only be reached if identity exists.
|
|
setErrorEncounteredPage(window, errors.New("Identity not found."), previousPage)
|
|
return
|
|
}
|
|
|
|
getDescriptionSection := func()*fyne.Container{
|
|
|
|
if (myIdentityType == "Mate"){
|
|
description1 := getLabelCentered("Below is your Mate identity balance.")
|
|
description2 := getLabelCentered("It must be sufficient for your profile to be broadcast.")
|
|
description3 := getLabelCentered("Spend credit to increase your identity balance.")
|
|
description4 := getLabelCentered("You can be gifted credit or buy some with cryptocurrency.")
|
|
descriptionSection := container.NewVBox(description1, description2, description3, description4)
|
|
return descriptionSection
|
|
}
|
|
|
|
// myIdentityType == "Host"
|
|
description1 := getLabelCentered("Below is your Host identity balance.")
|
|
description2 := getLabelCentered("It must be sufficient for you to be a Seekia host.")
|
|
description3 := getLabelCentered("Spend credit to increase your identity balance.")
|
|
description4 := getLabelCentered("You can be gifted credit or buy some with cryptocurrency.")
|
|
|
|
descriptionSection := container.NewVBox(description1, description2, description3, description4)
|
|
return descriptionSection
|
|
}
|
|
|
|
descriptionSection := getDescriptionSection()
|
|
|
|
getMyIdentityExpirationTimeDisplaySection := func()(*fyne.Container, error){
|
|
|
|
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
|
|
if (err != nil) { return nil, err }
|
|
|
|
identityFound, identityIsActivated, balanceIsSufficient, _, balanceExpirationTime, err := myIdentityBalance.GetMyIdentityBalanceStatus(myIdentityHash, appNetworkType)
|
|
if (err != nil) { return nil, err }
|
|
if (identityFound == false) {
|
|
return nil, errors.New("Identity found not found after being found already.")
|
|
}
|
|
|
|
currentBalanceStatusText := getBoldLabelCentered("My Balance Status:")
|
|
|
|
getBalanceStatusIcon := func()(*canvas.Image, error){
|
|
|
|
iconSize := getCustomFyneSize(0)
|
|
|
|
if (identityIsActivated == false || balanceIsSufficient == false){
|
|
|
|
insufficientIcon, err := getFyneImageIcon("Insufficient")
|
|
if (err != nil) { return nil, err }
|
|
|
|
insufficientIcon.SetMinSize(iconSize)
|
|
return insufficientIcon, nil
|
|
}
|
|
sufficientIcon, err := getFyneImageIcon("Sufficient")
|
|
if (err != nil) { return nil, err }
|
|
sufficientIcon.SetMinSize(iconSize)
|
|
|
|
return sufficientIcon, nil
|
|
}
|
|
|
|
balanceStatusIcon, err := getBalanceStatusIcon()
|
|
if (err != nil){ return nil, err }
|
|
|
|
balanceStatusIconCentered := getFyneImageCentered(balanceStatusIcon)
|
|
|
|
getBalanceStatusText := func()string{
|
|
if (balanceIsSufficient == true){
|
|
return "Sufficient"
|
|
}
|
|
return "Insufficient"
|
|
}
|
|
|
|
balanceStatusText := getBalanceStatusText()
|
|
balanceStatusLabel := getBoldLabelCentered(balanceStatusText)
|
|
|
|
displaySection := container.NewVBox(currentBalanceStatusText, balanceStatusIconCentered, balanceStatusLabel, widget.NewSeparator())
|
|
|
|
if (balanceIsSufficient == true){
|
|
|
|
timeRemainingTitle := getLabelCentered("Time Until Expiration:")
|
|
|
|
currentTime := time.Now().Unix()
|
|
|
|
if (currentTime > balanceExpirationTime){
|
|
return nil, errors.New("Balance expiration time is less than current time while Balance is sufficient = true")
|
|
}
|
|
|
|
timeLeft := balanceExpirationTime - currentTime
|
|
|
|
timeTranslated, err := helpers.ConvertUnixTimeDurationToUnitsTimeTranslated(timeLeft, true)
|
|
if (err != nil) { return nil, err }
|
|
|
|
timeRemainingLabel := getBoldLabelCentered(timeTranslated)
|
|
|
|
displaySection.Add(timeRemainingTitle)
|
|
displaySection.Add(timeRemainingLabel)
|
|
}
|
|
|
|
refreshBalanceButton := widget.NewButtonWithIcon(translate("Refresh"), theme.ViewRefreshIcon(), func(){
|
|
//TODO: Add manualDownloads download and page to monitor it
|
|
showUnderConstructionDialog(window)
|
|
})
|
|
|
|
addTimeButton := widget.NewButtonWithIcon(translate("Add Time"), theme.MoveUpIcon(), func(){
|
|
setIncreaseMyIdentityBalancePage(window, myIdentityType, 30, currentPage)
|
|
})
|
|
|
|
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, refreshBalanceButton, addTimeButton))
|
|
|
|
displaySection.Add(buttonsGrid)
|
|
|
|
return displaySection, nil
|
|
}
|
|
|
|
identityExpirationTimeSection, err := getMyIdentityExpirationTimeDisplaySection()
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionSection, widget.NewSeparator(), identityExpirationTimeSection)
|
|
|
|
setPageContent(page, window)
|
|
}
|
|
|
|
|