seekia/gui/settingsGui.go

1883 lines
64 KiB
Go
Raw Normal View History

package gui
// settingsGui.go implements pages to manage a user's settings and user data
import "fyne.io/fyne/v2"
import "fyne.io/fyne/v2/container"
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/globalSettings"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/localFilesystem"
import "seekia/internal/logger"
import "seekia/internal/messaging/chatMessageStorage"
import "seekia/internal/messaging/myChatMessages"
import "seekia/internal/moderation/reportStorage"
import "seekia/internal/moderation/reviewStorage"
import "seekia/internal/myIdentity"
import "seekia/internal/mySeedPhrases"
import "seekia/internal/mySettings"
import "seekia/internal/network/appNetworkType/getAppNetworkType"
import "seekia/internal/network/appNetworkType/setAppNetworkType"
import "seekia/internal/network/myBroadcasts"
import "seekia/internal/profiles/profileStorage"
import "seekia/internal/seedPhrase"
import "errors"
func setSettingsPage(window fyne.Window){
appMemory.SetMemoryEntry("CurrentViewedPage", "Settings")
currentPage := func(){setSettingsPage(window)}
title := getPageTitleCentered("Settings")
description := getLabelCentered("Manage your Seekia settings.")
myDataButton := widget.NewButton("My Data", func(){
setSettingsPage_MyData(window, currentPage)
})
themesButton := widget.NewButton("Themes", func(){
setChooseAppThemePage(window, currentPage)
})
navigationButton := widget.NewButton("Navigation", func(){
setNavigationSettingsPage(window, currentPage)
})
contentFilteringButton := widget.NewButton("Content Filtering", func(){
setContentFilteringSettingsPage(window, currentPage)
})
networkSettingsButton := widget.NewButton("Network", func(){
setManageNetworkSettingsPage(window, currentPage)
})
storageSettingsButton := widget.NewButton("Storage", func(){
setManageStoragePage(window, currentPage)
})
toolsButton := widget.NewButton("Tools", func(){
setToolsPage(window, currentPage)
})
languageButton := widget.NewButton("Language", func(){
setSelectLanguagePage(window, true, currentPage)
})
logsButton := widget.NewButton("Logs", func(){
setViewLogsPage(window, "General", currentPage)
})
developerButton := widget.NewButton("Developer", func(){
setViewDeveloperSettingsPage(window, currentPage)
})
buttonGrid := getContainerCentered(container.NewGridWithColumns(1, myDataButton, themesButton, navigationButton, contentFilteringButton, networkSettingsButton, storageSettingsButton, toolsButton, languageButton, logsButton, developerButton))
pageContent := container.NewVBox(title, widget.NewSeparator(), description, widget.NewSeparator(), buttonGrid)
setPageContent(pageContent, window)
}
func getMetricImperialSwitchButton(window fyne.Window, currentPage func())(fyne.Widget, error){
currentUnitsExist, currentUnits, err := globalSettings.GetSetting("MetricOrImperial")
if (err != nil){ return nil, err }
getSwitchButton := func()fyne.Widget{
if (currentUnitsExist == false || currentUnits == "Metric"){
button := widget.NewButton(translate("Metric"), func(){
err := globalSettings.SetSetting("MetricOrImperial", "Imperial")
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
})
return button
}
button := widget.NewButton(translate("Imperial"), func(){
err := globalSettings.SetSetting("MetricOrImperial", "Metric")
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
})
return button
}
switchButton := getSwitchButton()
return switchButton, nil
}
func setSettingsPage_MyData(window fyne.Window, previousPage func()){
currentPage := func(){setSettingsPage_MyData(window, previousPage)}
title := getPageTitleCentered("My Data")
backButton := getBackButtonCentered(previousPage)
myIdentityHashesButton := widget.NewButton("My Identity Hashes", func(){
setViewMyIdentityHashesPage(window, "Mate", currentPage)
})
mySeedPhrasesButton := widget.NewButton("My Seed Phrases", func(){
setViewMySeedPhrasesPage(window, "Mate", currentPage)
})
deleteIdentityButton := widget.NewButton("Delete Identity", func(){
setDeleteMyIdentityPage(window, "Mate", currentPage)
})
importIdentityButton := widget.NewButton("Import Identity", func(){
setImportMyIdentityPage(window, "Mate", currentPage)
})
exportDataButton := widget.NewButton("Export Data", func(){
//TODO
// Page to export user data
// This should export the UserData folder(s) to a folder, and that data should be able to be imported to a new device
// The GUI should give options on which data to export
showUnderConstructionDialog(window)
})
importDataButton := widget.NewButton("Import Data", func(){
//TODO
// On import, user should be able to select which elements to import, and any conflicts with existing data should be shown
// When importing chat keys, we must make sure to prune the user's undecryptable message hashes list
// The new keys should be used to attempt to decrypt those messages again (if they still exist on the network)
showUnderConstructionDialog(window)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, myIdentityHashesButton, mySeedPhrasesButton, deleteIdentityButton, importIdentityButton, exportDataButton, importDataButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), buttonsGrid)
setPageContent(page, window)
}
func setViewMySeedPhrasesPage(window fyne.Window, myIdentityType string, previousPage func()){
title := getPageTitleCentered(translate("My Seed Phrase"))
currentPage := func(){setViewMySeedPhrasesPage(window, myIdentityType, previousPage)}
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Your seed phrase can recover your " + myIdentityType + " identity on any device.")
description2 := getLabelCentered("Write it down to prevent the loss of your identity.")
description3 := getBoldLabelCentered("Do not share your seed phrases!")
identityTypeIcon, err := getIdentityTypeIcon(myIdentityType, -10)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
getIdentityTypeLabelOrChangeButton := func()(fyne.Widget, error){
getHostModeEnabledBool := func()(bool, error){
exists, hostModeOnOffStatus, err := mySettings.GetSetting("HostModeOnOffStatus")
if (err != nil) { return false, err }
if (exists == true && hostModeOnOffStatus == "On"){
return true, nil
}
return false, nil
}
getModeratorModeEnabledBool := func()(bool, error){
exists, moderatorModeOnOffStatus, err := mySettings.GetSetting("ModeratorModeOnOffStatus")
if (err != nil) { return false, err }
if (exists == true && moderatorModeOnOffStatus == "On"){
return true, nil
}
return false, nil
}
hostModeEnabled, err := getHostModeEnabledBool()
if (err != nil) { return nil, err }
moderatorModeEnabled, err := getModeratorModeEnabledBool()
if (err != nil) { return nil, err }
hostIdentityExists, _, err := mySeedPhrases.GetMySeedPhrase("Host")
if (err != nil) { return nil, err }
moderatorIdentityExists, _, err := mySeedPhrases.GetMySeedPhrase("Moderator")
if (err != nil) { return nil, err }
if (hostModeEnabled == false && hostIdentityExists == false && moderatorModeEnabled == false && moderatorIdentityExists == false){
// Mate identity is the only identity to show
mateLabel := getBoldLabel("Mate")
return mateLabel, nil
}
getNextIdentityType := func()string{
if (myIdentityType == "Mate"){
if (hostModeEnabled == true || hostIdentityExists == true){
return "Host"
}
return "Moderator"
}
if (myIdentityType == "Host"){
if (moderatorModeEnabled == true || moderatorIdentityExists == true){
return "Moderator"
}
return "Mate"
}
return "Mate"
}
nextIdentityType := getNextIdentityType()
changeIdentityTypeButton := widget.NewButton(myIdentityType, func(){
setViewMySeedPhrasesPage(window, nextIdentityType, previousPage)
})
return changeIdentityTypeButton, nil
}
identityTypeLabelOrChangeButton, err := getIdentityTypeLabelOrChangeButton()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
identityTypeLabelOrChangeButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, identityTypeIcon, identityTypeLabelOrChangeButton))
currentSeedPhraseExists, currentSeedPhrase, err := mySeedPhrases.GetMySeedPhrase(myIdentityType)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (currentSeedPhraseExists == false) {
identityDoesNotExistLabel := getBoldLabelCentered("Your " + myIdentityType + " identity does not exist.")
createIdentityButton := getWidgetCentered(widget.NewButtonWithIcon("Create Identity", theme.NavigateNextIcon(), func(){
setChooseNewIdentityHashPage(window, myIdentityType, currentPage, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), identityTypeLabelOrChangeButtonWithIcon, identityDoesNotExistLabel, createIdentityButton, widget.NewSeparator())
setPageContent(page, window)
return
}
seedPhraseLabel := widget.NewMultiLineEntry()
seedPhraseLabel.Wrapping = 3
seedPhraseLabel.SetText(currentSeedPhrase)
seedPhraseLabel.OnChanged = func(_ string){
seedPhraseLabel.SetText(currentSeedPhrase)
}
seedPhraseLabelBoxed := getWidgetBoxed(seedPhraseLabel)
widener := widget.NewLabel(" ")
seedPhraseBoxWidened := getContainerCentered(container.NewGridWithColumns(1, seedPhraseLabelBoxed, widener))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), identityTypeLabelOrChangeButtonWithIcon, seedPhraseBoxWidened)
setPageContent(page, window)
}
func setViewMyIdentityHashesPage(window fyne.Window, myIdentityType string, previousPage func()){
currentPage := func(){setViewMyIdentityHashesPage(window, myIdentityType, previousPage)}
title := getPageTitleCentered(translate("My Identity Hashes"))
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("All users of Seekia have a unique identity hash.")
description2 := getLabelCentered("Share your identity hash to advertise your identity.")
identityTypeIcon, err := getIdentityTypeIcon(myIdentityType, -10)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
getIdentityTypeLabelOrChangeButton := func()(fyne.Widget, error){
getHostModeEnabledBool := func()(bool, error){
exists, hostModeOnOffStatus, err := mySettings.GetSetting("HostModeOnOffStatus")
if (err != nil) { return false, err }
if (exists == true && hostModeOnOffStatus == "On"){
return true, nil
}
return false, nil
}
getModeratorModeEnabledBool := func()(bool, error){
exists, moderatorModeOnOffStatus, err := mySettings.GetSetting("ModeratorModeOnOffStatus")
if (err != nil) { return false, err }
if (exists == true && moderatorModeOnOffStatus == "On"){
return true, nil
}
return false, nil
}
hostModeEnabled, err := getHostModeEnabledBool()
if (err != nil) { return nil, err }
moderatorModeEnabled, err := getModeratorModeEnabledBool()
if (err != nil) { return nil, err }
hostIdentityExists, _, err := mySeedPhrases.GetMySeedPhrase("Host")
if (err != nil) { return nil, err }
moderatorIdentityExists, _, err := mySeedPhrases.GetMySeedPhrase("Moderator")
if (err != nil) { return nil, err }
if (hostModeEnabled == false && hostIdentityExists == false && moderatorModeEnabled == false && moderatorIdentityExists == false){
// Mate identity is the only identity to show
mateLabel := getBoldLabel("Mate")
return mateLabel, nil
}
getNextIdentityType := func()string{
if (myIdentityType == "Mate"){
if (hostModeEnabled == true || hostIdentityExists == true){
return "Host"
}
return "Moderator"
}
if (myIdentityType == "Host"){
if (moderatorModeEnabled == true || moderatorIdentityExists == true){
return "Moderator"
}
return "Mate"
}
return "Mate"
}
nextIdentityType := getNextIdentityType()
changeIdentityTypeButton := widget.NewButton(myIdentityType, func(){
setViewMyIdentityHashesPage(window, nextIdentityType, previousPage)
})
return changeIdentityTypeButton, nil
}
identityTypeLabelOrChangeButton, err := getIdentityTypeLabelOrChangeButton()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
identityTypeLabelOrChangeButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, identityTypeIcon, identityTypeLabelOrChangeButton))
currentIdentityHashExists, currentIdentityHash, err := myIdentity.GetMyIdentityHash(myIdentityType)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
if (currentIdentityHashExists == false) {
identityDoesNotExistLabel := getBoldLabelCentered("Your " + myIdentityType + " identity does not exist.")
createIdentityButton := getWidgetCentered(widget.NewButtonWithIcon("Create Identity", theme.NavigateNextIcon(), func(){
setChooseNewIdentityHashPage(window, myIdentityType, currentPage, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), identityTypeLabelOrChangeButtonWithIcon, identityDoesNotExistLabel, createIdentityButton, widget.NewSeparator())
setPageContent(page, window)
return
}
currentIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(currentIdentityHash)
if (err != nil){
currentIdentityHashHex := encoding.EncodeBytesToHexString(currentIdentityHash[:])
setErrorEncounteredPage(window, errors.New("GetMyIdentityHash returning invalid myIdentityHash: " + currentIdentityHashHex), previousPage)
return
}
identityHashLabel := widget.NewMultiLineEntry()
identityHashLabel.SetText(currentIdentityHashString)
identityHashLabel.OnChanged = func(_ string){
identityHashLabel.SetText(currentIdentityHashString)
}
identityHashLabelBoxed := getWidgetBoxed(identityHashLabel)
boxWidener := widget.NewLabel(" ")
identityHashBoxWidened := getContainerCentered(container.NewGridWithColumns(1, identityHashLabelBoxed, boxWidener))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), identityTypeLabelOrChangeButtonWithIcon, identityHashBoxWidened)
setPageContent(page, window)
}
func setDeleteMyIdentityPage(window fyne.Window, myIdentityType string, previousPage func()){
currentPage := func(){setDeleteMyIdentityPage(window, myIdentityType, previousPage)}
if (myIdentityType != "Mate" && myIdentityType != "Host" && myIdentityType != "Moderator"){
setErrorEncounteredPage(window, errors.New("setDeleteMyIdentityPage called with invalid identity type: " + myIdentityType), previousPage)
return
}
title := getPageTitleCentered(translate("Delete Identity"))
backButton := getBackButtonCentered(previousPage)
getDescriptionSection := func()*fyne.Container{
description1 := getLabelCentered("This page allows you to delete your " + myIdentityType + " identity.")
if (myIdentityType == "Host"){
description2 := getLabelCentered("You will lose your host identity balance and account credit.")
description3 := getLabelCentered("Write down your seed phrase to keep access to your identity and credit.")
description4 := getLabelCentered("Disable your profile before doing this.")
descriptionSection := container.NewVBox(description1, description2, description3, description4)
return descriptionSection
}
if (myIdentityType == "Moderator"){
description2 := getLabelCentered("You will lose your identity score and account credit.")
description3 := getLabelCentered("This will also delete your message history.")
description4 := getLabelCentered("Write down your seed phrase to keep access to your identity and credit.")
descriptionSection := container.NewVBox(description1, description2, description3, description4)
return descriptionSection
}
description2 := getLabelCentered("This will delete your messages, identity balance, and account credit.")
description3 := getLabelCentered("Write down your seed phrase to keep access to your identity and credit.")
description4 := getLabelCentered("Disable your profile before doing this.")
descriptionSection := container.NewVBox(description1, description2, description3, description4)
return descriptionSection
}
descriptionSection := getDescriptionSection()
getIdentityTypeLabelOrChangeButton := func()(fyne.Widget, error){
hostIdentityExists, _, err := mySeedPhrases.GetMySeedPhrase("Host")
if (err != nil) { return nil, err }
moderatorIdentityExists, _, err := mySeedPhrases.GetMySeedPhrase("Moderator")
if (err != nil) { return nil, err }
if (myIdentityType == "Mate" && hostIdentityExists == false && moderatorIdentityExists == false){
mateLabel := getBoldLabel("Mate")
return mateLabel, nil
}
getNextIdentityType := func()string{
if (myIdentityType == "Mate"){
if (hostIdentityExists == true){
return "Host"
}
return "Moderator"
}
if (myIdentityType == "Host"){
if (moderatorIdentityExists == true){
return "Moderator"
}
}
return "Mate"
}
nextIdentityType := getNextIdentityType()
changeIdentityTypeButton := widget.NewButton(myIdentityType, func(){
setDeleteMyIdentityPage(window, nextIdentityType, previousPage)
})
return changeIdentityTypeButton, nil
}
identityTypeLabelOrChangeButton, err := getIdentityTypeLabelOrChangeButton()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
identityTypeIcon, err := getIdentityTypeIcon(myIdentityType, -10)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
identityTypeLabelOrChangeButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, identityTypeIcon, identityTypeLabelOrChangeButton))
identityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(myIdentityType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (identityExists == false){
// This is only reached if the user has just deleted this identity
description4 := getBoldLabelCentered("Your " + myIdentityType + " identity does not exist.")
description5 := getLabelCentered("There is no identity to delete.")
page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionSection, widget.NewSeparator(), identityTypeLabelOrChangeButtonWithIcon, description4, description5, widget.NewSeparator())
setPageContent(page, window)
return
}
myIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(myIdentityHash)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
identityHashTrimmed, _, err := helpers.TrimAndFlattenString(myIdentityHashString, 10)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
currentIdentityHashText := widget.NewLabel("Current Identity:")
identityHashLabel := getBoldLabel(identityHashTrimmed)
myIdentityHashRow := getContainerCentered(container.NewHBox(currentIdentityHashText, identityHashLabel))
deleteIdentityButton := getWidgetCentered(widget.NewButtonWithIcon("Delete Identity", theme.DeleteIcon(), func(){
setConfirmDeleteMyIdentityPage(window, myIdentityType, currentPage, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), descriptionSection, widget.NewSeparator(), identityTypeLabelOrChangeButtonWithIcon, myIdentityHashRow, deleteIdentityButton, widget.NewSeparator())
setPageContent(page, window)
}
func setConfirmDeleteMyIdentityPage(window fyne.Window, myIdentityType string, previousPage func(), nextPage func()){
title := getPageTitleCentered("Confirm Delete " + myIdentityType + " Identity")
backButton := getBackButtonCentered(previousPage)
identityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash(myIdentityType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (identityExists == false){
setErrorEncounteredPage(window, errors.New("setConfirmDeleteMyIdentityPage called with missing identity."), previousPage)
return
}
subtitle := getPageSubtitleCentered("Delete your " + myIdentityType + " identity?")
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator())
if (myIdentityType == "Mate"){
description2 := getBoldLabelCentered(translate("You will lose access to your identity balance and account credit."))
description3 := getBoldLabelCentered(translate("This will delete your chat messages and decryption keys."))
description4 := getLabelCentered(translate("Write down your seed phrase to retain your identity and account credit."))
description5 := getLabelCentered(translate("Export your data to save your chat messages and decryption keys."))
page.Add(description2)
page.Add(description3)
page.Add(description4)
page.Add(description5)
} else if (myIdentityType == "Host"){
description2 := getBoldLabelCentered(translate("You will lose access to your identity balance and account credit."))
description3 := getLabelCentered(translate("Write down your seed phrase to retain your identity and account credit."))
page.Add(description2)
page.Add(description3)
} else if (myIdentityType == "Moderator"){
description2 := getBoldLabelCentered(translate("You will lose access to your identity score and account credit."))
description3 := getBoldLabelCentered(translate("This will delete your chat messages and decryption keys."))
description4 := getLabelCentered(translate("Write down your seed phrase to retain your identity and account credit."))
description5 := getLabelCentered(translate("Export your data to save your chat messages and decryption keys."))
page.Add(description2)
page.Add(description3)
page.Add(description4)
page.Add(description5)
}
description5 := getLabelCentered("This will not delete your local profile.")
page.Add(description5)
page.Add(widget.NewSeparator())
if (myIdentityType != "Host"){
//Outputs:
// -int: Number of messages to delete
// -int: Number of conversations to delete
getMessagesToDeleteInfo := func(networkType byte)(int, int, error){
updateProgressFunction := func(_ int)error{
return nil
}
myChatMessagesMapList, err := myChatMessages.GetUpdatedMyChatMessagesMapList(myIdentityType, networkType, updateProgressFunction)
if (err != nil) { return 0, 0, err }
numberOfMessagesToDelete := len(myChatMessagesMapList)
//Map Structure: Recipient Identity Hash -> Nothing
conversationRecipientsMap := make(map[string]struct{})
for _, messageMap := range myChatMessagesMapList{
theirIdentityHash, exists := messageMap["TheirIdentityHash"]
if (exists == false) {
return 0, 0, errors.New("Malformed myChatMessages map list: Item missing TheirIdentityHash")
}
conversationRecipientsMap[theirIdentityHash] = struct{}{}
}
numberOfConversationsToDelete := len(conversationRecipientsMap)
return numberOfMessagesToDelete, numberOfConversationsToDelete, nil
}
numberOfMessagesToDelete_Network1, numberOfConversationsToDelete_Network1, err := getMessagesToDeleteInfo(1)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfMessagesToDelete_Network2, numberOfConversationsToDelete_Network2, err := getMessagesToDeleteInfo(2)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfMessagesToDelete := numberOfMessagesToDelete_Network1 + numberOfMessagesToDelete_Network2
numberOfConversationsToDelete := numberOfConversationsToDelete_Network1 + numberOfConversationsToDelete_Network2
if (numberOfMessagesToDelete != 0){
thisWillDeleteLabel := widget.NewLabel("This will delete")
numberOfMessagesString := helpers.ConvertIntToString(numberOfMessagesToDelete)
numberOfMessagesLabel := getBoldLabel(numberOfMessagesString)
getMessageOrMessagesText := func()string{
if (numberOfMessagesToDelete == 1){
return "message"
}
return "messages"
}
messageOrMessagesText := getMessageOrMessagesText()
messagesAndLabel := widget.NewLabel(messageOrMessagesText + " and")
numberOfConversationsString := helpers.ConvertIntToString(numberOfConversationsToDelete)
numberOfConversationsLabel := getBoldLabel(numberOfConversationsString)
getConversationOrConversationsText := func()string{
if (numberOfConversationsToDelete == 1){
return "conversation"
}
return "conversations"
}
conversationOrConversationsText := getConversationOrConversationsText()
conversationsLabel := widget.NewLabel(conversationOrConversationsText)
chatDeleteWarningRow := container.NewHBox(layout.NewSpacer(), thisWillDeleteLabel, numberOfMessagesLabel, messagesAndLabel, numberOfConversationsLabel, conversationsLabel, layout.NewSpacer())
page.Add(chatDeleteWarningRow)
page.Add(widget.NewSeparator())
}
}
//TODO: Show account credit balance
if (myIdentityType == "Mate"){
description6 := getLabelCentered("Disable your profile before doing this on the Profile - Broadcast page.")
description7 := getLabelCentered("Otherwise, it will automatically expire from the network in 3 months.")
page.Add(description6)
page.Add(description7)
page.Add(widget.NewSeparator())
} else if (myIdentityType == "Host"){
description6 := getLabelCentered("Disable your profile before doing this on the Profile - Broadcast page.")
description7 := getLabelCentered("Otherwise, users will keep trying to connect to your inactive host address.")
//TODO: Retrieve true expiration time from parameters
description8 := getLabelCentered("This should stop after 6 hours, when your profile expires.")
page.Add(description6)
page.Add(description7)
page.Add(description8)
page.Add(widget.NewSeparator())
}
deleteIdentityFunction := func()error{
err := myBroadcasts.DeleteMyBroadcastProfiles(myIdentityHash)
if (err != nil) { return err }
if (myIdentityType != "Host"){
err := myChatMessages.DeleteMyChatMessagesMapList(myIdentityType)
if (err != nil) { return err }
err = mySettings.SetSetting(myIdentityType + "ChatConversationsGeneratedStatus", "No")
if (err != nil) { return err }
//TODO: Delete secret inboxes
//TODO: Delete saved message cipher keys
//TODO: Delete everything else relevant
}
err = mySeedPhrases.DeleteMySeedPhrase(myIdentityType)
if (err != nil) { return err }
return nil
}
confirmDeleteIdentityButton := getWidgetCentered(widget.NewButtonWithIcon("Delete Identity", theme.DeleteIcon(), func(){
err := deleteIdentityFunction()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
}
nextPage()
}))
page.Add(confirmDeleteIdentityButton)
setPageContent(page, window)
}
func setImportMyIdentityPage(window fyne.Window, myIdentityType string, previousPage func()){
if (myIdentityType != "Mate" && myIdentityType != "Host" && myIdentityType != "Moderator"){
setErrorEncounteredPage(window, errors.New("setImportMyIdentityPage called with invalid identity type: " + myIdentityType), previousPage)
return
}
currentPage := func(){setImportMyIdentityPage(window, myIdentityType, previousPage)}
title := getPageTitleCentered(translate("Import Identity"))
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("Import your " + myIdentityType + " identity from a seed phrase.")
getNextIdentityType := func()string{
if (myIdentityType == "Mate"){
return "Host"
}
if (myIdentityType == "Host"){
return "Moderator"
}
return "Mate"
}
nextIdentityType := getNextIdentityType()
identityTypeIcon, err := getIdentityTypeIcon(myIdentityType, -10)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
changeIdentityTypeButton := widget.NewButton(myIdentityType, func(){
setImportMyIdentityPage(window, nextIdentityType, previousPage)
})
changeIdentityTypeButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, identityTypeIcon, changeIdentityTypeButton))
identityExists, _, err := mySeedPhrases.GetMySeedPhrase(myIdentityType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (identityExists == true){
description2 := getBoldLabelCentered("Your " + myIdentityType + " identity exists.")
description3 := getLabelCentered("You must delete it before restoring a new identity.")
description4 := getLabelCentered("Delete your identity on the Settings - My Data - Delete Identity page")
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), changeIdentityTypeButtonWithIcon, description2, description3, description4)
setPageContent(page, window)
return
}
seedPhraseEntry := widget.NewMultiLineEntry()
seedPhraseEntry.Wrapping = 3
seedPhraseEntry.SetPlaceHolder(translate("Enter seed phrase to import..."))
seedPhraseEntryBoxed := getWidgetBoxed(seedPhraseEntry)
submitSeedButton := getWidgetCentered(widget.NewButtonWithIcon(translate("Import " + myIdentityType + " Identity"), theme.UploadIcon(), func(){
newSeedPhrase := seedPhraseEntry.Text
seedPhraseIsValid := seedPhrase.VerifySeedPhrase(newSeedPhrase)
if (seedPhraseIsValid == false){
dialogTitle := translate("Invalid Seed Phrase")
dialogMessageA := getLabelCentered("Your seed phrase is invalid.")
dialogMessageB := getLabelCentered("A Seekia seed phrase is 15 words long.")
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
err = mySeedPhrases.SetMySeedPhrase(myIdentityType, newSeedPhrase)
if (err != nil) {
setErrorEncounteredPage(window, err, currentPage)
return
}
setViewMySeedPhrasesPage(window, myIdentityType, previousPage)
}))
widener := widget.NewLabel(" ")
heightener := widget.NewLabel("")
submitSeedButtonWithWidener := container.NewVBox(submitSeedButton, widener, heightener)
entryWithSubmitSeedButton := getContainerCentered(container.NewGridWithColumns(1, seedPhraseEntryBoxed, submitSeedButtonWithWidener))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), changeIdentityTypeButtonWithIcon, entryWithSubmitSeedButton)
setPageContent(page, window)
}
func setChooseAppThemePage(window fyne.Window, previousPage func()){
currentPage := func(){setChooseAppThemePage(window, previousPage)}
title := getPageTitleCentered(translate("App Themes"))
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("Choose your Seekia app theme.")
getThemeButtonsGrid := func()(*fyne.Container, error){
getCurrentAppTheme := func()(string, error){
exists, currentTheme, err := globalSettings.GetSetting("AppTheme")
if (err != nil){ return "", err }
if (exists == false){
return "Light", nil
}
return currentTheme, nil
}
currentAppTheme, err := getCurrentAppTheme()
if (err != nil){ return nil, err }
getThemeButtonWithIcon := func(themeName string, themeIconName string)(*fyne.Container, error){
themeIcon, err := getFyneImageIcon(themeIconName)
if (err != nil) { return nil, err }
chooseThemeButton := widget.NewButton(translate(themeName), func(){
_ = globalSettings.SetSetting("AppTheme", themeName)
customTheme, _ := getCustomFyneTheme(themeName)
app := fyne.CurrentApp()
app.Settings().SetTheme(customTheme)
currentPage()
})
if (themeName == currentAppTheme){
chooseThemeButton.Importance = widget.HighImportance
}
buttonWithIcon := getContainerCentered(container.NewGridWithColumns(1, themeIcon, chooseThemeButton))
return buttonWithIcon, nil
}
lightThemeButton, err := getThemeButtonWithIcon("Light", "Sun")
if (err != nil) { return nil, err }
darkThemeButton, err := getThemeButtonWithIcon("Dark", "Moon")
if (err != nil) { return nil, err }
loveThemeButton, err := getThemeButtonWithIcon("Love", "Mate")
if (err != nil) { return nil, err }
oceanThemeButton, err := getThemeButtonWithIcon("Ocean", "Ocean")
if (err != nil) { return nil, err }
buttonsGrid := container.NewGridWithColumns(2, lightThemeButton, darkThemeButton, loveThemeButton, oceanThemeButton)
return buttonsGrid, nil
}
themeButtonsGrid, err := getThemeButtonsGrid()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
themeButtonsGridCentered := getContainerCentered(themeButtonsGrid)
//TODO: Add a way to create a custom theme within the GUI
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), themeButtonsGridCentered)
setPageContent(page, window)
}
func setNavigationSettingsPage(window fyne.Window, previousPage func()){
currentPage := func(){setNavigationSettingsPage(window, previousPage)}
title := getPageTitleCentered(translate("Navigation Settings"))
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("Manage your navigation settings.")
showButtonTextColumn := container.NewVBox()
showButtonChecksColumn := container.NewVBox()
addShowButtonCheckRow := func(showButtonText string, settingName string)error{
showButtonTextLabel := getBoldLabelCentered(showButtonText)
showButtonCheck := widget.NewCheck("", func(selected bool){
yesOrNoString := helpers.ConvertBoolToYesOrNoString(selected)
err := mySettings.SetSetting(settingName, yesOrNoString)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
})
exists, showButtonCurrent, err := mySettings.GetSetting(settingName)
if (err != nil) { return err }
if (exists == true && showButtonCurrent == "Yes"){
showButtonCheck.Checked = true
}
showButtonTextColumn.Add(showButtonTextLabel)
showButtonChecksColumn.Add(showButtonCheck)
return nil
}
err := addShowButtonCheckRow("Show Host Button", "ShowHostButtonNavigation")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
showButtonTextColumn.Add(widget.NewSeparator())
showButtonChecksColumn.Add(widget.NewSeparator())
err = addShowButtonCheckRow("Show Moderate Button", "ShowModerateButtonNavigation")
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
showButtonChecksGrid := container.NewHBox(layout.NewSpacer(), showButtonTextColumn, showButtonChecksColumn, layout.NewSpacer())
navigationLocationLabel := getLabelCentered("Navigation Bar Location:")
getMyCurrentNavigationBarLocation := func()(string, error){
exists, currentNavigationLocation, err := mySettings.GetSetting("NavigationBarLocation")
if (err != nil){ return "", err }
if (exists == false){
return "Top", nil
}
if (currentNavigationLocation != "Top" && currentNavigationLocation != "Bottom" && currentNavigationLocation != "Left" && currentNavigationLocation != "Right"){
return "", errors.New("Invalid navigation bar location: " + currentNavigationLocation)
}
return currentNavigationLocation, nil
}
currentNavigationLocation, err := getMyCurrentNavigationBarLocation()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
option1Translated := translate("Top")
option2Translated := translate("Bottom")
option3Translated := translate("Left")
option4Translated := translate("Right")
untranslatedOptionsMap := map[string]string{
option1Translated: "Top",
option2Translated: "Bottom",
option3Translated: "Left",
option4Translated: "Right",
}
locationOptionsList := []string{option1Translated, option2Translated, option3Translated, option4Translated}
locationSelector := widget.NewSelect(locationOptionsList, func(response string){
responseUntranslated, exists := untranslatedOptionsMap[response]
if (exists == false){
setErrorEncounteredPage(window, errors.New("untranslatedOptionsMap missing response: " + response), currentPage)
return
}
if (responseUntranslated == currentNavigationLocation){
return
}
err = mySettings.SetSetting("NavigationBarLocation", responseUntranslated)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
})
locationSelector.Selected = currentNavigationLocation
locationSelectorCentered := getWidgetCentered(locationSelector)
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), showButtonChecksGrid, widget.NewSeparator(), navigationLocationLabel, locationSelectorCentered)
setPageContent(page, window)
}
func setContentFilteringSettingsPage(window fyne.Window, previousPage func()){
currentPage := func(){setContentFilteringSettingsPage(window, previousPage)}
title := getPageTitleCentered(translate("Content Filtering Settings"))
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("Manage your content filtering settings.")
pixelateImagesButton := getWidgetCentered(widget.NewButton("Pixelate Images", func(){
setManagePixelateImagesSettingPage(window, currentPage)
}))
// TODO: Add ability to mute/block/censor words?
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), pixelateImagesButton)
setPageContent(page, window)
}
func setManagePixelateImagesSettingPage(window fyne.Window, previousPage func()){
currentPage := func(){setManagePixelateImagesSettingPage(window, previousPage)}
title := getPageTitleCentered("Pixelate Images Setting")
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Seekia can pixelate the images you receive in messages.")
description2 := getLabelCentered("You must slowly reveal the image by incrementally depixelating the image.")
description3 := getLabelCentered("If you are not afraid of seeing unreviewed images, you can disable this feature.")
getCurrentStatus := func()(string, error){
exists, currentSetting, err := mySettings.GetSetting("PixelateImagesOnOffStatus")
if (err != nil) { return "", err }
if (exists == false){
return "On", nil
}
return currentSetting, nil
}
currentStatus, err := getCurrentStatus()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
currentStatusTitle := widget.NewLabel("Current Status: ")
currentStatusLabel := getBoldLabel(currentStatus)
currentStatusRow := container.NewHBox(layout.NewSpacer(), currentStatusTitle, currentStatusLabel, layout.NewSpacer())
getEnableOrDisableButton := func()fyne.Widget{
if (currentStatus == "On"){
disableButton := widget.NewButton("Disable", func(){
err := mySettings.SetSetting("PixelateImagesOnOffStatus", "Off")
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
})
return disableButton
}
enableButton := widget.NewButton("Enable", func(){
err := mySettings.SetSetting("PixelateImagesOnOffStatus", "On")
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
})
return enableButton
}
enableOrDisableButton := getEnableOrDisableButton()
enableOrDisableButtonCentered := getWidgetCentered(enableOrDisableButton)
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), currentStatusRow, enableOrDisableButtonCentered)
setPageContent(page, window)
}
func setManageNetworkSettingsPage(window fyne.Window, previousPage func()){
appMemory.SetMemoryEntry("CurrentViewedPage", "ManageNetworkConnection")
title := getPageTitleCentered("Manage Network Connection")
//TODO
// Enable download profiles over clearnet mode
// This should not be possible for messages, only profiles
// Disable Tor mode (if using system-wide tor proxy already, such as Whonix. We can autodetect if user is using Whonix)
// Add HiddenServiceOnly mode so users can only download messages over .onion rather than using exit nodes to access clearnet hosts?
backButton := getBackButtonCentered(previousPage)
description := getBoldLabelCentered("Under Construction")
page := container.NewVBox(title, backButton, widget.NewSeparator(), description)
setPageContent(page, window)
}
func setManageStoragePage(window fyne.Window, previousPage func()){
setLoadingScreen(window, "Manage Storage", "Loading Manage Storage Page...")
currentPage := func(){setManageStoragePage(window, previousPage)}
title := getPageTitleCentered(translate("Manage Storage"))
backButton := getBackButtonCentered(previousPage)
getAllowedStorageGigabytes := func()(float64, error){
exists, allowedStorageSpaceString, err := globalSettings.GetSetting("AllowedStorageSpace")
if (err != nil){ return 0, err }
if (exists == false){
//We default to 10 gigabytes
return 10, nil
}
allowedStorageSpaceFloat, err := helpers.ConvertStringToFloat64(allowedStorageSpaceString)
if (err != nil){
return 0, errors.New("MySettings contains invalid AllowedStorageSpace: " + allowedStorageSpaceString)
}
return allowedStorageSpaceFloat, nil
}
allowedStorageSpace, err := getAllowedStorageGigabytes()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
databaseDirectory, err := localFilesystem.GetAppDatabaseFolderPath()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
databaseSizeBytes, err := localFilesystem.GetFolderSizeInBytes(databaseDirectory)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
// TODO: Change to use actual gigabyte size
databaseSizeGigabytes := float64(databaseSizeBytes)/1000000000
allowedStorageSpaceString := helpers.ConvertFloat64ToStringRounded(allowedStorageSpace, 2)
databaseSizeString := helpers.ConvertFloat64ToStringRounded(databaseSizeGigabytes, 1)
percentageOfSpaceUsed := (databaseSizeGigabytes/allowedStorageSpace)*100
percentageOfSpaceUsedString := helpers.ConvertFloat64ToStringRounded(percentageOfSpaceUsed, 0)
spaceUsedTitle := getLabelCentered("Storage Space Used:")
spaceUsedLabel := getBoldLabelCentered(databaseSizeString + "/" + allowedStorageSpaceString + " gigabytes" + " (" + percentageOfSpaceUsedString + "%)")
numberOfStoredProfiles, err := profileStorage.GetNumberOfStoredProfiles()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfStoredProfilesString := helpers.ConvertInt64ToString(numberOfStoredProfiles)
numberOfStoredProfilesLabel := widget.NewLabel("Stored Profiles:")
numberOfStoredProfilesText := getBoldLabel(numberOfStoredProfilesString)
numberOfStoredProfilesRow := container.NewHBox(layout.NewSpacer(), numberOfStoredProfilesLabel, numberOfStoredProfilesText, layout.NewSpacer())
numberOfStoredMessages, err := chatMessageStorage.GetNumberOfStoredMessages()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfStoredMessagesString := helpers.ConvertInt64ToString(numberOfStoredMessages)
numberOfStoredMessagesLabel := widget.NewLabel("Stored Messages:")
numberOfStoredMessagesText := getBoldLabel(numberOfStoredMessagesString)
numberOfStoredMessagesRow := container.NewHBox(layout.NewSpacer(), numberOfStoredMessagesLabel, numberOfStoredMessagesText, layout.NewSpacer())
numberOfStoredReviews, err := reviewStorage.GetNumberOfStoredReviews()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfStoredReviewsString := helpers.ConvertInt64ToString(numberOfStoredReviews)
numberOfStoredReviewsLabel := widget.NewLabel("Stored Reviews:")
numberOfStoredReviewsText := getBoldLabel(numberOfStoredReviewsString)
numberOfStoredReviewsRow := container.NewHBox(layout.NewSpacer(), numberOfStoredReviewsLabel, numberOfStoredReviewsText, layout.NewSpacer())
numberOfStoredReports, err := reportStorage.GetNumberOfStoredReports()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
numberOfStoredReportsString := helpers.ConvertInt64ToString(numberOfStoredReports)
numberOfStoredReportsLabel := widget.NewLabel("Stored Reports:")
numberOfStoredReportsText := getBoldLabel(numberOfStoredReportsString)
numberOfStoredReportsRow := container.NewHBox(layout.NewSpacer(), numberOfStoredReportsLabel, numberOfStoredReportsText, layout.NewSpacer())
freeUpSpaceButton := widget.NewButton("Free Up Space", func(){
//TODO: A page to manually prune data
// This should also be happening automatically by backgroundJobs
showUnderConstructionDialog(window)
})
allowedSpaceButton := widget.NewButton("Manage Allowed Space", func(){
setManageAllowedStorageSpacePage(window, currentPage)
})
changeDownloadsDirectoryButton := widget.NewButton("Change Database Location", func(){
setManageDatabaseLocationPage(window, currentPage)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, freeUpSpaceButton, allowedSpaceButton, changeDownloadsDirectoryButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), spaceUsedTitle, spaceUsedLabel, widget.NewSeparator(), numberOfStoredProfilesRow, numberOfStoredMessagesRow, numberOfStoredReviewsRow, numberOfStoredReportsRow, widget.NewSeparator(), buttonsGrid)
setPageContent(page, window)
}
func setManageAllowedStorageSpacePage(window fyne.Window, previousPage func()){
currentPage := func(){setManageAllowedStorageSpacePage(window, previousPage)}
title := getPageTitleCentered(translate("Manage Allowed Storage Space"))
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("Select the amount of storage space Seekia is allowed to use.")
getCurrentAllowedSpace := func()(float64, error){
exists, currentAllowedStorageSpace, err := globalSettings.GetSetting("AllowedStorageSpace")
if (err != nil){ return 0, err }
if (exists == false){
// Default to 10 GB
return 10, nil
}
currentAllowedSpaceFloat64, err := helpers.ConvertStringToFloat64(currentAllowedStorageSpace)
if (err != nil) {
return 0, errors.New("MyGlobalSettings Malformed: Contains invalid AllowedStorageSpace: " + currentAllowedStorageSpace)
}
return currentAllowedSpaceFloat64, nil
}
currentAllowedSpaceFloat64, err := getCurrentAllowedSpace()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
currentAllowedSpaceString := helpers.ConvertFloat64ToStringRounded(currentAllowedSpaceFloat64, 2)
allowedStorageSpaceLabel := getLabelCentered("Allowed storage space:")
allowedStorageSpaceText := getBoldLabelCentered(currentAllowedSpaceString + " gigabytes")
allowedStorageSpaceGigabytesText := getLabelCentered("Enter a new allowed amount in gigabytes:")
allowedStorageSpaceEntry := widget.NewEntry()
allowedStorageSpaceEntry.Text = currentAllowedSpaceString
allowedStorageSpaceSubmitButton := widget.NewButtonWithIcon("Submit", theme.ConfirmIcon(), func(){
newSize := allowedStorageSpaceEntry.Text
if (newSize == ""){
dialogTitle := translate("Invalid Size.")
dialogMessage := getLabelCentered(translate("Your must enter a new allowed amount."))
dialogContent := container.NewVBox(dialogMessage)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
newSizeFloat64, err := helpers.ConvertStringToFloat64(newSize)
if (err != nil){
dialogTitle := translate("Invalid Size.")
dialogMessage := getLabelCentered(translate("Your new maximum size must be a number."))
dialogContent := container.NewVBox(dialogMessage)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
if (newSizeFloat64 < 3){
dialogTitle := translate("Invalid Size.")
dialogMessage := getLabelCentered(translate("Your new maximum size must be at least 3 GB."))
dialogContent := container.NewVBox(dialogMessage)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
err = globalSettings.SetSetting("AllowedStorageSpace", newSize)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
})
allowedStorageSpaceEntryRow := getContainerCentered(container.NewGridWithRows(1, allowedStorageSpaceEntry, allowedStorageSpaceSubmitButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), allowedStorageSpaceLabel, allowedStorageSpaceText, widget.NewSeparator(), allowedStorageSpaceGigabytesText, allowedStorageSpaceEntryRow)
setPageContent(page, window)
}
func setManageDatabaseLocationPage(window fyne.Window, previousPage func()){
currentPage := func(){setManageDatabaseLocationPage(window, previousPage)}
title := getPageTitleCentered(translate("Manage Database Location"))
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Use this page to change the location of the Seekia database.")
description2 := getLabelCentered("After changing the location, close Seekia.")
description3 := getLabelCentered("Then, you must manually move the existing database folder contents to the new location.")
description4 := getLabelCentered("Upon starting Seekia again, all existing data should be available.")
description5 := getLabelCentered("If you don't copy the folder, you will still retain your user data.")
databaseDirectory, err := localFilesystem.GetAppDatabaseFolderPath()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
currentLocationLabel := getLabelCentered("Current Database Directory:")
currentLocationText := getBoldLabelCentered(databaseDirectory)
resetLocation := widget.NewButtonWithIcon("Reset To Original", theme.ContentUndoIcon(), func(){
confirmDialogCallbackFunction := func(response bool){
if (response == false){
return
}
err := globalSettings.DeleteSetting("DatabaseFolderpath")
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
}
dialogTitle := translate("Confirm Reset Database Location?")
dialogMessageA := getLabelCentered("Confirm to reset the database location?")
dialogMessageB := getLabelCentered("You must restart Seekia for the change to take effect.")
dialogMessageC := getLabelCentered("Move your existing database to the new location before starting Seekia again.")
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
dialog.ShowCustomConfirm(dialogTitle, translate("Yes"), translate("No"), dialogContent, confirmDialogCallbackFunction, window)
})
selectNewLocationButton := widget.NewButtonWithIcon("Select New Location", theme.FolderIcon(), func(){
folderOpenCallbackFunction := func(folderObject fyne.ListableURI, err error){
if (err != nil) {
title := translate("Failed To Open Folder Path.")
dialogMessage := getLabelCentered(translate("Report this error to Seekia developers: " + err.Error()))
dialogContent := container.NewVBox(dialogMessage)
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
return
}
if (folderObject == nil){
return
}
folderPath := folderObject.Path()
err = globalSettings.SetSetting("DatabaseFolderpath", folderPath)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
}
dialog.ShowFolderOpen(folderOpenCallbackFunction, window)
})
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, resetLocation, selectNewLocationButton))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, description5, widget.NewSeparator(), currentLocationLabel, currentLocationText, widget.NewSeparator(), buttonsGrid)
setPageContent(page, window)
}
/*
func setSyncSettingsPage(window fyne.Window, previousPage func()){
title := getPageTitleCentered(translate("Sync Settings"))
backButton := getBackButtonCentered(previousPage)
underConstructionLabel := getBoldLabelCentered("Under construction.")
//TODO
page := container.NewVBox(title, backButton, widget.NewSeparator(), underConstructionLabel)
setPageContent(page, window)
}
*/
func setChangeAppCurrencyPage(window fyne.Window, previousPage func()){
getCurrentCurrencyFunction := func()(string, error){
exists, currentAppCurrency, err := globalSettings.GetSetting("Currency")
if (err != nil) { return "", err }
if (exists == false){
return "USD", nil
}
return currentAppCurrency, nil
}
onSelectFunction := func(newCurrencyCode string)error{
err := globalSettings.SetSetting("Currency", newCurrencyCode)
if (err != nil){ return err }
return nil
}
setChooseCurrencyPage(window, getCurrentCurrencyFunction, onSelectFunction, previousPage)
}
func setChooseCurrencyPage(window fyne.Window, getCurrentCurrencyFunction func()(string, error), onSelectFunction func(string)error, previousPage func()){
currentPage := func(){setChooseCurrencyPage(window, getCurrentCurrencyFunction, onSelectFunction, previousPage)}
title := getPageTitleCentered(translate("Choose Currency"))
backButton := getBackButtonCentered(previousPage)
currentCurrencyCode, err := getCurrentCurrencyFunction()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
currentCurrencyName, currentCurrencySymbol, err := currencies.GetCurrencyInfoFromCurrencyCode(currentCurrencyCode)
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
currentCurrencyLabel := getLabelCentered("Current Currency:")
currentCurrencyText := getBoldLabelCentered(currentCurrencySymbol + " - " + currentCurrencyName + " - " + currentCurrencyCode)
chooseCurrencyLabel := getItalicLabelCentered("Choose Currency:")
allCurrencyObjectsList, err := currencies.GetCurrencyObjectsList()
if (err != nil) {
setErrorEncounteredPage(window, err, previousPage)
return
}
allCurrencyDescriptionsList := make([]string, 0, len(allCurrencyObjectsList))
allCurrencyCodesList := make([]string, 0, len(allCurrencyObjectsList))
for _, currencyObject := range allCurrencyObjectsList{
currencyName := currencyObject.Name
currencySymbol := currencyObject.Symbol
currencyCode := currencyObject.Code
currencyNameTranslated := translate(currencyName)
currencyDescription := currencySymbol + " - " + currencyNameTranslated + " - " + currencyCode
allCurrencyDescriptionsList = append(allCurrencyDescriptionsList, currencyDescription)
allCurrencyCodesList = append(allCurrencyCodesList, currencyCode)
}
onSelectedFunction := func(currencyIndex int) {
selectedCurrencyCode := allCurrencyCodesList[currencyIndex]
err := onSelectFunction(selectedCurrencyCode)
if (err != nil){
setErrorEncounteredPage(window, err, currentPage)
return
}
currentPage()
}
widgetList, err := getFyneWidgetListFromStringList(allCurrencyDescriptionsList, onSelectedFunction)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
header := container.NewVBox(title, backButton, widget.NewSeparator(), currentCurrencyLabel, currentCurrencyText, widget.NewSeparator(), chooseCurrencyLabel, widget.NewSeparator())
page := container.NewBorder(header, nil, nil, nil, widgetList)
setPageContent(page, window)
}
func setViewLogsPage(window fyne.Window, logType string, previousPage func()){
if (logType != "General" && logType != "Network" && logType != "BackgroundJobs"){
setErrorEncounteredPage(window, errors.New("setViewLogsPage called with invalid logType: " + logType), previousPage)
return
}
currentPage := func(){setViewLogsPage(window, logType, previousPage)}
title := getPageTitleCentered("Logs")
backButton := getBackButtonCentered(previousPage)
description := getLabelCentered("View Seekia logs.")
logTypeLabel := getBoldLabelCentered("Log Type:")
logTypesList := []string{translate("General"), translate("Network"), translate("Background Jobs")}
logTypeSelector := widget.NewSelect(logTypesList, func(selection string){
if (selection == translate("General")){
setViewLogsPage(window, "General", previousPage)
} else if (selection == translate("Network")){
setViewLogsPage(window, "Network", previousPage)
} else if (selection == translate("Background Jobs")){
setViewLogsPage(window, "BackgroundJobs", previousPage)
}
})
if (logType == "General"){
logTypeSelector.Selected = translate("General")
} else if (logType == "Network"){
logTypeSelector.Selected = translate("Network")
} else if (logType == "BackgroundJobs"){
logTypeSelector.Selected = translate("Background Jobs")
}
logTypeSelectorCentered := getWidgetCentered(logTypeSelector)
logList, err := logger.GetLogList(logType)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
if (len(logList) == 0){
noLogsExistLabel := getBoldLabelCentered("No log entries exist.")
refreshButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh", theme.ViewRefreshIcon(), currentPage))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), logTypeLabel, logTypeSelectorCentered, widget.NewSeparator(), noLogsExistLabel, refreshButton)
setPageContent(page, window)
return
}
trimmedLogList := make([]string, 0, len(logList))
for _, logText := range logList{
trimmedText, _, err := helpers.TrimAndFlattenString(logText, 30)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
trimmedLogList = append(trimmedLogList, trimmedText)
}
onClickedFunction := func(selectedIndex int){
logText := logList[selectedIndex]
setViewTextPage(window, "Viewing Log", logText, false, currentPage)
}
logsWidgetList, err := getFyneWidgetListFromStringList(logList, onClickedFunction)
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
refreshButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh", theme.ViewRefreshIcon(), currentPage))
header := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), logTypeLabel, logTypeSelectorCentered, widget.NewSeparator())
page := container.NewBorder(header, refreshButton, nil, nil, logsWidgetList)
setPageContent(page, window)
}
func setViewDeveloperSettingsPage(window fyne.Window, previousPage func()){
currentPage := func(){setViewDeveloperSettingsPage(window, previousPage)}
title := getPageTitleCentered("Developer Settings")
backButton := getBackButtonCentered(previousPage)
networkTypeButton := getWidgetCentered(widget.NewButton("Network Type", func(){
setViewAppNetworkTypePage(window, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), networkTypeButton)
setPageContent(page, window)
}
func setViewAppNetworkTypePage(window fyne.Window, previousPage func()){
currentPage := func(){setViewAppNetworkTypePage(window, previousPage)}
title := getPageTitleCentered("Network Type")
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Your Seekia application can interact with different network types.")
description2 := getLabelCentered("There are 2 network types: Mainnet and Testnet 1.")
description3 := getLabelCentered("You can change your app network type to interact with a different network.")
description4 := getLabelCentered("You should use Testnet 1 when testing new releases of Seekia before they are versioned.")
currentNetworkType, err := getAppNetworkType.GetAppNetworkType()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
getCurrentNetworkTypeString := func()(string, error){
if (currentNetworkType == 1){
return "Mainnet", nil
}
if (currentNetworkType == 2){
return "Testnet 1", nil
}
currentNetworkTypeString := helpers.ConvertByteToString(currentNetworkType)
return "", errors.New("GetAppNetworkType returning invalid network type: " + currentNetworkTypeString)
}
currentNetworkTypeString, err := getCurrentNetworkTypeString()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
currentNetworkTypeLabel := widget.NewLabel("Current Network Type:")
currentNetworkTypeText := getBoldLabel(currentNetworkTypeString)
currentNetworkTypeRow := container.NewHBox(layout.NewSpacer(), currentNetworkTypeLabel, currentNetworkTypeText, layout.NewSpacer())
changeNetworkTypeButton := getWidgetCentered(widget.NewButtonWithIcon("Change Network Type", theme.NavigateNextIcon(), func(){
setChooseNewAppNetworkTypePage(window, currentPage)
}))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, widget.NewSeparator(), currentNetworkTypeRow, widget.NewSeparator(), changeNetworkTypeButton)
setPageContent(page, window)
}
func setChooseNewAppNetworkTypePage(window fyne.Window, previousPage func()){
title := getPageTitleCentered("Choose Network Type")
backButton := getBackButtonCentered(previousPage)
description1 := getLabelCentered("Choose your new network type.")
description2 := getLabelCentered("This change will impact your matches and chat conversations.")
description3 := getLabelCentered("Your client will automatically delete content for other network types from the database.")
description4 := getLabelCentered("Your broadcasted content and messages will not be deleted.")
description5 := getLabelCentered("This change will take effect for all of your app users.")
currentNetworkType, err := getAppNetworkType.GetAppNetworkType()
if (err != nil){
setErrorEncounteredPage(window, err, previousPage)
return
}
chooseMainnetButton := widget.NewButton("Mainnet", func(){
if (currentNetworkType == 1){
dialogTitle := translate("Cannot Change Network Type")
dialogMessageA := getLabelCentered("This network type is already your current app network type.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
setChangeAppNetworkTypePage(window, 1, previousPage)
})
if (currentNetworkType == 1){
chooseMainnetButton.Importance = widget.HighImportance
}
chooseTestnet1Button := widget.NewButton("Testnet 1", func(){
if (currentNetworkType == 2){
dialogTitle := translate("Cannot Change Network Type")
dialogMessageA := getLabelCentered("This network type is already your current app network type.")
dialogContent := container.NewVBox(dialogMessageA)
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
return
}
setChangeAppNetworkTypePage(window, 2, previousPage)
})
if (currentNetworkType == 2){
chooseTestnet1Button.Importance = widget.HighImportance
}
chooseNetworkTypesGrid := getContainerCentered(container.NewGridWithColumns(1, chooseMainnetButton, chooseTestnet1Button))
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, description5, widget.NewSeparator(), chooseNetworkTypesGrid)
setPageContent(page, window)
}
func setChangeAppNetworkTypePage(window fyne.Window, newAppNetworkType byte, nextPage func()){
isValid := helpers.VerifyNetworkType(newAppNetworkType)
if (isValid == false){
newAppNetworkTypeString := helpers.ConvertByteToString(newAppNetworkType)
setErrorEncounteredPage(window, errors.New("setChangeAppNetworkTypePage called with invalid newAppNetworkType: " + newAppNetworkTypeString), nextPage)
return
}
title := getPageTitleCentered("Changing Network Type")
description := getLabelCentered("Changing network type...")
progressBar := getWidgetCentered(widget.NewProgressBarInfinite())
page := container.NewVBox(title, widget.NewSeparator(), description, progressBar)
window.SetContent(page)
err := setAppNetworkType.SetAppNetworkType(newAppNetworkType)
if (err != nil){
setErrorEncounteredPage(window, err, nextPage)
return
}
nextPage()
}