3623 lines
128 KiB
Go
3623 lines
128 KiB
Go
|
package gui
|
||
|
|
||
|
// buildProfileGui_General.go impements pages to build the general portion of a user profile
|
||
|
// Some attributes support multiple profileTypes, most attributes are only used for Mate profiles
|
||
|
|
||
|
import "fyne.io/fyne/v2"
|
||
|
import "fyne.io/fyne/v2/canvas"
|
||
|
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/worldLanguages"
|
||
|
import "seekia/resources/worldLocations"
|
||
|
import "seekia/resources/imageFiles"
|
||
|
|
||
|
import "seekia/internal/allowedText"
|
||
|
import "seekia/internal/globalSettings"
|
||
|
import "seekia/internal/helpers"
|
||
|
import "seekia/internal/imagery"
|
||
|
import "seekia/internal/mateQuestionnaire"
|
||
|
import "seekia/internal/profiles/myLocalProfiles"
|
||
|
|
||
|
import "strings"
|
||
|
import "errors"
|
||
|
import "image"
|
||
|
import "reflect"
|
||
|
import "slices"
|
||
|
|
||
|
|
||
|
func setBuildMyMateProfilePage(window fyne.Window, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMyMateProfilePage(window, previousPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
description1 := getLabelCentered("Build your Mate profile.")
|
||
|
description2 := getLabelCentered("All information is optional.")
|
||
|
|
||
|
generalIcon, err := getFyneImageIcon("General")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
physicalIcon, err := getFyneImageIcon("Person")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
lifestyleIcon, err := getFyneImageIcon("Lifestyle")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
mentalIcon, err := getFyneImageIcon("Mental")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
generalButton := widget.NewButton(translate("General"), func(){
|
||
|
setBuildMateProfileCategoryPage_General(window, currentPage)
|
||
|
})
|
||
|
generalButtonWithIcon := container.NewGridWithRows(2, generalIcon, generalButton)
|
||
|
|
||
|
physicalButton := widget.NewButton(translate("Physical"), func(){
|
||
|
setBuildMateProfileCategoryPage_Physical(window, currentPage)
|
||
|
})
|
||
|
physicalButtonWithIcon := container.NewGridWithRows(2, physicalIcon, physicalButton)
|
||
|
|
||
|
lifestyleButton := widget.NewButton(translate("Lifestyle"), func(){
|
||
|
setBuildMateProfileCategoryPage_Lifestyle(window, currentPage)
|
||
|
})
|
||
|
lifestyleButtonWithIcon := container.NewGridWithRows(2, lifestyleIcon, lifestyleButton)
|
||
|
|
||
|
mentalButton := widget.NewButton(translate("Mental"), func(){
|
||
|
setBuildMateProfileCategoryPage_Mental(window, currentPage)
|
||
|
})
|
||
|
mentalButtonWithIcon := container.NewGridWithRows(2, mentalIcon, mentalButton)
|
||
|
|
||
|
categoriesRow := container.NewGridWithRows(1, generalButtonWithIcon, physicalButtonWithIcon, lifestyleButtonWithIcon, mentalButtonWithIcon)
|
||
|
|
||
|
categoriesRowCentered := getContainerCentered(categoriesRow)
|
||
|
|
||
|
categoriesRowPadded := container.NewPadded(categoriesRowCentered)
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), categoriesRowPadded)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildMateProfileCategoryPage_General(window fyne.Window, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMateProfileCategoryPage_General(window, previousPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
profileLanguageButton := widget.NewButton(translate("Profile Language"), func(){
|
||
|
setBuildProfilePage_ProfileLanguage(window, "Mate", currentPage)
|
||
|
})
|
||
|
|
||
|
usernameButton := widget.NewButton(translate("Username"), func(){
|
||
|
setBuildProfilePage_Username(window, "Mate", currentPage)
|
||
|
})
|
||
|
|
||
|
locationButton := widget.NewButton(translate("Location"), func(){
|
||
|
setBuildMateProfilePage_Location(window, currentPage)
|
||
|
})
|
||
|
|
||
|
descriptionButton := widget.NewButton(translate("Description"), func(){
|
||
|
setBuildProfilePage_Description(window, "Mate", currentPage)
|
||
|
})
|
||
|
|
||
|
photosButton := widget.NewButton(translate("Photos"), func(){
|
||
|
setBuildMateProfilePage_Photos(window, 0, currentPage)
|
||
|
})
|
||
|
|
||
|
avatarButton := widget.NewButton(translate("Avatar"), func(){
|
||
|
setBuildProfilePage_Avatar(window, "Mate", currentPage)
|
||
|
})
|
||
|
|
||
|
sexualityButton := widget.NewButton(translate("Sexuality"), func(){
|
||
|
setBuildMateProfilePage_Sexuality(window, currentPage)
|
||
|
})
|
||
|
|
||
|
tagsButton := widget.NewButton(translate("Tags"), func(){
|
||
|
setBuildMateProfilePage_Tags(window, currentPage)
|
||
|
})
|
||
|
|
||
|
questionnaireButton := widget.NewButton(translate("Questionnaire"), func(){
|
||
|
setBuildMateProfilePage_Questionnaire(window, currentPage)
|
||
|
})
|
||
|
|
||
|
buttonsGrid := container.NewGridWithColumns(1, profileLanguageButton, usernameButton, locationButton, descriptionButton, photosButton, avatarButton, sexualityButton, tagsButton, questionnaireButton)
|
||
|
|
||
|
buttonsGridCentered := getContainerCentered(buttonsGrid)
|
||
|
|
||
|
buttonsGridPadded := container.NewPadded(buttonsGridCentered)
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), buttonsGridPadded)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildProfilePage_ProfileLanguage(window fyne.Window, profileType string, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildProfilePage_ProfileLanguage(window, profileType, previousPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build " + profileType + " Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getPageSubtitleCentered(translate("Profile Language"))
|
||
|
|
||
|
description1 := getLabelCentered("Choose your profile language.")
|
||
|
description2 := getLabelCentered("This is the language that your profile is written in.")
|
||
|
description3 := getLabelCentered("For example, select English if your description/hobbies/... are written in English.")
|
||
|
|
||
|
currentLanguageExists, currentLanguageIdentifier, err := myLocalProfiles.GetProfileData(profileType, "ProfileLanguage")
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
getCurrentLanguageLabel := func()(fyne.Widget, error){
|
||
|
|
||
|
if (currentLanguageExists == false){
|
||
|
|
||
|
noResponseLabel := getBoldItalicLabel("No Response")
|
||
|
|
||
|
return noResponseLabel, nil
|
||
|
}
|
||
|
|
||
|
currentLanguageIdentifierInt, err := helpers.ConvertStringToInt(currentLanguageIdentifier)
|
||
|
if (err != nil){
|
||
|
return nil, errors.New("MyLocalProfile is malformed: Contains invalid ProfileLanguage: " + currentLanguageIdentifier)
|
||
|
}
|
||
|
|
||
|
currentLanguageObject, err := worldLanguages.GetLanguageObjectFromLanguageIdentifier(currentLanguageIdentifierInt)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
currentLanguageNamesList := currentLanguageObject.NamesList
|
||
|
|
||
|
languageDescription := helpers.TranslateAndJoinStringListItems(currentLanguageNamesList, "/")
|
||
|
|
||
|
languageNamesLabel := getBoldLabel(languageDescription)
|
||
|
|
||
|
return languageNamesLabel, nil
|
||
|
}
|
||
|
|
||
|
currentLanguageLabel, err := getCurrentLanguageLabel()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
myProfileLanguageLabel := widget.NewLabel("My Profile Language:")
|
||
|
|
||
|
myProfileLanguageRow := container.NewHBox(layout.NewSpacer(), myProfileLanguageLabel, currentLanguageLabel, layout.NewSpacer())
|
||
|
|
||
|
getChooseOrEditButtonText := func()string{
|
||
|
if (currentLanguageExists == false){
|
||
|
result := translate("Choose Language")
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
result := translate("Edit Language")
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
chooseOrEditButtonText := getChooseOrEditButtonText()
|
||
|
|
||
|
chooseOrEditLanguageButton := widget.NewButtonWithIcon(chooseOrEditButtonText, theme.DocumentCreateIcon(), func(){
|
||
|
setBuildProfilePage_ChooseProfileLanguage(window, profileType, currentPage, currentPage)
|
||
|
})
|
||
|
|
||
|
noResponseButton := widget.NewButtonWithIcon("No Response", theme.CancelIcon(), func(){
|
||
|
err := myLocalProfiles.DeleteProfileData(profileType, "ProfileLanguage")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
currentPage()
|
||
|
})
|
||
|
|
||
|
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, chooseOrEditLanguageButton, noResponseButton))
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), myProfileLanguageRow, widget.NewSeparator(), buttonsGrid)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildProfilePage_ChooseProfileLanguage(window fyne.Window, profileType string, previousPage func(), nextPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildProfilePage_ChooseProfileLanguage(window, profileType, previousPage, nextPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getPageSubtitleCentered("Choose Language")
|
||
|
|
||
|
description := getLabelCentered("Choose your profile language.")
|
||
|
|
||
|
worldLanguageObjectsList, err := worldLanguages.GetWorldLanguageObjectsList()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// This list stores the translated language names
|
||
|
worldLanguageDescriptionsList := make([]string, 0, len(worldLanguageObjectsList))
|
||
|
|
||
|
// This map will store the language identifiers
|
||
|
//Map Structure: Language Description -> Language identifier
|
||
|
worldLanguageIdentifiersMap := make(map[string]int)
|
||
|
|
||
|
for _, languageObject := range worldLanguageObjectsList{
|
||
|
|
||
|
languageIdentifier := languageObject.Identifier
|
||
|
languageNamesList := languageObject.NamesList
|
||
|
|
||
|
languageDescription := helpers.TranslateAndJoinStringListItems(languageNamesList, "/")
|
||
|
|
||
|
worldLanguageDescriptionsList = append(worldLanguageDescriptionsList, languageDescription)
|
||
|
|
||
|
worldLanguageIdentifiersMap[languageDescription] = languageIdentifier
|
||
|
}
|
||
|
|
||
|
helpers.SortStringListToUnicodeOrder(worldLanguageDescriptionsList)
|
||
|
|
||
|
onSelectedFunction := func(itemIndex int){
|
||
|
|
||
|
languageDescription := worldLanguageDescriptionsList[itemIndex]
|
||
|
|
||
|
languageIdentifier, exists := worldLanguageIdentifiersMap[languageDescription]
|
||
|
if (exists == false){
|
||
|
setErrorEncounteredPage(window, errors.New("worldLanguageIdentifiersMap missing languageDescription: " + languageDescription), currentPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
languageIdentifierString := helpers.ConvertIntToString(languageIdentifier)
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData(profileType, "ProfileLanguage", languageIdentifierString)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
nextPage()
|
||
|
}
|
||
|
|
||
|
languagesWidgetList, err := getFyneWidgetListFromStringList(worldLanguageDescriptionsList, onSelectedFunction)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
header := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator())
|
||
|
|
||
|
page := container.NewBorder(header, nil, nil, nil, languagesWidgetList)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildProfilePage_Username(window fyne.Window, profileType string, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildProfilePage_Username(window, profileType, previousPage)}
|
||
|
|
||
|
pageTitle := getPageTitleCentered(translate("Build " + profileType + " Profile - General"))
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
pageSubtitle := getPageSubtitleCentered(translate("Username"))
|
||
|
|
||
|
description1 := getLabelCentered(translate("Create your username."))
|
||
|
description2 := getLabelCentered(translate("You do not have to enter your real name."))
|
||
|
description3 := getLabelCentered(translate("If you want to protect your privacy, don't share your surname."))
|
||
|
|
||
|
getMyCurrentUsernameRow := func()(*fyne.Container, error){
|
||
|
|
||
|
myUsernameLabel := widget.NewLabel("My Username:")
|
||
|
|
||
|
exists, currentUsername, err := myLocalProfiles.GetProfileData(profileType, "Username")
|
||
|
if (err != nil) { return nil, err }
|
||
|
if (exists == false){
|
||
|
|
||
|
noResponseLabel := getBoldItalicLabel("No Response")
|
||
|
currentUsernameRow := container.NewHBox(layout.NewSpacer(), myUsernameLabel, noResponseLabel, layout.NewSpacer())
|
||
|
|
||
|
return currentUsernameRow, nil
|
||
|
}
|
||
|
|
||
|
isAllowed := allowedText.VerifyStringIsAllowed(currentUsername)
|
||
|
if (isAllowed == false){
|
||
|
return nil, errors.New("My current " + profileType + " username is not allowed: Not valid utf8: " + currentUsername)
|
||
|
}
|
||
|
|
||
|
containsTabsOrNewlines := helpers.CheckIfStringContainsTabsOrNewlines(currentUsername)
|
||
|
if (containsTabsOrNewlines == true){
|
||
|
return nil, errors.New("My current " + profileType + " username is not allowed: Contains newline/tab: " + currentUsername)
|
||
|
}
|
||
|
|
||
|
if( len(currentUsername) > 25){
|
||
|
return nil, errors.New("Current " + profileType + " username is too long: " + currentUsername)
|
||
|
}
|
||
|
|
||
|
currentUsernameLabel := getBoldLabel(currentUsername)
|
||
|
|
||
|
currentUsernameRow := container.NewHBox(layout.NewSpacer(), myUsernameLabel, currentUsernameLabel, layout.NewSpacer())
|
||
|
|
||
|
return currentUsernameRow, nil
|
||
|
}
|
||
|
|
||
|
currentUsernameRow, err := getMyCurrentUsernameRow()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
editButton := widget.NewButtonWithIcon("Edit", theme.DocumentCreateIcon(), func(){
|
||
|
setBuildProfilePage_EditUsername(window, profileType, currentPage, currentPage)
|
||
|
})
|
||
|
|
||
|
noResponseButton := widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){
|
||
|
myLocalProfiles.DeleteProfileData(profileType, "Username")
|
||
|
currentPage()
|
||
|
})
|
||
|
|
||
|
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, editButton, noResponseButton))
|
||
|
|
||
|
page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), currentUsernameRow, widget.NewSeparator(), buttonsGrid)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildProfilePage_EditUsername(window fyne.Window, profileType string, previousPage func(), nextPage func()){
|
||
|
|
||
|
title := getPageTitleCentered("Edit " + profileType + " Username")
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
usernameEntry := widget.NewEntry()
|
||
|
|
||
|
// Outputs:
|
||
|
// -bool: Current username exists
|
||
|
// -string: Current username
|
||
|
// -error
|
||
|
getCurrentUsername := func()(bool, string, error){
|
||
|
|
||
|
exists, currentUsername, err := myLocalProfiles.GetProfileData(profileType, "Username")
|
||
|
if (err != nil) { return false, "", err }
|
||
|
if (exists == false){
|
||
|
return false, "", nil
|
||
|
}
|
||
|
|
||
|
isAllowed := allowedText.VerifyStringIsAllowed(currentUsername)
|
||
|
if (isAllowed == false){
|
||
|
return false, "", errors.New("My current " + profileType + " username is not allowed: " + currentUsername)
|
||
|
}
|
||
|
|
||
|
containsTabsOrNewlines := helpers.CheckIfStringContainsTabsOrNewlines(currentUsername)
|
||
|
if (containsTabsOrNewlines == true){
|
||
|
return false, "", errors.New("My current " + profileType + " username is not allowed: " + currentUsername)
|
||
|
}
|
||
|
|
||
|
if( len(currentUsername) > 25){
|
||
|
return false, "", errors.New("My current " + profileType + " username is too long: " + currentUsername)
|
||
|
}
|
||
|
|
||
|
return true, currentUsername, nil
|
||
|
}
|
||
|
|
||
|
currentUsernameExists, currentUsername, err := getCurrentUsername()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
if (currentUsernameExists == false){
|
||
|
usernameEntry.SetPlaceHolder("Enter a username...")
|
||
|
} else {
|
||
|
usernameEntry.SetText(currentUsername)
|
||
|
}
|
||
|
|
||
|
submitButton := getWidgetCentered(widget.NewButtonWithIcon(translate("Save"), theme.ConfirmIcon(), func(){
|
||
|
|
||
|
newUsername := usernameEntry.Text
|
||
|
|
||
|
isAllowed := allowedText.VerifyStringIsAllowed(newUsername)
|
||
|
if (isAllowed == false){
|
||
|
title := translate("Invalid Username")
|
||
|
dialogMessageA := getLabelCentered(translate("Your username contains an invalid character."))
|
||
|
dialogMessageB := getLabelCentered(translate("It must be encoded in UTF-8."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
containsTabsOrNewlines := helpers.CheckIfStringContainsTabsOrNewlines(newUsername)
|
||
|
if (containsTabsOrNewlines == true){
|
||
|
title := translate("Invalid Username")
|
||
|
dialogMessageA := getLabelCentered(translate("Your username contains a tab or a newline character."))
|
||
|
dialogMessageB := getLabelCentered(translate("Remove this character and resubmit."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
currentBytesLength := len(newUsername)
|
||
|
if (currentBytesLength > 25){
|
||
|
|
||
|
currentLengthString := helpers.ConvertIntToString(currentBytesLength)
|
||
|
|
||
|
title := translate("Invalid Username")
|
||
|
dialogMessageA := getLabelCentered(translate("Username is too long."))
|
||
|
dialogMessageB := getLabelCentered(translate("Username cannot be longer than 25 bytes."))
|
||
|
dialogMessageC := getLabelCentered(translate("Your submission length: ") + currentLengthString + " bytes.")
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (newUsername == ""){
|
||
|
myLocalProfiles.DeleteProfileData(profileType, "Username")
|
||
|
} else {
|
||
|
myLocalProfiles.SetProfileData(profileType, "Username", newUsername)
|
||
|
}
|
||
|
nextPage()
|
||
|
}))
|
||
|
|
||
|
emptyLabelA := widget.NewLabel("")
|
||
|
emptyLabelB := widget.NewLabel("")
|
||
|
emptyLabelC := widget.NewLabel("")
|
||
|
|
||
|
submitButtonWithSpacers := container.NewVBox(submitButton, emptyLabelA, emptyLabelB, emptyLabelC)
|
||
|
|
||
|
usernameEntryBoxed := getWidgetBoxed(usernameEntry)
|
||
|
|
||
|
usernameEntryWithSubmitButton := container.NewBorder(nil, submitButtonWithSpacers, nil, nil, usernameEntryBoxed)
|
||
|
|
||
|
header := container.NewVBox(title, backButton, widget.NewSeparator())
|
||
|
|
||
|
page := container.NewBorder(header, nil, nil, nil, usernameEntryWithSubmitButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildProfilePage_Description(window fyne.Window, profileType string, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildProfilePage_Description(window, profileType, previousPage)}
|
||
|
|
||
|
pageTitle := getPageTitleCentered(translate("Build " + profileType + " Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
pageSubtitle := getPageSubtitleCentered(translate("Description"))
|
||
|
|
||
|
pageDescription := getLabelCentered(translate("Create your profile description."))
|
||
|
|
||
|
getMyCurrentDescriptionRow := func()(*fyne.Container, error){
|
||
|
|
||
|
myDescriptionLabel := widget.NewLabel("My Description:")
|
||
|
|
||
|
exists, currentDescription, err := myLocalProfiles.GetProfileData(profileType, "Description")
|
||
|
if (err != nil) { return nil, err }
|
||
|
if (exists == false){
|
||
|
|
||
|
noResponseLabel := getBoldItalicLabel("No Response")
|
||
|
currentDescriptionRow := container.NewHBox(layout.NewSpacer(), myDescriptionLabel, noResponseLabel, layout.NewSpacer())
|
||
|
|
||
|
return currentDescriptionRow, nil
|
||
|
}
|
||
|
|
||
|
isAllowed := allowedText.VerifyStringIsAllowed(currentDescription)
|
||
|
if (isAllowed == false){
|
||
|
return nil, errors.New("My current " + profileType + "description is not allowed: Not utf8.")
|
||
|
}
|
||
|
|
||
|
getSizeLimit := func()int{
|
||
|
if (profileType == "Moderator"){
|
||
|
return 500
|
||
|
}
|
||
|
if (profileType == "Host"){
|
||
|
return 300
|
||
|
}
|
||
|
return 3000
|
||
|
}
|
||
|
|
||
|
sizeLimit := getSizeLimit()
|
||
|
|
||
|
if (len(currentDescription) > sizeLimit){
|
||
|
return nil, errors.New("My current " + profileType + " description is too long.")
|
||
|
}
|
||
|
|
||
|
currentDescriptionTrimmed, _, err := helpers.TrimAndFlattenString(currentDescription, 15)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
currentDescriptionLabel := getBoldLabel(currentDescriptionTrimmed)
|
||
|
|
||
|
viewMyDescriptionButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
||
|
setViewTextPage(window, "Viewing Description", currentDescription, false, currentPage)
|
||
|
})
|
||
|
|
||
|
currentDescriptionRow := container.NewHBox(layout.NewSpacer(), myDescriptionLabel, currentDescriptionLabel, viewMyDescriptionButton, layout.NewSpacer())
|
||
|
|
||
|
return currentDescriptionRow, nil
|
||
|
}
|
||
|
|
||
|
currentDescriptionRow, err := getMyCurrentDescriptionRow()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
editButton := widget.NewButtonWithIcon("Edit", theme.DocumentCreateIcon(), func(){
|
||
|
setBuildProfilePage_EditDescription(window, profileType, currentPage, currentPage)
|
||
|
})
|
||
|
|
||
|
noResponseButton := widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){
|
||
|
setBuildProfilePage_DeleteDescription(window, profileType, currentPage, currentPage)
|
||
|
})
|
||
|
|
||
|
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, editButton, noResponseButton))
|
||
|
|
||
|
page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), pageDescription, widget.NewSeparator(), currentDescriptionRow, widget.NewSeparator(), buttonsGrid)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildProfilePage_DeleteDescription(window fyne.Window, profileType string, previousPage func(), nextPage func()){
|
||
|
|
||
|
title := getPageTitleCentered("Delete " + profileType + " Description")
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
description1 := getBoldLabelCentered("Delete " + profileType + " Description?")
|
||
|
|
||
|
description2 := getLabelCentered("Confirm to delete your description?")
|
||
|
|
||
|
confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){
|
||
|
myLocalProfiles.DeleteProfileData(profileType, "Description")
|
||
|
nextPage()
|
||
|
}))
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, confirmButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildProfilePage_EditDescription(window fyne.Window, profileType string, previousPage func(), nextPage func()){
|
||
|
|
||
|
title := getPageTitleCentered("Edit " + profileType + " Description")
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
// Returns byte length limit for description length
|
||
|
getSizeLimit := func()int{
|
||
|
if (profileType == "Moderator"){
|
||
|
return 500
|
||
|
}
|
||
|
if (profileType == "Host"){
|
||
|
return 300
|
||
|
}
|
||
|
return 3000
|
||
|
}
|
||
|
|
||
|
sizeLimit := getSizeLimit()
|
||
|
|
||
|
descriptionEntry := widget.NewMultiLineEntry()
|
||
|
descriptionEntry.Wrapping = 3
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Current description exists
|
||
|
// -string: Current description
|
||
|
// -error
|
||
|
getCurrentDescription := func()(bool, string, error){
|
||
|
|
||
|
exists, currentDescription, err := myLocalProfiles.GetProfileData(profileType, "Description")
|
||
|
if (err != nil) { return false, "", err }
|
||
|
if (exists == false){
|
||
|
return false, "", nil
|
||
|
}
|
||
|
|
||
|
isAllowed := allowedText.VerifyStringIsAllowed(currentDescription)
|
||
|
if (isAllowed == false){
|
||
|
return false, "", errors.New("My current " + profileType + "description is not allowed.")
|
||
|
}
|
||
|
|
||
|
if (len(currentDescription) > sizeLimit){
|
||
|
return false, "", errors.New("My current " + profileType + " description is too long.")
|
||
|
}
|
||
|
|
||
|
return true, currentDescription, nil
|
||
|
}
|
||
|
|
||
|
currentDescriptionExists, currentDescription, err := getCurrentDescription()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
if (currentDescriptionExists == false){
|
||
|
descriptionEntry.SetPlaceHolder("Enter a description...")
|
||
|
} else {
|
||
|
descriptionEntry.SetText(currentDescription)
|
||
|
}
|
||
|
|
||
|
submitButton := getWidgetCentered(widget.NewButtonWithIcon(translate("Save"), theme.ConfirmIcon(), func(){
|
||
|
|
||
|
newDescription := descriptionEntry.Text
|
||
|
|
||
|
isAllowed := allowedText.VerifyStringIsAllowed(newDescription)
|
||
|
if (isAllowed == false){
|
||
|
title := translate("Invalid Description")
|
||
|
dialogMessageA := getLabelCentered(translate("Your description contains an invalid character."))
|
||
|
dialogMessageB := getLabelCentered(translate("Your description must be encoded in UTF-8."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
newDescriptionLength := len(newDescription)
|
||
|
if (newDescriptionLength > sizeLimit){
|
||
|
|
||
|
sizeLimitString := helpers.ConvertIntToStringWithCommas(sizeLimit)
|
||
|
currentLengthString := helpers.ConvertIntToString(newDescriptionLength)
|
||
|
|
||
|
title := translate("Invalid Description")
|
||
|
dialogMessageA := getLabelCentered(translate("Description is too long."))
|
||
|
dialogMessageB := getLabelCentered(translate("Description cannot be longer than " + sizeLimitString + " bytes."))
|
||
|
dialogMessageC := getLabelCentered(translate("Your submission is " + currentLengthString + " bytes."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (newDescription == ""){
|
||
|
myLocalProfiles.DeleteProfileData(profileType, "Description")
|
||
|
} else {
|
||
|
myLocalProfiles.SetProfileData(profileType, "Description", newDescription)
|
||
|
}
|
||
|
|
||
|
nextPage()
|
||
|
}))
|
||
|
|
||
|
emptyLabel := widget.NewLabel("")
|
||
|
|
||
|
submitButtonWithSpacer := container.NewVBox(submitButton, emptyLabel)
|
||
|
|
||
|
descriptionEntryBoxed := getWidgetBoxed(descriptionEntry)
|
||
|
|
||
|
descriptionEntryWithSubmitButton := container.NewBorder(nil, submitButtonWithSpacer, nil, nil, descriptionEntryBoxed)
|
||
|
|
||
|
header := container.NewVBox(title, backButton, widget.NewSeparator())
|
||
|
|
||
|
page := container.NewBorder(header, nil, nil, nil, descriptionEntryWithSubmitButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildMateProfilePage_Location(window fyne.Window, previousPage func()){
|
||
|
|
||
|
setLoadingScreen(window, "Build Mate Profile - General", "Loading...")
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_Location(window, previousPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getPageSubtitleCentered(translate("Location"))
|
||
|
|
||
|
description1 := getLabelCentered("Add your location to your profile.")
|
||
|
description2 := getLabelCentered("You can add a primary and a secondary location.")
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Location exists
|
||
|
// -float64: Location latitude
|
||
|
// -float64: Location longitude
|
||
|
// -bool: Location country exists
|
||
|
// -int: Location country identifier
|
||
|
// -error
|
||
|
getMyLocationInfo := func(rank string)(bool, float64, float64, bool, int, error){
|
||
|
|
||
|
if (rank != "Primary" && rank != "Secondary"){
|
||
|
return false, 0, 0, false, 0, errors.New("getMyLocationInfo called with invalid rank: " + rank)
|
||
|
}
|
||
|
|
||
|
exists, locationLatitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLatitude")
|
||
|
if (err != nil) { return false, 0, 0, false, 0, err }
|
||
|
if (exists == false){
|
||
|
|
||
|
return false, 0, 0, false, 0, nil
|
||
|
|
||
|
}
|
||
|
exists, locationLongitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLongitude")
|
||
|
if (err != nil) { return false, 0, 0, false, 0, err }
|
||
|
if (exists == false){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains " + rank + "LocationLatitude but missing " + rank + "LocationLongitude")
|
||
|
}
|
||
|
|
||
|
locationLatitudeFloat64, err := helpers.ConvertStringToFloat64(locationLatitude)
|
||
|
if (err != nil){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLatitude: " + locationLatitude)
|
||
|
}
|
||
|
|
||
|
locationLongitudeFloat64, err := helpers.ConvertStringToFloat64(locationLongitude)
|
||
|
if (err != nil){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLongitude: " + locationLongitude)
|
||
|
}
|
||
|
|
||
|
exists, locationCountryIdentifier, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationCountry")
|
||
|
if (err != nil) { return false, 0, 0, false, 0, err }
|
||
|
if (exists == false){
|
||
|
return true, locationLatitudeFloat64, locationLongitudeFloat64, false, 0, nil
|
||
|
}
|
||
|
|
||
|
locationCountryIdentifierInt, err := helpers.ConvertStringToInt(locationCountryIdentifier)
|
||
|
if (err != nil){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationCountry: " + locationCountryIdentifier)
|
||
|
}
|
||
|
|
||
|
return true, locationLatitudeFloat64, locationLongitudeFloat64, true, locationCountryIdentifierInt, nil
|
||
|
}
|
||
|
|
||
|
primaryLocationExists, primaryLocationLatitude, primaryLocationLongitude, primaryLocationCountryExists, primaryLocationCountryIdentifier, err := getMyLocationInfo("Primary")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
secondaryLocationExists, secondaryLocationLatitude, secondaryLocationLongitude, secondaryLocationCountryExists, secondaryLocationCountryIdentifier, err := getMyLocationInfo("Secondary")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (primaryLocationExists == false && secondaryLocationExists == true){
|
||
|
setErrorEncounteredPage(window, errors.New("Primary location does not exist, secondary location exists."), previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
addLocationButton := getWidgetCentered(widget.NewButtonWithIcon("Add Location", theme.ContentAddIcon(), func(){
|
||
|
if (primaryLocationExists == true && secondaryLocationExists == true){
|
||
|
title := translate("Cannot Add Location.")
|
||
|
dialogMessageA := getLabelCentered(translate("You can only have 2 locations."))
|
||
|
dialogMessageB := getLabelCentered(translate("Delete a location first."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
setBuildMateProfilePage_AddLocation_ChooseCountry(window, currentPage, currentPage)
|
||
|
}))
|
||
|
|
||
|
if (primaryLocationExists == false){
|
||
|
|
||
|
noLocationsExistLabel := getBoldLabelCentered("No Locations Exist.")
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), noLocationsExistLabel, addLocationButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
visibilityLabel := getBoldLabel("Visibility:")
|
||
|
|
||
|
visibilityOptions := []string{translate("Show On Profile"), translate("Hide From Profile")}
|
||
|
visibilitySelector := widget.NewSelect(visibilityOptions, func(newVisibility string){
|
||
|
|
||
|
getNewVisibilityStatus := func()string{
|
||
|
|
||
|
if (newVisibility == translate("Show On Profile")){
|
||
|
return "Yes"
|
||
|
}
|
||
|
|
||
|
return "No"
|
||
|
}
|
||
|
|
||
|
newVisibilityStatus := getNewVisibilityStatus()
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData("Mate", "VisibilityStatus_Location", newVisibilityStatus)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
})
|
||
|
|
||
|
exists, visibilityStatus, err := myLocalProfiles.GetProfileData("Mate", "VisibilityStatus_Location")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
if (exists == true && visibilityStatus == "No"){
|
||
|
visibilitySelector.Selected = translate("Hide From Profile")
|
||
|
} else {
|
||
|
visibilitySelector.Selected = translate("Show On Profile")
|
||
|
}
|
||
|
|
||
|
profileVisibilityHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
||
|
setMateProfileAttributeVisibilityExplainerPage(window, currentPage)
|
||
|
})
|
||
|
|
||
|
visibilitySelectorRow := container.NewHBox(layout.NewSpacer(), visibilityLabel, visibilitySelector, profileVisibilityHelpButton, layout.NewSpacer())
|
||
|
|
||
|
getLocationsGrid := func()(*fyne.Container, error){
|
||
|
|
||
|
rankLabel := getItalicLabelCentered("Rank")
|
||
|
|
||
|
countryLabel := getItalicLabelCentered("Country")
|
||
|
|
||
|
locationLabel := getItalicLabelCentered("Location")
|
||
|
|
||
|
emptyLabel := widget.NewLabel("")
|
||
|
|
||
|
rankColumn := container.NewVBox(rankLabel, widget.NewSeparator())
|
||
|
countryColumn := container.NewVBox(countryLabel, widget.NewSeparator())
|
||
|
locationColumn := container.NewVBox(locationLabel, widget.NewSeparator())
|
||
|
manageButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator())
|
||
|
|
||
|
addLocationRow := func(locationRank string, locationLatitude float64, locationLongitude float64, locationCountryExists bool, locationCountryIdentifier int)error{
|
||
|
|
||
|
getCountryText := func()(string, error){
|
||
|
if (locationCountryExists == false){
|
||
|
noneText := translate("None")
|
||
|
return noneText, nil
|
||
|
}
|
||
|
|
||
|
locationObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(locationCountryIdentifier)
|
||
|
if (err != nil) { return "", err }
|
||
|
|
||
|
locationNamesList := locationObject.NamesList
|
||
|
|
||
|
locationDescription := helpers.TranslateAndJoinStringListItems(locationNamesList, "/")
|
||
|
|
||
|
locationCountryTranslatedTrimmed, _, err := helpers.TrimAndFlattenString(locationDescription, 20)
|
||
|
if (err != nil) { return "", err }
|
||
|
|
||
|
return locationCountryTranslatedTrimmed, nil
|
||
|
}
|
||
|
|
||
|
countryText, err := getCountryText()
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
getLocationText := func()(string, error){
|
||
|
|
||
|
locationCityFound, locationCity, locationState, _, err := worldLocations.GetCityFromCoordinates(locationLatitude, locationLongitude)
|
||
|
if (err != nil) { return "", err }
|
||
|
if (locationCityFound == false){
|
||
|
|
||
|
locationLatitudeString := helpers.ConvertFloat64ToStringRounded(locationLatitude, 5)
|
||
|
locationLongitudeString := helpers.ConvertFloat64ToStringRounded(locationLongitude, 5)
|
||
|
|
||
|
formattedCoordinates := locationLatitudeString + "°, " + locationLongitudeString + "°"
|
||
|
|
||
|
return formattedCoordinates, nil
|
||
|
}
|
||
|
|
||
|
locationCityFormatted := locationCity + ", " + locationState
|
||
|
|
||
|
locationCityTrimmed, _, err := helpers.TrimAndFlattenString(locationCityFormatted, 25)
|
||
|
if (err != nil) { return "", err }
|
||
|
|
||
|
return locationCityTrimmed, nil
|
||
|
}
|
||
|
|
||
|
locationText, err := getLocationText()
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
locationRankLabel := getBoldLabelCentered(locationRank)
|
||
|
locationCountryLabel := getBoldLabelCentered(countryText)
|
||
|
locationTextLabel := getBoldLabelCentered(locationText)
|
||
|
|
||
|
manageLocationButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
||
|
setBuildMateProfilePage_ManageLocation(window, locationRank, currentPage, currentPage)
|
||
|
})
|
||
|
|
||
|
rankColumn.Add(locationRankLabel)
|
||
|
countryColumn.Add(locationCountryLabel)
|
||
|
locationColumn.Add(locationTextLabel)
|
||
|
manageButtonsColumn.Add(manageLocationButton)
|
||
|
|
||
|
rankColumn.Add(widget.NewSeparator())
|
||
|
countryColumn.Add(widget.NewSeparator())
|
||
|
locationColumn.Add(widget.NewSeparator())
|
||
|
manageButtonsColumn.Add(widget.NewSeparator())
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
err = addLocationRow("Primary", primaryLocationLatitude, primaryLocationLongitude, primaryLocationCountryExists, primaryLocationCountryIdentifier)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
if (secondaryLocationExists == true){
|
||
|
err := addLocationRow("Secondary", secondaryLocationLatitude, secondaryLocationLongitude, secondaryLocationCountryExists, secondaryLocationCountryIdentifier)
|
||
|
if (err != nil) { return nil, err }
|
||
|
}
|
||
|
|
||
|
locationsGrid := container.NewHBox(layout.NewSpacer(), rankColumn, countryColumn, locationColumn, manageButtonsColumn, layout.NewSpacer())
|
||
|
|
||
|
if (secondaryLocationExists == false){
|
||
|
|
||
|
locationGridWithAddLocationButton := container.NewVBox(locationsGrid, addLocationButton)
|
||
|
|
||
|
return locationGridWithAddLocationButton, nil
|
||
|
}
|
||
|
|
||
|
swapLocationsFunction := func()error{
|
||
|
|
||
|
primaryLocationLatitudeString := helpers.ConvertFloat64ToString(primaryLocationLatitude)
|
||
|
primaryLocationLongitudeString := helpers.ConvertFloat64ToString(primaryLocationLongitude)
|
||
|
|
||
|
secondaryLocationLatitudeString := helpers.ConvertFloat64ToString(secondaryLocationLatitude)
|
||
|
secondaryLocationLongitudeString := helpers.ConvertFloat64ToString(secondaryLocationLongitude)
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLatitude", secondaryLocationLatitudeString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLongitude", secondaryLocationLongitudeString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
if (secondaryLocationCountryExists == true){
|
||
|
|
||
|
secondaryLocationCountryIdentifierString := helpers.ConvertIntToString(secondaryLocationCountryIdentifier)
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData("Mate", "PrimaryLocationCountry", secondaryLocationCountryIdentifierString)
|
||
|
if (err != nil) { return err }
|
||
|
} else {
|
||
|
err := myLocalProfiles.DeleteProfileData("Mate", "PrimaryLocationCountry")
|
||
|
if (err != nil) { return err }
|
||
|
}
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "SecondaryLocationLatitude", primaryLocationLatitudeString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "SecondaryLocationLongitude", primaryLocationLongitudeString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
if (primaryLocationCountryExists == true){
|
||
|
|
||
|
primaryLocationCountryIdentifierString := helpers.ConvertIntToString(primaryLocationCountryIdentifier)
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData("Mate", "SecondaryLocationCountry", primaryLocationCountryIdentifierString)
|
||
|
if (err != nil) { return err }
|
||
|
} else {
|
||
|
err := myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationCountry")
|
||
|
if (err != nil) { return err }
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
swapLocationsButton := widget.NewButtonWithIcon("Swap Locations", theme.ContentRedoIcon(), func(){
|
||
|
err := swapLocationsFunction()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
currentPage()
|
||
|
})
|
||
|
|
||
|
buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, swapLocationsButton, addLocationButton))
|
||
|
|
||
|
locationGridWithButtons := container.NewVBox(locationsGrid, buttonsGrid)
|
||
|
|
||
|
return locationGridWithButtons, nil
|
||
|
}
|
||
|
|
||
|
locationsGrid, err := getLocationsGrid()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), visibilitySelectorRow, widget.NewSeparator(), locationsGrid)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildMateProfilePage_ManageLocation(window fyne.Window, locationRank string, previousPage func(), afterDeletePage func()){
|
||
|
|
||
|
if (locationRank != "Primary" && locationRank != "Secondary"){
|
||
|
setErrorEncounteredPage(window, errors.New("setBuildMateProfilePage_ManageLocation called with invalid locationRank: " + locationRank), previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
setLoadingScreen(window, "Build Mate Profile - General", "Loading...")
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_ManageLocation(window, locationRank, previousPage, afterDeletePage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getPageSubtitleCentered(translate("Manage Location"))
|
||
|
|
||
|
description1 := getLabelCentered("This is your " + locationRank + " location.")
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Location exists
|
||
|
// -float64: Location latitude
|
||
|
// -float64: Location longitude
|
||
|
// -bool: Location country exists
|
||
|
// -int: Location country identifier
|
||
|
// -error
|
||
|
getMyLocationInfo := func(rank string)(bool, float64, float64, bool, int, error){
|
||
|
|
||
|
if (rank != "Primary" && rank != "Secondary"){
|
||
|
return false, 0, 0, false, 0, errors.New("getMyLocationInfo called with invalid rank: " + rank)
|
||
|
}
|
||
|
|
||
|
exists, locationLatitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLatitude")
|
||
|
if (err != nil) { return false, 0, 0, false, 0, err }
|
||
|
if (exists == false){
|
||
|
|
||
|
return false, 0, 0, false, 0, nil
|
||
|
}
|
||
|
|
||
|
exists, locationLongitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLongitude")
|
||
|
if (err != nil) { return false, 0, 0, false, 0, err }
|
||
|
if (exists == false){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains " + rank + "LocationLatitude but missing " + rank + "LocationLongitude")
|
||
|
}
|
||
|
|
||
|
locationLatitudeFloat64, err := helpers.ConvertStringToFloat64(locationLatitude)
|
||
|
if (err != nil){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLatitude: " + locationLatitude)
|
||
|
}
|
||
|
|
||
|
locationLongitudeFloat64, err := helpers.ConvertStringToFloat64(locationLongitude)
|
||
|
if (err != nil){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLongitude: " + locationLongitude)
|
||
|
}
|
||
|
|
||
|
exists, locationCountryIdentifier, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationCountry")
|
||
|
if (err != nil) { return false, 0, 0, false, 0, err }
|
||
|
if (exists == false){
|
||
|
return true, locationLatitudeFloat64, locationLongitudeFloat64, false, 0, nil
|
||
|
}
|
||
|
|
||
|
locationCountryIdentifierInt, err := helpers.ConvertStringToInt(locationCountryIdentifier)
|
||
|
if (err != nil){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid locationCountryIdentifier: " + locationCountryIdentifier)
|
||
|
}
|
||
|
|
||
|
return true, locationLatitudeFloat64, locationLongitudeFloat64, true, locationCountryIdentifierInt, nil
|
||
|
}
|
||
|
|
||
|
locationExists, locationLatitude, locationLongitude, locationCountryExists, locationCountryIdentifier, err := getMyLocationInfo(locationRank)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
if (locationExists == false){
|
||
|
setErrorEncounteredPage(window, errors.New("setBuildMateProfilePage_ManageLocation called with missing location"), previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
getCountryText := func()(string, error){
|
||
|
if (locationCountryExists == false){
|
||
|
noneText := translate("None")
|
||
|
return noneText, nil
|
||
|
}
|
||
|
|
||
|
locationObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(locationCountryIdentifier)
|
||
|
if (err != nil) { return "", err }
|
||
|
|
||
|
locationNamesList := locationObject.NamesList
|
||
|
|
||
|
locationDescription := helpers.TranslateAndJoinStringListItems(locationNamesList, "/")
|
||
|
|
||
|
return locationDescription, nil
|
||
|
}
|
||
|
|
||
|
countryText, err := getCountryText()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
countryLabel := getLabelCentered("Country:")
|
||
|
locationCountryLabel := getBoldLabel(countryText)
|
||
|
|
||
|
coordinatesLabel := getLabelCentered("Coordinates:")
|
||
|
|
||
|
locationLatitudeString := helpers.ConvertFloat64ToString(locationLatitude)
|
||
|
locationLongitudeString := helpers.ConvertFloat64ToString(locationLongitude)
|
||
|
|
||
|
locationCoordinatesLabel := getBoldLabel(locationLatitudeString + "°, " + locationLongitudeString + "°")
|
||
|
|
||
|
cityLabel := getLabelCentered("City:")
|
||
|
|
||
|
getCityContent := func()(*fyne.Container, error){
|
||
|
|
||
|
// We will either find the exact city, or find the closest city
|
||
|
|
||
|
cityName, cityState, cityCountryIdentifier, cityDistanceKilometers, err := worldLocations.GetClosestCityFromCoordinates(locationLatitude, locationLongitude)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
if (cityDistanceKilometers == 0){
|
||
|
|
||
|
locationCityFormatted := cityName + ", " + cityState
|
||
|
|
||
|
locationCityLabel := getBoldLabelCentered(locationCityFormatted)
|
||
|
|
||
|
return locationCityLabel, nil
|
||
|
}
|
||
|
|
||
|
getNearbyCityDistanceFormattedString := func()(string, error){
|
||
|
|
||
|
currentUnitsExist, currentUnits, err := globalSettings.GetSetting("MetricOrImperial")
|
||
|
if (err != nil){ return "", err }
|
||
|
|
||
|
if (currentUnitsExist == true && currentUnits == "Imperial"){
|
||
|
|
||
|
distanceMiles, err := helpers.ConvertKilometersToMiles(cityDistanceKilometers)
|
||
|
if (err != nil){ return "", err }
|
||
|
|
||
|
distanceMilesString := helpers.ConvertFloat64ToStringRounded(distanceMiles, 1)
|
||
|
result := distanceMilesString + " miles"
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
distanceKilometersString := helpers.ConvertFloat64ToStringRounded(cityDistanceKilometers, 1)
|
||
|
|
||
|
result := distanceKilometersString + " kilometers"
|
||
|
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
nearbyCityDistanceFormattedString, err := getNearbyCityDistanceFormattedString()
|
||
|
if (err != nil){ return nil, err }
|
||
|
|
||
|
getNearbyCityNameFormatted := func()(string, error){
|
||
|
|
||
|
if (locationCountryExists == true && cityCountryIdentifier == locationCountryIdentifier){
|
||
|
result := cityName + ", " + cityState
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
locationObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(cityCountryIdentifier)
|
||
|
if (err != nil) { return "", err }
|
||
|
|
||
|
locationPrimaryName := locationObject.NamesList[0]
|
||
|
|
||
|
locationPrimaryNameTranslated := translate(locationPrimaryName)
|
||
|
|
||
|
result := cityName + ", " + cityState + ", " + locationPrimaryNameTranslated
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
nearbyCityNameFormatted, err := getNearbyCityNameFormatted()
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
nearbyCityNameFormattedAndTrimmed, _, err := helpers.TrimAndFlattenString(nearbyCityNameFormatted, 40)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
locationDistanceLabel := getBoldLabelCentered(nearbyCityDistanceFormattedString + " from")
|
||
|
|
||
|
locationCityNameLabel := getBoldLabelCentered(nearbyCityNameFormattedAndTrimmed)
|
||
|
|
||
|
cityInfoContainer := container.NewVBox(locationDistanceLabel, locationCityNameLabel)
|
||
|
|
||
|
return cityInfoContainer, nil
|
||
|
}
|
||
|
|
||
|
cityContent, err := getCityContent()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
deleteFunction := func()error{
|
||
|
|
||
|
if (locationRank == "Secondary"){
|
||
|
|
||
|
err := myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationLatitude")
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationLongitude")
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationCountry")
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// We want to delete the primary location
|
||
|
// If there is a secondary location, we set it as the primary after deleting the primary
|
||
|
|
||
|
primaryLocationExists, _, _, _, _, err := getMyLocationInfo("Primary")
|
||
|
if (err != nil){ return err }
|
||
|
if (primaryLocationExists == false){
|
||
|
return errors.New("Trying to delete the primary location, but the location does not exist.")
|
||
|
}
|
||
|
|
||
|
err = myLocalProfiles.DeleteProfileData("Mate", "PrimaryLocationLatitude")
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.DeleteProfileData("Mate", "PrimaryLocationLongitude")
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.DeleteProfileData("Mate", "PrimaryLocationCountry")
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
secondaryLocationExists, secondaryLocationLatitude, secondaryLocationLongitude, secondaryLocationCountryExists, secondaryLocationCountryIdentifier, err := getMyLocationInfo("Secondary")
|
||
|
if (err != nil){ return err }
|
||
|
if (secondaryLocationExists == false){
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
err = myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationLatitude")
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationLongitude")
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationCountry")
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
secondaryLocationLatitudeString := helpers.ConvertFloat64ToString(secondaryLocationLatitude)
|
||
|
secondaryLocationLongitudeString := helpers.ConvertFloat64ToString(secondaryLocationLongitude)
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLatitude", secondaryLocationLatitudeString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLongitude", secondaryLocationLongitudeString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
if (secondaryLocationCountryExists == true){
|
||
|
|
||
|
secondaryLocationCountryIdentifierString := helpers.ConvertIntToString(secondaryLocationCountryIdentifier)
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData("Mate", "PrimaryLocationCountry", secondaryLocationCountryIdentifierString)
|
||
|
if (err != nil) { return err }
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
deleteButton := getWidgetCentered(widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){
|
||
|
err := deleteFunction()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
afterDeletePage()
|
||
|
}))
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, widget.NewSeparator(), countryLabel, locationCountryLabel, widget.NewSeparator(), coordinatesLabel, locationCoordinatesLabel, widget.NewSeparator(), cityLabel, cityContent, widget.NewSeparator(), deleteButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildMateProfilePage_AddLocation_ChooseCountry(window fyne.Window, previousPage func(), visitOnCompletePage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_AddLocation_ChooseCountry(window, previousPage, visitOnCompletePage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getPageSubtitleCentered(translate("Add Location"))
|
||
|
|
||
|
description1 := getLabelCentered("Choose the country of the location to add.")
|
||
|
description2 := getLabelCentered("If your country is not listed, select Country Is Missing")
|
||
|
|
||
|
allCountryObjectsList, err := worldLocations.GetAllCountryObjectsList()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
allCountryDescriptionsList := make([]string, 0, len(allCountryObjectsList))
|
||
|
|
||
|
// Map Structure: Country Description -> Country identifier
|
||
|
countryIdentifiersMap := make(map[string]int)
|
||
|
|
||
|
for _, countryObject := range allCountryObjectsList{
|
||
|
|
||
|
countryIdentifier := countryObject.Identifier
|
||
|
countryNamesList := countryObject.NamesList
|
||
|
|
||
|
countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")
|
||
|
|
||
|
countryIdentifiersMap[countryDescription] = countryIdentifier
|
||
|
allCountryDescriptionsList = append(allCountryDescriptionsList, countryDescription)
|
||
|
}
|
||
|
|
||
|
helpers.SortStringListToUnicodeOrder(allCountryDescriptionsList)
|
||
|
|
||
|
onSelectedFunction := func(selectedCountryIndex int) {
|
||
|
|
||
|
selectedCountryDescription := allCountryDescriptionsList[selectedCountryIndex]
|
||
|
|
||
|
selectedCountryIdentifier, exists := countryIdentifiersMap[selectedCountryDescription]
|
||
|
if (exists == false){
|
||
|
setErrorEncounteredPage(window, errors.New("countryIdentifiersMap missing country description: " + selectedCountryDescription), currentPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
setBuildMateProfilePage_AddLocation_ChooseCity(window, selectedCountryIdentifier, false, "", currentPage, visitOnCompletePage)
|
||
|
}
|
||
|
|
||
|
widgetList, err := getFyneWidgetListFromStringList(allCountryDescriptionsList, onSelectedFunction)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
header := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator())
|
||
|
|
||
|
countryIsMissingButton := getWidgetCentered(widget.NewButton("Country Is Missing", func(){
|
||
|
setBuildMateProfilePage_AddLocation_CustomCoordinates(window, false, 0, currentPage, visitOnCompletePage)
|
||
|
}))
|
||
|
|
||
|
page := container.NewBorder(header, countryIsMissingButton, nil, nil, widgetList)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildMateProfilePage_AddLocation_ChooseCity(window fyne.Window, selectedCountryIdentifier int, searchTermExists bool, searchTerm string, previousPage func(), visitOnCompletePage func()){
|
||
|
|
||
|
if (searchTermExists == true){
|
||
|
setLoadingScreen(window, "Build Mate Profile - General", "Loading Search Results...")
|
||
|
} else {
|
||
|
setLoadingScreen(window, "Build Mate Profile - General", "Loading...")
|
||
|
}
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_AddLocation_ChooseCity(window, selectedCountryIdentifier, searchTermExists, searchTerm, previousPage, visitOnCompletePage)}
|
||
|
|
||
|
allCountryCityObjectsList, err := worldLocations.GetAllCityObjectsInCountry(selectedCountryIdentifier)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (len(allCountryCityObjectsList) == 0){
|
||
|
// This country has no cities in the database
|
||
|
// We go directly to the custom location screen
|
||
|
setBuildMateProfilePage_AddLocation_CustomCoordinates(window, true, selectedCountryIdentifier, previousPage, visitOnCompletePage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
countryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(selectedCountryIdentifier)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
countryNamesList := countryObject.NamesList
|
||
|
|
||
|
countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getPageSubtitleCentered("Choose Location")
|
||
|
|
||
|
description1 := getLabelCentered("You must now enter a specific location in " + countryDescription + ".")
|
||
|
description2 := getLabelCentered("You can search for and choose a city from below.")
|
||
|
description3 := getLabelCentered("If your city is not listed, or to add a custom location, select Add Custom")
|
||
|
|
||
|
addCustomLocationButton := getWidgetCentered(widget.NewButtonWithIcon("Add Custom", theme.ContentAddIcon(), func(){
|
||
|
setBuildMateProfilePage_AddLocation_CustomCoordinates(window, true, selectedCountryIdentifier, currentPage, visitOnCompletePage)
|
||
|
}))
|
||
|
|
||
|
enterSearchTermLabel := getBoldLabelCentered("Enter Search Term:")
|
||
|
enterSearchTermEntry := widget.NewEntry()
|
||
|
if (searchTermExists == true){
|
||
|
enterSearchTermEntry.SetText(searchTerm)
|
||
|
} else {
|
||
|
enterSearchTermEntry.SetPlaceHolder("Enter search term...")
|
||
|
}
|
||
|
|
||
|
searchButton := widget.NewButtonWithIcon("Search", theme.SearchIcon(), func(){
|
||
|
|
||
|
newSearchTerm := enterSearchTermEntry.Text
|
||
|
if (newSearchTerm == searchTerm){
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (newSearchTerm == ""){
|
||
|
setBuildMateProfilePage_AddLocation_ChooseCity(window, selectedCountryIdentifier, false, "", previousPage, visitOnCompletePage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
setBuildMateProfilePage_AddLocation_ChooseCity(window, selectedCountryIdentifier, true, newSearchTerm, previousPage, visitOnCompletePage)
|
||
|
})
|
||
|
|
||
|
enterSearchTermRow := getContainerCentered(container.NewGridWithRows(1, enterSearchTermLabel, enterSearchTermEntry, searchButton))
|
||
|
|
||
|
header := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), addCustomLocationButton, widget.NewSeparator(), enterSearchTermRow, widget.NewSeparator())
|
||
|
|
||
|
getCityObjectsToDisplayList := func()[]worldLocations.CityObject{
|
||
|
|
||
|
if (searchTermExists == false){
|
||
|
|
||
|
return allCountryCityObjectsList
|
||
|
}
|
||
|
|
||
|
allCityObjectsWithSearchTermList := make([]worldLocations.CityObject, 0)
|
||
|
|
||
|
searchTermLowercase := strings.ToLower(searchTerm)
|
||
|
|
||
|
for _, cityObject := range allCountryCityObjectsList{
|
||
|
|
||
|
cityName := cityObject.Name
|
||
|
cityNameLowercase := strings.ToLower(cityName)
|
||
|
|
||
|
containsAny := strings.Contains(cityNameLowercase, searchTermLowercase)
|
||
|
if (containsAny == true){
|
||
|
allCityObjectsWithSearchTermList = append(allCityObjectsWithSearchTermList, cityObject)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return allCityObjectsWithSearchTermList
|
||
|
}
|
||
|
|
||
|
cityObjectsToDisplayList := getCityObjectsToDisplayList()
|
||
|
|
||
|
if (len(cityObjectsToDisplayList) == 0){
|
||
|
|
||
|
noCitiesFoundLabel := getBoldLabelCentered("No cities found.")
|
||
|
|
||
|
page := container.NewVBox(header, noCitiesFoundLabel)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
cityNamesFormattedList := make([]string, 0, len(cityObjectsToDisplayList))
|
||
|
|
||
|
for _, cityObject := range cityObjectsToDisplayList{
|
||
|
|
||
|
stateName := cityObject.State
|
||
|
cityName := cityObject.Name
|
||
|
|
||
|
nameFormatted := cityName + ", " + stateName
|
||
|
|
||
|
nameFormattedAndTrimmed, _, err := helpers.TrimAndFlattenString(nameFormatted, 50)
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
cityNamesFormattedList = append(cityNamesFormattedList, nameFormattedAndTrimmed)
|
||
|
}
|
||
|
|
||
|
onClickedFunction := func(cityIndex int){
|
||
|
|
||
|
selectedCityObject := cityObjectsToDisplayList[cityIndex]
|
||
|
|
||
|
selectedCityLatitude := selectedCityObject.Latitude
|
||
|
selectedCityLongitude := selectedCityObject.Longitude
|
||
|
|
||
|
setBuildMateProfilePage_ConfirmAddLocation(window, true, selectedCountryIdentifier, selectedCityLatitude, selectedCityLongitude, currentPage, visitOnCompletePage)
|
||
|
}
|
||
|
|
||
|
citySearchResultsWidgetList, err := getFyneWidgetListFromStringList(cityNamesFormattedList, onClickedFunction)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
page := container.NewBorder(header, nil, nil, nil, citySearchResultsWidgetList)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func setBuildMateProfilePage_AddLocation_CustomCoordinates(window fyne.Window, countryExists bool, countryIdentifier int, previousPage func(), visitOnCompletePage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_AddLocation_CustomCoordinates(window, countryExists, countryIdentifier, previousPage, visitOnCompletePage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getPageSubtitleCentered(translate("Add Custom Location"))
|
||
|
|
||
|
getDescription1Text := func()(string, error){
|
||
|
if (countryExists == false){
|
||
|
result := translate("Enter a location.")
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
countryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(countryIdentifier)
|
||
|
if (err != nil){ return "", err }
|
||
|
|
||
|
countryNamesList := countryObject.NamesList
|
||
|
|
||
|
countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")
|
||
|
|
||
|
result := translate("Enter a location in ") + countryDescription
|
||
|
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
description1Text, err := getDescription1Text()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
description1 := getLabelCentered(description1Text)
|
||
|
description2 := getBoldLabelCentered("We advise against entering your exact location.")
|
||
|
description3 := getLabelCentered("Choose a location that is not a house.")
|
||
|
description4 := getLabelCentered("Use an online map explorer to find the coordinates.")
|
||
|
description5 := getItalicLabelCentered("Latitude Examples: N 31.7619°, S -40.7128°, -65.358579")
|
||
|
description6 := getItalicLabelCentered("Longitude Examples: E 74.0060°, W -106.4850°, -14.1592")
|
||
|
|
||
|
enterLatitudeLabel := getBoldLabelCentered("Enter Latitude:")
|
||
|
enterLongitudeLabel := getBoldLabelCentered("Enter Longitude:")
|
||
|
|
||
|
latitudeEntry := widget.NewEntry()
|
||
|
longitudeEntry := widget.NewEntry()
|
||
|
|
||
|
latitudeEntry.SetPlaceHolder("Enter Latitude...")
|
||
|
longitudeEntry.SetPlaceHolder("Enter Longitude...")
|
||
|
|
||
|
entryGrid := getContainerCentered(container.NewGridWithColumns(2, enterLatitudeLabel, latitudeEntry, enterLongitudeLabel, longitudeEntry))
|
||
|
|
||
|
confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Confirm", theme.ConfirmIcon(), func(){
|
||
|
|
||
|
latitudeEntered := latitudeEntry.Text
|
||
|
longitudeEntered := longitudeEntry.Text
|
||
|
|
||
|
latitudeFormattedA := strings.TrimRight(latitudeEntered, "°NS")
|
||
|
latitudeFormattedB := strings.TrimLeft(latitudeFormattedA, "NS")
|
||
|
latitudeFormattedC := strings.TrimSpace(latitudeFormattedB)
|
||
|
|
||
|
longitudeFormattedA := strings.TrimRight(longitudeEntered, "°EW")
|
||
|
longitudeFormattedB := strings.TrimLeft(longitudeFormattedA, "EW")
|
||
|
longitudeFormattedC := strings.TrimSpace(longitudeFormattedB)
|
||
|
|
||
|
latitudeFloat64, err := helpers.ConvertStringToFloat64(latitudeFormattedC)
|
||
|
if (err != nil){
|
||
|
title := translate("Invalid Latitude.")
|
||
|
dialogMessage := getLabelCentered(translate("Latitude is not a number."))
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
longitudeFloat64, err := helpers.ConvertStringToFloat64(longitudeFormattedC)
|
||
|
if (err != nil){
|
||
|
title := translate("Invalid Longitude.")
|
||
|
dialogMessage := getLabelCentered(translate("Longitude is not a number."))
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
latitudeIsValid := helpers.VerifyLatitude(latitudeFloat64)
|
||
|
if (latitudeIsValid == false){
|
||
|
title := translate("Invalid Latitude.")
|
||
|
dialogMessage := getLabelCentered(translate("Latitude should be a number between -90 and 90"))
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
longitudeIsValid := helpers.VerifyLongitude(longitudeFloat64)
|
||
|
if (longitudeIsValid == false){
|
||
|
title := translate("Invalid Longitude.")
|
||
|
dialogMessage := getLabelCentered(translate("Longitude should be a number between -180 and 180"))
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
setBuildMateProfilePage_ConfirmAddLocation(window, countryExists, countryIdentifier, latitudeFloat64, longitudeFloat64, currentPage, visitOnCompletePage)
|
||
|
}))
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, widget.NewSeparator(), description5, description6, widget.NewSeparator(), entryGrid, confirmButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildMateProfilePage_ConfirmAddLocation(window fyne.Window, newLocationCountryExists bool, newLocationCountryIdentifier int, newLocationLatitude float64, newLocationLongitude float64, previousPage func(), visitOnCompletePage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_ConfirmAddLocation(window, newLocationCountryExists, newLocationCountryIdentifier, newLocationLatitude, newLocationLongitude, previousPage, visitOnCompletePage)}
|
||
|
|
||
|
setLoadingScreen(window, "Build Mate Profile - General", "Loading...")
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getPageSubtitleCentered(translate("Add Location"))
|
||
|
|
||
|
description1 := getBoldLabelCentered("Confirm Add Location?")
|
||
|
description2 := getLabelCentered("This location will be publicly displayed on your profile.")
|
||
|
|
||
|
countryLabel := getLabelCentered("Country:")
|
||
|
|
||
|
getLocationCountryLabelText := func()(string, error){
|
||
|
if (newLocationCountryExists == false){
|
||
|
result := translate("None")
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
countryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(newLocationCountryIdentifier)
|
||
|
if (err != nil){ return "", err }
|
||
|
|
||
|
countryNamesList := countryObject.NamesList
|
||
|
|
||
|
countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")
|
||
|
|
||
|
result := translate(countryDescription)
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
locationCountryLabelText, err := getLocationCountryLabelText()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
locationCountryLabel := getBoldLabel(locationCountryLabelText)
|
||
|
|
||
|
coordinatesLabel := getLabelCentered("Coordinates:")
|
||
|
|
||
|
locationLatitudeString := helpers.ConvertFloat64ToString(newLocationLatitude)
|
||
|
locationLongitudeString := helpers.ConvertFloat64ToString(newLocationLongitude)
|
||
|
|
||
|
locationCoordinatesLabel := getBoldLabel(locationLatitudeString + "°, " + locationLongitudeString + "°")
|
||
|
|
||
|
cityLabel := getLabelCentered("City:")
|
||
|
|
||
|
getCityContent := func()(*fyne.Container, error){
|
||
|
|
||
|
// We will either find the exact city, or find the closest city
|
||
|
|
||
|
cityName, cityState, cityCountryIdentifier, cityDistanceKilometers, err := worldLocations.GetClosestCityFromCoordinates(newLocationLatitude, newLocationLongitude)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
if (cityDistanceKilometers == 0){
|
||
|
|
||
|
locationCityFormatted := cityName + ", " + cityState
|
||
|
|
||
|
locationCityLabel := getBoldLabelCentered(locationCityFormatted)
|
||
|
|
||
|
return locationCityLabel, nil
|
||
|
}
|
||
|
|
||
|
getNearbyCityDistanceFormattedString := func()(string, error){
|
||
|
|
||
|
currentUnitsExist, currentUnits, err := globalSettings.GetSetting("MetricOrImperial")
|
||
|
if (err != nil){ return "", err }
|
||
|
|
||
|
if (currentUnitsExist == true && currentUnits == "Imperial"){
|
||
|
|
||
|
distanceMiles, err := helpers.ConvertKilometersToMiles(cityDistanceKilometers)
|
||
|
if (err != nil) { return "", err }
|
||
|
|
||
|
distanceMilesString := helpers.ConvertFloat64ToStringRounded(distanceMiles, 1)
|
||
|
|
||
|
result := distanceMilesString + " miles"
|
||
|
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
distanceKilometersString := helpers.ConvertFloat64ToStringRounded(cityDistanceKilometers, 1)
|
||
|
|
||
|
result := distanceKilometersString + " kilometers"
|
||
|
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
nearbyCityDistanceFormattedString, err := getNearbyCityDistanceFormattedString()
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
getNearbyCityNameFormatted := func()(string, error){
|
||
|
|
||
|
if (cityCountryIdentifier == newLocationCountryIdentifier){
|
||
|
result := cityName + ", " + cityState
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
cityCountryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(cityCountryIdentifier)
|
||
|
if (err != nil){ return "", err }
|
||
|
|
||
|
cityCountryNamesList := cityCountryObject.NamesList
|
||
|
|
||
|
countryPrimaryName := cityCountryNamesList[0]
|
||
|
|
||
|
countryNameTranslated := translate(countryPrimaryName)
|
||
|
|
||
|
result := cityName + ", " + cityState + ", " + countryNameTranslated
|
||
|
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
nearbyCityNameFormatted, err := getNearbyCityNameFormatted()
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
nearbyCityNameFormattedAndTrimmed, _, err := helpers.TrimAndFlattenString(nearbyCityNameFormatted, 40)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
locationDistanceLabel := getBoldLabelCentered(nearbyCityDistanceFormattedString + " from")
|
||
|
|
||
|
locationCityNameLabel := getBoldLabelCentered(nearbyCityNameFormattedAndTrimmed)
|
||
|
|
||
|
cityInfoContainer := container.NewVBox(locationDistanceLabel, locationCityNameLabel)
|
||
|
|
||
|
return cityInfoContainer, nil
|
||
|
}
|
||
|
|
||
|
cityContent, err := getCityContent()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
addLocationFunction := func()error{
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Location exists
|
||
|
// -float64: Location latitude
|
||
|
// -float64: Location longitude
|
||
|
// -bool: Location country exists
|
||
|
// -int: Location country identifier
|
||
|
// -error
|
||
|
getMyLocationInfo := func(rank string)(bool, float64, float64, bool, int, error){
|
||
|
|
||
|
if (rank != "Primary" && rank != "Secondary"){
|
||
|
return false, 0, 0, false, 0, errors.New("getMyLocationInfo called with invalid rank: " + rank)
|
||
|
}
|
||
|
|
||
|
exists, locationLatitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLatitude")
|
||
|
if (err != nil) { return false, 0, 0, false, 0, err }
|
||
|
if (exists == false){
|
||
|
|
||
|
return false, 0, 0, false, 0, nil
|
||
|
}
|
||
|
|
||
|
exists, locationLongitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLongitude")
|
||
|
if (err != nil) { return false, 0, 0, false, 0, err }
|
||
|
if (exists == false){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains " + rank + "LocationLatitude but missing " + rank + "LocationLongitude")
|
||
|
}
|
||
|
|
||
|
locationLatitudeFloat64, err := helpers.ConvertStringToFloat64(locationLatitude)
|
||
|
if (err != nil){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLatitude")
|
||
|
}
|
||
|
|
||
|
locationLongitudeFloat64, err := helpers.ConvertStringToFloat64(locationLongitude)
|
||
|
if (err != nil){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLongitude")
|
||
|
}
|
||
|
|
||
|
exists, locationCountryIdentifier, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationCountry")
|
||
|
if (err != nil) { return false, 0, 0, false, 0, err }
|
||
|
if (exists == false){
|
||
|
return true, locationLatitudeFloat64, locationLongitudeFloat64, false, 0, nil
|
||
|
}
|
||
|
|
||
|
locationCountryIdentifierInt, err := helpers.ConvertStringToInt(locationCountryIdentifier)
|
||
|
if (err != nil){
|
||
|
return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationCountry: " + locationCountryIdentifier)
|
||
|
}
|
||
|
|
||
|
return true, locationLatitudeFloat64, locationLongitudeFloat64, true, locationCountryIdentifierInt, nil
|
||
|
}
|
||
|
|
||
|
primaryLocationExists, primaryLocationLatitude, primaryLocationLongitude, primaryLocationCountryExists, primaryLocationCountryIdentifier, err := getMyLocationInfo("Primary")
|
||
|
if (err != nil){ return err }
|
||
|
|
||
|
secondaryLocationExists, _, _, _, _, err := getMyLocationInfo("Secondary")
|
||
|
if (err != nil){ return err }
|
||
|
|
||
|
if (primaryLocationExists == false && secondaryLocationExists == true){
|
||
|
return errors.New("My Profile has a secondary location, but not a primary location.")
|
||
|
}
|
||
|
if (primaryLocationExists == true && secondaryLocationExists == true){
|
||
|
return errors.New("Trying to add a location to a profile with 2 locations.")
|
||
|
}
|
||
|
|
||
|
if (primaryLocationExists == false){
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLatitude", locationLatitudeString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLongitude", locationLongitudeString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
if (newLocationCountryExists == true){
|
||
|
|
||
|
newLocationCountryIdentifierString := helpers.ConvertIntToString(newLocationCountryIdentifier)
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData("Mate", "PrimaryLocationCountry", newLocationCountryIdentifierString)
|
||
|
if (err != nil) { return err }
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Primary location exists
|
||
|
// We see if the location already exists
|
||
|
|
||
|
if (primaryLocationLatitude == newLocationLatitude && primaryLocationLongitude == newLocationLongitude){
|
||
|
if (newLocationCountryExists == false && primaryLocationCountryExists == false){
|
||
|
// Location is identical, Nothing left to do.
|
||
|
return nil
|
||
|
}
|
||
|
if (newLocationCountryExists == true && primaryLocationCountryExists == true && primaryLocationCountryIdentifier == newLocationCountryIdentifier){
|
||
|
// Location is identical, Nothing left to do.
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "SecondaryLocationLatitude", locationLatitudeString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "SecondaryLocationLongitude", locationLongitudeString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
if (newLocationCountryExists == true){
|
||
|
|
||
|
newLocationCountryIdentifierString := helpers.ConvertIntToString(newLocationCountryIdentifier)
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData("Mate", "SecondaryLocationCountry", newLocationCountryIdentifierString)
|
||
|
if (err != nil) { return err }
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Confirm", theme.ConfirmIcon(), func(){
|
||
|
|
||
|
err := addLocationFunction()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
visitOnCompletePage()
|
||
|
}))
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), countryLabel, locationCountryLabel, widget.NewSeparator(), coordinatesLabel, locationCoordinatesLabel, widget.NewSeparator(), cityLabel, cityContent, widget.NewSeparator(), confirmButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildMateProfilePage_Photos(window fyne.Window, currentPhotoIndex int, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_Photos(window, currentPhotoIndex, previousPage)}
|
||
|
|
||
|
pageTitle := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
pageSubtitle := getBoldLabelCentered(translate("Photos"))
|
||
|
|
||
|
description1 := getLabelCentered(translate("Add photos to your Mate profile."))
|
||
|
description2 := getLabelCentered(translate("You can add 5 photos."))
|
||
|
|
||
|
addImageFileCallbackFunction := func(fileObject fyne.URIReadCloser, err error){
|
||
|
|
||
|
if (err != nil) {
|
||
|
title := translate("Failed to open image file.")
|
||
|
dialogMessage := getLabelCentered(translate("Report this error to Seekia developers: " + err.Error()))
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (fileObject == nil) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
setLoadingScreen(window, "Add Image", "Importing image...")
|
||
|
|
||
|
filePath := fileObject.URI().String()
|
||
|
filePath = strings.TrimPrefix(filePath, "file://")
|
||
|
|
||
|
fileExists, ableToReadImage, imageObject, err := imagery.ReadImageFile(filePath)
|
||
|
if (err != nil) {
|
||
|
currentPage()
|
||
|
title := translate("Failed to open image file.")
|
||
|
dialogMessage := getLabelCentered(translate("Report this error to Seekia developers:") + " " + err.Error())
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
if (fileExists == false) {
|
||
|
currentPage()
|
||
|
title := translate("Failed to open image file.")
|
||
|
dialogMessage := getLabelCentered(translate("Report this error to Seekia developers: Image file not found."))
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (ableToReadImage == false) {
|
||
|
currentPage()
|
||
|
title := translate("Failed to import image file.")
|
||
|
dialogMessageA := getLabelCentered(translate("Seekia only supports these image file formats:"))
|
||
|
dialogMessageB := getLabelCentered("JPG, JPEG, PNG, WEBP")
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// We resize image to something that will be more managable when we edit it
|
||
|
// When we export it, we will downsize it even further
|
||
|
imageObjectResized, err := imagery.DownsizeGolangImage(imageObject, 1500)
|
||
|
if (err != nil) {
|
||
|
currentPage()
|
||
|
title := translate("Failed To Process Image File.")
|
||
|
dialogMessageA := getLabelCentered(translate("Your file may be too large."))
|
||
|
|
||
|
errorString := err.Error()
|
||
|
|
||
|
errorTrimmed, _, err := helpers.TrimAndFlattenString(errorString, 20)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
errorTrimmedLabel := getBoldLabel("Error: " + errorTrimmed)
|
||
|
|
||
|
viewFullErrorButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
|
||
|
setViewTextPage(window, "Viewing Error", errorString, false, currentPage)
|
||
|
})
|
||
|
|
||
|
errorDescriptionRow := container.NewHBox(layout.NewSpacer(), errorTrimmedLabel, viewFullErrorButton, layout.NewSpacer())
|
||
|
|
||
|
dialogContent := container.NewVBox(dialogMessageA, errorDescriptionRow)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
setSubmitImageToSubmitPageFunction := func(finalImage image.Image, prevPage func()){
|
||
|
|
||
|
setLoadingScreen(window, "Add Image To Mate Profile", "Compressing Image...")
|
||
|
|
||
|
newImageBase64String, err := imagery.ConvertImageObjectToStandardWebpBase64String(finalImage)
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, prevPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
setConfirmAddImageToMyMateProfilePage(window, newImageBase64String, prevPage, currentPage)
|
||
|
}
|
||
|
|
||
|
setEditImagePage(window, imageObjectResized, false, nil, imageObjectResized, currentPage, setSubmitImageToSubmitPageFunction)
|
||
|
}
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Any images exist
|
||
|
// -[]string: Webp Base64 images list
|
||
|
// -error
|
||
|
getCurrentImagesList := func()(bool, []string, error){
|
||
|
|
||
|
exists, photosAttributeString, err := myLocalProfiles.GetProfileData("Mate", "Photos")
|
||
|
if (err != nil){ return false, nil, err }
|
||
|
if (exists == false){
|
||
|
return false, nil, nil
|
||
|
}
|
||
|
|
||
|
webpImagesList := strings.Split(photosAttributeString, "+")
|
||
|
|
||
|
if (len(webpImagesList) > 5){
|
||
|
return false, nil, errors.New("My Photos attribute malformed: More than 5 photos")
|
||
|
}
|
||
|
|
||
|
return true, webpImagesList, nil
|
||
|
}
|
||
|
|
||
|
anyImagesExist, myCurrentBase64WebpsList, err := getCurrentImagesList()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
if (anyImagesExist == false){
|
||
|
|
||
|
noImagesExistLabel := getBoldLabelCentered("No Photos Exist.")
|
||
|
|
||
|
addImageButton := getWidgetCentered(widget.NewButtonWithIcon("Add Image", theme.ContentAddIcon(), func(){
|
||
|
dialog.ShowFileOpen(addImageFileCallbackFunction, window)
|
||
|
}))
|
||
|
|
||
|
page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), addImageButton, widget.NewSeparator(), noImagesExistLabel)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
numberOfImages := len(myCurrentBase64WebpsList)
|
||
|
|
||
|
addImageButton := getWidgetCentered(widget.NewButtonWithIcon("Add Image", theme.ContentAddIcon(), func(){
|
||
|
|
||
|
if (numberOfImages >= 5){
|
||
|
title := translate("Image Limit Reached.")
|
||
|
dialogMessageA := getLabelCentered(translate("Your profile can only contain 5 images."))
|
||
|
dialogMessageB := getLabelCentered(translate("Delete existing images to add more."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
dialog.ShowFileOpen(addImageFileCallbackFunction, window)
|
||
|
}))
|
||
|
|
||
|
myPhotosLabel := getBoldLabelCentered("My Photos:")
|
||
|
|
||
|
// We use this function to fix an out-of-bound currentPhotoIndex
|
||
|
getCurrentImageIndex := func()int{
|
||
|
if (currentPhotoIndex < 0){
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
finalIndex := len(myCurrentBase64WebpsList) - 1
|
||
|
if (currentPhotoIndex > finalIndex){
|
||
|
return finalIndex
|
||
|
}
|
||
|
return currentPhotoIndex
|
||
|
}
|
||
|
|
||
|
currentImageIndex := getCurrentImageIndex()
|
||
|
|
||
|
selectButtonsRow := container.NewHBox(layout.NewSpacer())
|
||
|
|
||
|
for imageIndex, _ := range myCurrentBase64WebpsList{
|
||
|
|
||
|
imageIndexString := helpers.ConvertIntToString(imageIndex+1)
|
||
|
|
||
|
selectButton := widget.NewButton(imageIndexString, func(){
|
||
|
setBuildMateProfilePage_Photos(window, imageIndex, previousPage)
|
||
|
})
|
||
|
|
||
|
if (imageIndex == currentImageIndex){
|
||
|
selectButton.Importance = widget.HighImportance
|
||
|
}
|
||
|
|
||
|
selectButtonsRow.Add(selectButton)
|
||
|
}
|
||
|
|
||
|
selectButtonsRow.Add(layout.NewSpacer())
|
||
|
|
||
|
currentImageBase64 := myCurrentBase64WebpsList[currentImageIndex]
|
||
|
|
||
|
imageObject, err := imagery.ConvertWEBPBase64StringToCroppedDownsizedImageObject(currentImageBase64)
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fyneImageObject := canvas.NewImageFromImage(imageObject)
|
||
|
fyneImageObject.FillMode = canvas.ImageFillContain
|
||
|
fyneImageSize := getCustomFyneSize(50)
|
||
|
fyneImageObject.SetMinSize(fyneImageSize)
|
||
|
|
||
|
getBackButton := func()fyne.Widget{
|
||
|
if (currentImageIndex <= 0) {
|
||
|
button := widget.NewButton("", nil)
|
||
|
return button
|
||
|
}
|
||
|
button := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func(){
|
||
|
newImageIndex := currentImageIndex - 1
|
||
|
setBuildMateProfilePage_Photos(window, newImageIndex, previousPage)
|
||
|
})
|
||
|
return button
|
||
|
}
|
||
|
getNextButton := func()fyne.Widget{
|
||
|
if (currentImageIndex >= numberOfImages-1) {
|
||
|
button := widget.NewButton("", nil)
|
||
|
return button
|
||
|
}
|
||
|
button := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func(){
|
||
|
newImageIndex := currentImageIndex + 1
|
||
|
setBuildMateProfilePage_Photos(window, newImageIndex, previousPage)
|
||
|
})
|
||
|
return button
|
||
|
}
|
||
|
|
||
|
navigateBackButton := getBackButton()
|
||
|
navigateNextButton := getNextButton()
|
||
|
|
||
|
zoomButton := widget.NewButtonWithIcon("", theme.ZoomInIcon(), func(){
|
||
|
setViewFullpageImagePage(window, imageObject, currentPage)
|
||
|
})
|
||
|
|
||
|
navAndZoomButtonsRow := getContainerCentered(container.NewGridWithRows(1, navigateBackButton, zoomButton, navigateNextButton))
|
||
|
|
||
|
deleteButton := getWidgetCentered(widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){
|
||
|
|
||
|
confirmDialogCallbackFunction := func(response bool){
|
||
|
if (response == false){
|
||
|
return
|
||
|
}
|
||
|
if (numberOfImages == 1){
|
||
|
err := myLocalProfiles.DeleteProfileData("Mate", "Photos")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
currentPage()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
newList, err := helpers.DeleteIndexFromStringList(myCurrentBase64WebpsList, currentImageIndex)
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
newPhotosAttribute := strings.Join(newList, "+")
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "Photos", newPhotosAttribute)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
currentPage()
|
||
|
}
|
||
|
|
||
|
dialogTitle := translate("Confirm Delete Image?")
|
||
|
dialogMessage := getLabelCentered("Confirm to delete this image?")
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustomConfirm(dialogTitle, translate("Yes"), translate("No"), dialogContent, confirmDialogCallbackFunction, window)
|
||
|
}))
|
||
|
|
||
|
page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), addImageButton, widget.NewSeparator(), myPhotosLabel, selectButtonsRow, widget.NewSeparator(), fyneImageObject, widget.NewSeparator(), navAndZoomButtonsRow, deleteButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setConfirmAddImageToMyMateProfilePage(window fyne.Window, newImageBase64String string, previousPage func(), nextPage func()){
|
||
|
|
||
|
currentPage := func(){setConfirmAddImageToMyMateProfilePage(window, newImageBase64String, previousPage, nextPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Add Image To Mate Profile"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
description1 := getBoldLabelCentered("Confirm add image to your Mate profile?")
|
||
|
description2 := getLabelCentered("This image will be displayed on your profile.")
|
||
|
description3 := getLabelCentered("Be aware that the image has been compressed.")
|
||
|
|
||
|
submitButton := getWidgetCentered(widget.NewButtonWithIcon("Add Image", theme.ConfirmIcon(), func(){
|
||
|
|
||
|
setLoadingScreen(window, "Add Image To Mate Profile", "Adding Image...")
|
||
|
|
||
|
addImageFunction := func()error{
|
||
|
|
||
|
getNewAttributeValue := func()(string, error){
|
||
|
|
||
|
exists, currentPhotosAttributeValue, err := myLocalProfiles.GetProfileData("Mate", "Photos")
|
||
|
if (err != nil) { return "", err }
|
||
|
if (exists == false){
|
||
|
return newImageBase64String, nil
|
||
|
}
|
||
|
existingWebpPhotosList := strings.Split(currentPhotosAttributeValue, "+")
|
||
|
|
||
|
newList := append(existingWebpPhotosList, newImageBase64String)
|
||
|
|
||
|
newListJoined := strings.Join(newList, "+")
|
||
|
|
||
|
return newListJoined, nil
|
||
|
}
|
||
|
|
||
|
newAttributeValue, err := getNewAttributeValue()
|
||
|
if (err != nil){ return err }
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "Photos", newAttributeValue)
|
||
|
if (err != nil){ return err }
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
err := addImageFunction()
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
nextPage()
|
||
|
}))
|
||
|
|
||
|
croppedImageObject, err := imagery.ConvertWEBPBase64StringToCroppedDownsizedImageObject(newImageBase64String)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
currentImageFyne := canvas.NewImageFromImage(croppedImageObject)
|
||
|
currentImageFyne.FillMode = canvas.ImageFillContain
|
||
|
|
||
|
viewFullpageButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.ZoomInIcon(), func(){
|
||
|
setViewFullpageImagePage(window, croppedImageObject, currentPage)
|
||
|
}))
|
||
|
|
||
|
emptyLabelA := widget.NewLabel("")
|
||
|
|
||
|
zoomButtonWithSpacer := container.NewVBox(viewFullpageButton, emptyLabelA)
|
||
|
|
||
|
header := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), submitButton, widget.NewSeparator())
|
||
|
|
||
|
page := container.NewBorder(header, zoomButtonWithSpacer, nil, nil, currentImageFyne)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildProfilePage_Avatar(window fyne.Window, profileType string, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildProfilePage_Avatar(window, profileType, previousPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build " + profileType + " Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getPageSubtitleCentered(translate("Avatar"))
|
||
|
|
||
|
description := getLabelCentered(translate("Choose your avatar."))
|
||
|
|
||
|
currentAvatarLabel := getBoldLabelCentered("Current Avatar:")
|
||
|
|
||
|
getCurrentAvatarIdentifier := func()(int, error){
|
||
|
|
||
|
exists, myAvatarIdentifier, err := myLocalProfiles.GetProfileData(profileType, "Avatar")
|
||
|
if (err != nil) { return 0, err }
|
||
|
if (exists == false){
|
||
|
return 2929, nil
|
||
|
}
|
||
|
myAvatarIdentifierInt, err := helpers.ConvertStringToInt(myAvatarIdentifier)
|
||
|
if (err != nil) {
|
||
|
return 0, errors.New("MyLocalProfile is malformed: Invalid avatar: " + myAvatarIdentifier)
|
||
|
}
|
||
|
|
||
|
return myAvatarIdentifierInt, nil
|
||
|
}
|
||
|
|
||
|
myAvatarIdentifier, err := getCurrentAvatarIdentifier()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
emojiImage, err := getEmojiImageObject(myAvatarIdentifier)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
emojiImageFyne := canvas.NewImageFromImage(emojiImage)
|
||
|
|
||
|
nextPageFunction := func(newEmojiIdentifier int){
|
||
|
|
||
|
isValid := imageFiles.VerifyEmojiIdentifier(newEmojiIdentifier)
|
||
|
if (isValid == false){
|
||
|
setErrorEncounteredPage(window, errors.New("Invalid emoji identifier selected from chooseEmojiPage"), currentPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
newEmojiIdentifierString := helpers.ConvertIntToString(newEmojiIdentifier)
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData(profileType, "Avatar", newEmojiIdentifierString)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
currentPage()
|
||
|
}
|
||
|
|
||
|
chooseAvatarButton := getWidgetCentered(widget.NewButtonWithIcon("Choose Avatar", theme.GridIcon(), func(){
|
||
|
setChooseEmojiPage(window, "Choose Avatar", "People", 0, currentPage, nextPageFunction)
|
||
|
}))
|
||
|
|
||
|
emptyLabelA := widget.NewLabel("")
|
||
|
emptyLabelB := widget.NewLabel("")
|
||
|
emptyLabelC := widget.NewLabel("")
|
||
|
|
||
|
chooseAvatarButtonHeightened := container.NewVBox(chooseAvatarButton, emptyLabelA, emptyLabelB, emptyLabelC)
|
||
|
|
||
|
avatarWithButton := getContainerCentered(container.NewGridWithColumns(1, emojiImageFyne, chooseAvatarButtonHeightened))
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), currentAvatarLabel, avatarWithButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildMateProfilePage_Sexuality(window fyne.Window, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_Sexuality(window, previousPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
pageSubtitle := getPageSubtitleCentered(translate("Sexuality"))
|
||
|
|
||
|
description := getLabelCentered(translate("What sex(es) are you interested in mating with?"))
|
||
|
|
||
|
option1Translated := translate("Male")
|
||
|
option2Translated := translate("Female")
|
||
|
option3Translated := translate("Male And Female")
|
||
|
|
||
|
untranslatedOptionsMap := map[string]string{
|
||
|
option1Translated: "Male",
|
||
|
option2Translated: "Female",
|
||
|
option3Translated: "Male And Female",
|
||
|
}
|
||
|
|
||
|
sexualitySelectorOptions := []string{option1Translated, option2Translated, option3Translated}
|
||
|
|
||
|
sexualitySelector := widget.NewRadioGroup(sexualitySelectorOptions, func(response string){
|
||
|
|
||
|
if (response == ""){
|
||
|
myLocalProfiles.DeleteProfileData("Mate", "Sexuality")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
responseUntranslated, exists := untranslatedOptionsMap[response]
|
||
|
if (exists == false){
|
||
|
setErrorEncounteredPage(window, errors.New("untranslatedOptionsMap missing response: " + response), currentPage)
|
||
|
return
|
||
|
}
|
||
|
myLocalProfiles.SetProfileData("Mate", "Sexuality", responseUntranslated)
|
||
|
})
|
||
|
|
||
|
exists, currentSexuality, err := myLocalProfiles.GetProfileData("Mate", "Sexuality")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
if (exists == true){
|
||
|
if (currentSexuality != "Male" && currentSexuality != "Female" && currentSexuality != "Male And Female"){
|
||
|
setErrorEncounteredPage(window, errors.New("MyLocalProfiles contains invalid sexuality: " + currentSexuality), previousPage)
|
||
|
return
|
||
|
}
|
||
|
sexualitySelector.Selected = translate(currentSexuality)
|
||
|
}
|
||
|
sexualitySelectorCentered := getWidgetCentered(sexualitySelector)
|
||
|
|
||
|
noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){
|
||
|
myLocalProfiles.DeleteProfileData("Mate", "Sexuality")
|
||
|
currentPage()
|
||
|
}))
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description, widget.NewSeparator(), sexualitySelectorCentered, noResponseButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildMateProfilePage_Tags(window fyne.Window, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_Tags(window, previousPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getBoldLabelCentered(translate("Tags"))
|
||
|
|
||
|
description1 := getLabelCentered("Add tags to your profile.")
|
||
|
description2 := getLabelCentered("Users can search for matches whose tags match custom terms.")
|
||
|
|
||
|
addTagButton := getWidgetCentered(widget.NewButtonWithIcon("Add Tag", theme.ContentAddIcon(), func(){
|
||
|
setBuildMateProfilePage_AddTag(window, currentPage, currentPage)
|
||
|
}))
|
||
|
|
||
|
myTagsExist, myTagsAttributeValue, err := myLocalProfiles.GetProfileData("Mate", "Tags")
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
if (myTagsExist == false){
|
||
|
|
||
|
noTagsExistLabel := getBoldLabelCentered("No tags exist.")
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), addTagButton, widget.NewSeparator(), noTagsExistLabel)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
getTagsGrid := func()(*fyne.Container, error){
|
||
|
|
||
|
myTagsList := strings.Split(myTagsAttributeValue, "+&")
|
||
|
|
||
|
nameLabel := getItalicLabelCentered("Name")
|
||
|
emptyLabel := widget.NewLabel("")
|
||
|
|
||
|
tagNameColumn := container.NewVBox(nameLabel, widget.NewSeparator())
|
||
|
deleteButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator())
|
||
|
|
||
|
for _, tagName := range myTagsList{
|
||
|
|
||
|
tagNameLabel := getBoldLabelCentered(tagName)
|
||
|
|
||
|
deleteButton := widget.NewButtonWithIcon("", theme.DeleteIcon(), func(){
|
||
|
|
||
|
newList, deletedAny := helpers.DeleteAllMatchingItemsFromStringList(myTagsList, tagName)
|
||
|
if (deletedAny == false){
|
||
|
setErrorEncounteredPage(window, errors.New("Cannot delete tag: tag not found."), currentPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (len(newList) == 0){
|
||
|
err := myLocalProfiles.DeleteProfileData("Mate", "Tags")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
currentPage()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
newTagsAttributeValue := strings.Join(newList, "+&")
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData("Mate", "Tags", newTagsAttributeValue)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, currentPage)
|
||
|
return
|
||
|
}
|
||
|
currentPage()
|
||
|
})
|
||
|
|
||
|
tagNameColumn.Add(tagNameLabel)
|
||
|
deleteButtonsColumn.Add(deleteButton)
|
||
|
|
||
|
tagNameColumn.Add(widget.NewSeparator())
|
||
|
deleteButtonsColumn.Add(widget.NewSeparator())
|
||
|
}
|
||
|
|
||
|
tagsGrid := container.NewHBox(layout.NewSpacer(), tagNameColumn, deleteButtonsColumn, layout.NewSpacer())
|
||
|
|
||
|
return tagsGrid, nil
|
||
|
}
|
||
|
|
||
|
tagsGrid, err := getTagsGrid()
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), addTagButton, widget.NewSeparator(), tagsGrid)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildMateProfilePage_AddTag(window fyne.Window, previousPage func(), nextPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_AddTag(window, previousPage, nextPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
subtitle := getPageSubtitleCentered(translate("Add Tag"))
|
||
|
|
||
|
enterTagNameLabel := getBoldLabelCentered(translate("Enter Tag Name:"))
|
||
|
|
||
|
enterTagEntry := widget.NewEntry()
|
||
|
enterTagEntry.SetPlaceHolder("Enter Tag Name...")
|
||
|
|
||
|
myTagsExist, myTagsAttributeValue, err := myLocalProfiles.GetProfileData("Mate", "Tags")
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
addTagButton := getWidgetCentered(widget.NewButtonWithIcon("Add Tag", theme.ContentAddIcon(), func(){
|
||
|
|
||
|
newTag := enterTagEntry.Text
|
||
|
|
||
|
if (newTag == ""){
|
||
|
title := translate("Cannot Add Tag.")
|
||
|
dialogMessage := getLabelCentered(translate("Tag is empty."))
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
containsDelimiter := strings.Contains(newTag, "+&")
|
||
|
if (containsDelimiter == true){
|
||
|
title := translate("Cannot Add Tag.")
|
||
|
dialogMessageA := getLabelCentered(translate("Tag contains invalid substring: " + `"+&"`))
|
||
|
dialogMessageB := getLabelCentered(translate("Remove this substring and resubmit."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
newTagBytesLength := len(newTag)
|
||
|
|
||
|
if (newTagBytesLength > 40){
|
||
|
|
||
|
newTagBytesLengthString := helpers.ConvertIntToString(newTagBytesLength)
|
||
|
|
||
|
title := translate("Cannot Add Tag.")
|
||
|
dialogMessageA := getLabelCentered(translate("Tag is too long."))
|
||
|
dialogMessageB := getLabelCentered(translate("Tag cannot be longer than 40 bytes."))
|
||
|
dialogMessageC := getLabelCentered(translate("Your tag length:") + newTagBytesLengthString)
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (myTagsExist == false){
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData("Mate", "Tags", newTag)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
nextPage()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
existingTagsList := strings.Split(myTagsAttributeValue, "+&")
|
||
|
|
||
|
if (len(existingTagsList) >= 30){
|
||
|
title := translate("Cannot Create Tag.")
|
||
|
dialogMessageA := getLabelCentered(translate("You cannot have more than 30 tags."))
|
||
|
dialogMessageB := getLabelCentered(translate("You must first delete an existing tag."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
existingTagsTotalByteLength := 0
|
||
|
|
||
|
for _, tagString := range existingTagsList{
|
||
|
|
||
|
if (tagString == ""){
|
||
|
setErrorEncounteredPage(window, errors.New("My tags are invalid: Existing tag is empty."), currentPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
tagByteLength := len(tagString)
|
||
|
|
||
|
if (tagByteLength > 40){
|
||
|
setErrorEncounteredPage(window, errors.New("My tags are invalid: Existing tag is too long."), currentPage)
|
||
|
return
|
||
|
}
|
||
|
existingTagsTotalByteLength += tagByteLength
|
||
|
}
|
||
|
|
||
|
newTagListByteLength := existingTagsTotalByteLength + newTagBytesLength
|
||
|
|
||
|
if (newTagListByteLength > 500){
|
||
|
title := translate("Cannot Add Tag.")
|
||
|
dialogMessageA := getLabelCentered(translate("Byte length of all tags exceeds 500."))
|
||
|
dialogMessageB := getLabelCentered(translate("Enter a shorter tag or delete an existing tag."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
tagAlreadyExists := slices.Contains(existingTagsList, newTag)
|
||
|
if (tagAlreadyExists == true){
|
||
|
title := translate("Cannot Add Tag.")
|
||
|
dialogMessage := getLabelCentered(translate("Tag already exists."))
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
isAllowed := allowedText.VerifyStringIsAllowed(newTag)
|
||
|
if (isAllowed == false){
|
||
|
title := translate("Invalid Tag")
|
||
|
dialogMessageA := getLabelCentered(translate("Your tag contains an invalid character."))
|
||
|
dialogMessageB := getLabelCentered(translate("It must be encoded in UTF-8"))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
containsTabOrNewline := helpers.CheckIfStringContainsTabsOrNewlines(newTag)
|
||
|
if (containsTabOrNewline == true){
|
||
|
title := translate("Invalid Tag")
|
||
|
dialogMessageA := getLabelCentered(translate("Your tag contains a tab or newline character."))
|
||
|
dialogMessageB := getLabelCentered(translate("Remove the character and resubmit."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
newTagsList := append(existingTagsList, newTag)
|
||
|
|
||
|
newTagsAttributeValue := strings.Join(newTagsList, "+&")
|
||
|
|
||
|
err := myLocalProfiles.SetProfileData("Mate", "Tags", newTagsAttributeValue)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
nextPage()
|
||
|
}))
|
||
|
|
||
|
enterTagEntryWithLabel := getContainerCentered(container.NewGridWithColumns(1, enterTagNameLabel, enterTagEntry))
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), enterTagEntryWithLabel, addTagButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildMateProfilePage_Questionnaire(window fyne.Window, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_Questionnaire(window, previousPage)}
|
||
|
|
||
|
pageTitle := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
pageSubtitle := getPageSubtitleCentered(translate("Questionnaire"))
|
||
|
|
||
|
description1 := getLabelCentered("Create a questionnaire for users to fill out.")
|
||
|
description2 := getLabelCentered("You can filter users who choose your desired answers.")
|
||
|
description3 := getLabelCentered("Your desired answers are private.")
|
||
|
|
||
|
buildQuestionnaireIcon, err := getFyneImageIcon("BuildQuestionnaire")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
desiresIcon, err := getFyneImageIcon("Desires")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
viewQuestionnaireIcon, err := getFyneImageIcon("Questionnaire")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
buildQuestionnaireButton := widget.NewButton("Build Questionnaire", func(){
|
||
|
setBuildMateProfilePage_QuestionnaireQuestions(window, 0, currentPage)
|
||
|
})
|
||
|
chooseDesiresButton := widget.NewButton("Choose Desired Answers", func(){
|
||
|
//TODO
|
||
|
showUnderConstructionDialog(window)
|
||
|
})
|
||
|
viewQuestionnaireButton := widget.NewButton("View My Questionnaire", func(){
|
||
|
|
||
|
exists, myQuestionnaireRaw, err := myLocalProfiles.GetProfileData("Mate", "Questionnaire")
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
if (exists == false){
|
||
|
title := translate("No Questions Exist")
|
||
|
dialogMessageA := getLabelCentered("You must add a question before viewing your questionnaire.")
|
||
|
dialogMessageB := getLabelCentered("Add a question on the Build Questionnaire page.")
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
myQuestionnaireObject, err := mateQuestionnaire.ReadQuestionnaireString(myQuestionnaireRaw)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
submitPageFunction := func(_ string, _ func()){
|
||
|
currentPage()
|
||
|
}
|
||
|
|
||
|
emptyMap := make(map[string]string)
|
||
|
setTakeQuestionnairePage(window, myQuestionnaireObject, 0, emptyMap, currentPage, submitPageFunction)
|
||
|
})
|
||
|
|
||
|
buildQuestionnaireButtonWithIcon := container.NewGridWithColumns(1, buildQuestionnaireIcon, buildQuestionnaireButton)
|
||
|
chooseDesiresButtonWithIcon := container.NewGridWithColumns(1, desiresIcon, chooseDesiresButton)
|
||
|
viewQuestionnaireButtonWithIcon := container.NewGridWithColumns(1, viewQuestionnaireIcon, viewQuestionnaireButton)
|
||
|
|
||
|
buttonsSection := getContainerCentered(container.NewGridWithColumns(1, buildQuestionnaireButtonWithIcon, chooseDesiresButtonWithIcon, viewQuestionnaireButtonWithIcon))
|
||
|
|
||
|
page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), buttonsSection)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setBuildMateProfilePage_QuestionnaireQuestions(window fyne.Window, pageIndex int, previousPage func()){
|
||
|
|
||
|
setLoadingScreen(window, "View Questionnaire Questions ", "Loading questionnaire questions...")
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_QuestionnaireQuestions(window, pageIndex, previousPage)}
|
||
|
|
||
|
pageTitle := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
pageSubtitle := getPageSubtitleCentered(translate("Questionnaire Questions"))
|
||
|
|
||
|
exists, myQuestionnaireRaw, err := myLocalProfiles.GetProfileData("Mate", "Questionnaire")
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
if (exists == false) {
|
||
|
noQuestionsExistLabel := getBoldLabelCentered("No questions exist.")
|
||
|
|
||
|
addQuestionButton := getWidgetCentered(widget.NewButtonWithIcon("Add Question", theme.ContentAddIcon(), func(){
|
||
|
setBuildMateProfilePage_AddQuestionnaireQuestion(window, currentPage, currentPage)
|
||
|
}))
|
||
|
|
||
|
page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), noQuestionsExistLabel, addQuestionButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
myQuestionnaireObject, err := mateQuestionnaire.ReadQuestionnaireString(myQuestionnaireRaw)
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
deleteQuestionnaireQuestionFunction := func(questionIdentifier string)error{
|
||
|
|
||
|
newQuestionnaireQuestionsList := make([]mateQuestionnaire.QuestionObject, 0)
|
||
|
|
||
|
for _, questionObject := range myQuestionnaireObject{
|
||
|
|
||
|
currentQuestionIdentifier := questionObject.Identifier
|
||
|
|
||
|
if (currentQuestionIdentifier != questionIdentifier){
|
||
|
newQuestionnaireQuestionsList = append(newQuestionnaireQuestionsList, questionObject)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (len(newQuestionnaireQuestionsList) == 0){
|
||
|
err := myLocalProfiles.DeleteProfileData("Mate", "Questionnaire")
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
newQuestionnaireRaw, err := mateQuestionnaire.CreateQuestionnaireString(newQuestionnaireQuestionsList)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "Questionnaire", newQuestionnaireRaw)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
increaseOrDecreaseQuestionIndexFunction := func(questionIdentifier string, increaseOrDecrease string)error{
|
||
|
|
||
|
// Increase: Move question towards the end of questionnaire
|
||
|
// Decrease: Move question towards the beginning of questionnaire
|
||
|
|
||
|
if (increaseOrDecrease != "Increase" && increaseOrDecrease != "Decrease"){
|
||
|
return errors.New("increaseOrDecreaseQuestionIndexFunction called with invalid increaseOrDecrease: " + increaseOrDecrease)
|
||
|
}
|
||
|
|
||
|
if (len(myQuestionnaireObject) <= 1){
|
||
|
return errors.New("increaseOrDecreaseQuestionIndexFunction called when questionnaire has <=1 question.")
|
||
|
}
|
||
|
|
||
|
getQuestionCurrentIndex := func()(int, error){
|
||
|
|
||
|
for index, questionObject := range myQuestionnaireObject{
|
||
|
|
||
|
currentQuestionIdentifier := questionObject.Identifier
|
||
|
|
||
|
if (currentQuestionIdentifier == questionIdentifier){
|
||
|
return index, nil
|
||
|
}
|
||
|
}
|
||
|
//Should not happen, buttons will only be shown for existing questions
|
||
|
return 0, errors.New("Question to increase/decrease not found.")
|
||
|
}
|
||
|
|
||
|
questionIndex, err := getQuestionCurrentIndex()
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
finalIndex := len(myQuestionnaireObject) - 1
|
||
|
|
||
|
if (questionIndex == 0 && increaseOrDecrease == "Decrease"){
|
||
|
return errors.New("Trying to decrease the index of first question.")
|
||
|
}
|
||
|
if (questionIndex == finalIndex && increaseOrDecrease == "Increase"){
|
||
|
return errors.New("Trying to increase the index of last question.")
|
||
|
}
|
||
|
|
||
|
swapFunction := reflect.Swapper(myQuestionnaireObject)
|
||
|
|
||
|
if (increaseOrDecrease == "Increase"){
|
||
|
swapFunction(questionIndex, questionIndex+1)
|
||
|
} else {
|
||
|
swapFunction(questionIndex-1, questionIndex)
|
||
|
}
|
||
|
|
||
|
newRawQuestionnaire, err := mateQuestionnaire.CreateQuestionnaireString(myQuestionnaireObject)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "Questionnaire", newRawQuestionnaire)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
addQuestionButton := getWidgetCentered(widget.NewButtonWithIcon("Add Question", theme.ContentAddIcon(), func(){
|
||
|
|
||
|
if (len(myQuestionnaireObject) >= 25){
|
||
|
title := translate("Maximum Question Limit Reached")
|
||
|
dialogMessageA := getLabelCentered("You have added the maximum of 25 questions.")
|
||
|
dialogMessageB := getLabelCentered("You must delete a question to add a new question.")
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(title, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
setViewFinalQuestionnaireQuestionsPage := func(){setBuildMateProfilePage_QuestionnaireQuestions(window, 25, previousPage)}
|
||
|
setBuildMateProfilePage_AddQuestionnaireQuestion(window, currentPage, setViewFinalQuestionnaireQuestionsPage)
|
||
|
}))
|
||
|
|
||
|
getQuestionViewIndex := func()int{
|
||
|
|
||
|
if (pageIndex <= 0){
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
numberOfQuestions := len(myQuestionnaireObject)
|
||
|
|
||
|
finalQuestionIndex := numberOfQuestions - 1
|
||
|
|
||
|
if (pageIndex > finalQuestionIndex){
|
||
|
|
||
|
// Index is out of range, show last page of questions
|
||
|
|
||
|
if (numberOfQuestions <= 5){
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
lastPageNumberOfQuestions := numberOfQuestions % 5
|
||
|
if (lastPageNumberOfQuestions == 0){
|
||
|
lastPageViewIndex := finalQuestionIndex - 4
|
||
|
return lastPageViewIndex
|
||
|
}
|
||
|
lastPageViewIndex := finalQuestionIndex - lastPageNumberOfQuestions + 1
|
||
|
return lastPageViewIndex
|
||
|
}
|
||
|
|
||
|
return pageIndex
|
||
|
}
|
||
|
|
||
|
questionViewIndex := getQuestionViewIndex()
|
||
|
|
||
|
getQuestionsGrid := func()(*fyne.Container, error){
|
||
|
|
||
|
emptyLabelA := widget.NewLabel("")
|
||
|
typeLabel := getItalicLabelCentered("Type")
|
||
|
contentLabel := getItalicLabelCentered("Content")
|
||
|
emptyLabelB := widget.NewLabel("")
|
||
|
|
||
|
questionIndexColumn := container.NewVBox(emptyLabelA, widget.NewSeparator())
|
||
|
questionTypeColumn := container.NewVBox(typeLabel, widget.NewSeparator())
|
||
|
questionContentColumn := container.NewVBox(contentLabel, widget.NewSeparator())
|
||
|
buttonsColumn := container.NewVBox(emptyLabelB, widget.NewSeparator())
|
||
|
|
||
|
addQuestionRow := func(questionIndex int, questionObject mateQuestionnaire.QuestionObject)error{
|
||
|
|
||
|
questionIdentifier := questionObject.Identifier
|
||
|
questionType := questionObject.Type
|
||
|
questionContent := questionObject.Content
|
||
|
questionOptions := questionObject.Options
|
||
|
|
||
|
getQuestionTypeText := func()(string, error){
|
||
|
|
||
|
if (questionType == "Entry"){
|
||
|
questionTypeText := "Entry - " + questionOptions
|
||
|
|
||
|
return questionTypeText, nil
|
||
|
}
|
||
|
if (questionType == "Choice"){
|
||
|
|
||
|
maximumAnswersAllowed, choicesListString, delimiterFound := strings.Cut(questionOptions, "#")
|
||
|
if (delimiterFound == false){
|
||
|
return "", errors.New("Invalid choice question options: " + questionOptions)
|
||
|
}
|
||
|
|
||
|
choicesList := strings.Split(choicesListString, "$¥")
|
||
|
numberOfOptionsString := helpers.ConvertIntToString(len(choicesList))
|
||
|
if (len(choicesList) > 6){
|
||
|
return "", errors.New("Invalid choices list: too many choices.")
|
||
|
}
|
||
|
|
||
|
getMaximumAnswersAllowedAdjusted := func()(int, error){
|
||
|
|
||
|
maximumAnswersAllowedInt, err := helpers.ConvertStringToInt(maximumAnswersAllowed)
|
||
|
if (err != nil) { return 0, err }
|
||
|
|
||
|
if (maximumAnswersAllowedInt < 1 || maximumAnswersAllowedInt > 6){
|
||
|
return 0, errors.New("Invalid choice maximum answers allowed")
|
||
|
}
|
||
|
if (maximumAnswersAllowedInt > len(choicesList)){
|
||
|
return len(choicesList), nil
|
||
|
}
|
||
|
return maximumAnswersAllowedInt, nil
|
||
|
}
|
||
|
|
||
|
maximumAnswersAllowedAdjusted, err := getMaximumAnswersAllowedAdjusted()
|
||
|
if (err != nil) { return "", err }
|
||
|
|
||
|
maximumAnswersAllowedAdjustedString := helpers.ConvertIntToString(maximumAnswersAllowedAdjusted)
|
||
|
|
||
|
questionTypeText := numberOfOptionsString + " Choices - Max: " + maximumAnswersAllowedAdjustedString
|
||
|
|
||
|
return questionTypeText, nil
|
||
|
}
|
||
|
return "", errors.New("Malformed question map: invalid question type: " + questionType)
|
||
|
}
|
||
|
|
||
|
questionTypeText, err := getQuestionTypeText()
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
questionContentTrimmed, _, err := helpers.TrimAndFlattenString(questionContent, 15)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
emptyLabelA := widget.NewLabel("")
|
||
|
questionIndexString := helpers.ConvertIntToString(questionIndex + 1)
|
||
|
questionIndexLabel := getBoldLabel(questionIndexString + ".")
|
||
|
questionIndexCell := container.NewVBox(questionIndexLabel, emptyLabelA)
|
||
|
|
||
|
emptyLabelB := widget.NewLabel("")
|
||
|
questionTypeLabel := getBoldLabelCentered(questionTypeText)
|
||
|
questionTypeCell := container.NewVBox(questionTypeLabel, emptyLabelB)
|
||
|
|
||
|
emptyLabelC := widget.NewLabel("")
|
||
|
questionContentLabel := getBoldLabelCentered(questionContentTrimmed)
|
||
|
questionContentCell := container.NewVBox(questionContentLabel, emptyLabelC)
|
||
|
|
||
|
getQuestionIncreaseDecreaseIndexButtons := func()*fyne.Container{
|
||
|
|
||
|
getUpButton := func()fyne.Widget{
|
||
|
if (questionIndex == 0){
|
||
|
emptyButton := widget.NewButton("", nil)
|
||
|
return emptyButton
|
||
|
}
|
||
|
upButton := widget.NewButtonWithIcon("", theme.MoveUpIcon(), func(){
|
||
|
err := increaseOrDecreaseQuestionIndexFunction(questionIdentifier, "Decrease")
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
currentPage()
|
||
|
})
|
||
|
return upButton
|
||
|
}
|
||
|
|
||
|
getDownButton := func()fyne.Widget{
|
||
|
if (questionIndex >= len(myQuestionnaireObject)-1){
|
||
|
emptyButton := widget.NewButton("", nil)
|
||
|
return emptyButton
|
||
|
}
|
||
|
downButton := widget.NewButtonWithIcon("", theme.MoveDownIcon(), func(){
|
||
|
err := increaseOrDecreaseQuestionIndexFunction(questionIdentifier, "Increase")
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
currentPage()
|
||
|
})
|
||
|
return downButton
|
||
|
}
|
||
|
upButton := getUpButton()
|
||
|
downButton := getDownButton()
|
||
|
|
||
|
buttonsGrid := container.NewGridWithColumns(1, upButton, downButton)
|
||
|
return buttonsGrid
|
||
|
}
|
||
|
|
||
|
deleteQuestionButtonFunction := func(){
|
||
|
|
||
|
confirmDialogCallbackFunction := func(response bool){
|
||
|
if (response == false){
|
||
|
return
|
||
|
}
|
||
|
err := deleteQuestionnaireQuestionFunction(questionIdentifier)
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
currentPage()
|
||
|
}
|
||
|
|
||
|
dialogTitle := translate("Confirm Delete Question?")
|
||
|
dialogMessage := getLabelCentered("Confirm to delete this question?")
|
||
|
dialogContent := container.NewVBox(dialogMessage)
|
||
|
dialog.ShowCustomConfirm(dialogTitle, translate("Yes"), translate("No"), dialogContent, confirmDialogCallbackFunction, window)
|
||
|
}
|
||
|
|
||
|
deleteQuestionButton := widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), deleteQuestionButtonFunction)
|
||
|
|
||
|
previewQuestionButton := widget.NewButtonWithIcon("Preview", theme.VisibilityIcon(), func(){
|
||
|
setPreviewMyQuestionnaireQuestionPage(window, questionObject, currentPage)
|
||
|
})
|
||
|
|
||
|
deletePreviewButtons := container.NewVBox(previewQuestionButton, deleteQuestionButton)
|
||
|
|
||
|
getButtonsCell := func()*fyne.Container{
|
||
|
|
||
|
if (len(myQuestionnaireObject) < 2){
|
||
|
return deletePreviewButtons
|
||
|
}
|
||
|
|
||
|
questionIncreaseDecreaseIndexButtons := getQuestionIncreaseDecreaseIndexButtons()
|
||
|
|
||
|
buttonsCell := container.NewHBox(questionIncreaseDecreaseIndexButtons, deletePreviewButtons)
|
||
|
|
||
|
return buttonsCell
|
||
|
}
|
||
|
|
||
|
buttonsCell := getButtonsCell()
|
||
|
|
||
|
questionIndexColumn.Add(questionIndexCell)
|
||
|
questionTypeColumn.Add(questionTypeCell)
|
||
|
questionContentColumn.Add(questionContentCell)
|
||
|
buttonsColumn.Add(buttonsCell)
|
||
|
|
||
|
questionIndexColumn.Add(widget.NewSeparator())
|
||
|
questionTypeColumn.Add(widget.NewSeparator())
|
||
|
questionContentColumn.Add(widget.NewSeparator())
|
||
|
buttonsColumn.Add(widget.NewSeparator())
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
pageQuestionsList := myQuestionnaireObject[questionViewIndex:]
|
||
|
|
||
|
questionsGrid := container.NewHBox(layout.NewSpacer(), questionIndexColumn, questionTypeColumn, questionContentColumn, buttonsColumn, layout.NewSpacer())
|
||
|
|
||
|
counter := 0
|
||
|
for index, questionObject := range pageQuestionsList{
|
||
|
|
||
|
if (counter >= 5){
|
||
|
break
|
||
|
}
|
||
|
|
||
|
questionIndex := questionViewIndex + index
|
||
|
err := addQuestionRow(questionIndex, questionObject)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
counter += 1
|
||
|
}
|
||
|
|
||
|
return questionsGrid, nil
|
||
|
}
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Either button exists
|
||
|
// -*fyne.Container: Buttons
|
||
|
getPageNavigationButtons := func()(bool, *fyne.Container){
|
||
|
|
||
|
finalQuestionIndex := len(myQuestionnaireObject) -1
|
||
|
|
||
|
if (questionViewIndex == 0 && questionViewIndex > (finalQuestionIndex-5)){
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
getPreviousPageButton := func()fyne.Widget{
|
||
|
if (questionViewIndex == 0){
|
||
|
emptyButton := widget.NewButton("", nil)
|
||
|
return emptyButton
|
||
|
}
|
||
|
previousButton := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func(){
|
||
|
setBuildMateProfilePage_QuestionnaireQuestions(window, questionViewIndex-5, previousPage)
|
||
|
})
|
||
|
return previousButton
|
||
|
}
|
||
|
|
||
|
getNextPageButton := func()fyne.Widget{
|
||
|
if (questionViewIndex > (finalQuestionIndex-5)){
|
||
|
emptyButton := widget.NewButton("", nil)
|
||
|
return emptyButton
|
||
|
}
|
||
|
|
||
|
nextButton := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func(){
|
||
|
setBuildMateProfilePage_QuestionnaireQuestions(window, questionViewIndex+5, previousPage)
|
||
|
})
|
||
|
|
||
|
return nextButton
|
||
|
}
|
||
|
|
||
|
previousPageButton := getPreviousPageButton()
|
||
|
nextPageButton := getNextPageButton()
|
||
|
|
||
|
buttonsCentered := getContainerCentered(container.NewGridWithColumns(2, previousPageButton, nextPageButton))
|
||
|
|
||
|
return true, buttonsCentered
|
||
|
}
|
||
|
|
||
|
page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), addQuestionButton, widget.NewSeparator())
|
||
|
|
||
|
navigationButtonsNeeded, navigationButtons := getPageNavigationButtons()
|
||
|
if (navigationButtonsNeeded == true){
|
||
|
page.Add(navigationButtons)
|
||
|
}
|
||
|
|
||
|
questionsGrid, err := getQuestionsGrid()
|
||
|
if (err != nil) {
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
questionsGridCentered := getContainerCentered(questionsGrid)
|
||
|
page.Add(questionsGridCentered)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
func setPreviewMyQuestionnaireQuestionPage(window fyne.Window, questionObject mateQuestionnaire.QuestionObject, previousPage func()){
|
||
|
|
||
|
currentPage := func(){setPreviewMyQuestionnaireQuestionPage(window, questionObject, previousPage)}
|
||
|
|
||
|
title := getPageTitleCentered(translate("Preview Question"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
myResponsesMap := make(map[string]string)
|
||
|
|
||
|
viewQuestionContainer, err := getViewQuestionnaireQuestionContainer(window, currentPage, questionObject, myResponsesMap)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), viewQuestionContainer)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildMateProfilePage_AddQuestionnaireQuestion(window fyne.Window, previousPage func(), afterCreatePage func()){
|
||
|
|
||
|
currentPage := func(){setBuildMateProfilePage_AddQuestionnaireQuestion(window, previousPage, afterCreatePage)}
|
||
|
|
||
|
pageTitle := getPageTitleCentered(translate("Build Mate Profile - General"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
pageSubtitle := getPageSubtitleCentered(translate("Add Questionnaire Question"))
|
||
|
|
||
|
addQuestionToMyQuestionnaireFunction := func(newQuestionObject mateQuestionnaire.QuestionObject)error{
|
||
|
|
||
|
getNewQuestionnaireObject := func()([]mateQuestionnaire.QuestionObject, error){
|
||
|
|
||
|
exists, myQuestionnaireRaw, err := myLocalProfiles.GetProfileData("Mate", "Questionnaire")
|
||
|
if (err != nil) { return nil, err }
|
||
|
if (exists == false) {
|
||
|
|
||
|
newQuestionnaireObject := []mateQuestionnaire.QuestionObject{newQuestionObject}
|
||
|
|
||
|
return newQuestionnaireObject, nil
|
||
|
}
|
||
|
|
||
|
existingQuestionnaireObject, err := mateQuestionnaire.ReadQuestionnaireString(myQuestionnaireRaw)
|
||
|
if (err != nil) { return nil, err }
|
||
|
|
||
|
newQuestionnaireObject := append(existingQuestionnaireObject, newQuestionObject)
|
||
|
|
||
|
return newQuestionnaireObject, nil
|
||
|
}
|
||
|
|
||
|
newQuestionnaireObject, err := getNewQuestionnaireObject()
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
newQuestionnaireString, err := mateQuestionnaire.CreateQuestionnaireString(newQuestionnaireObject)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
err = myLocalProfiles.SetProfileData("Mate", "Questionnaire", newQuestionnaireString)
|
||
|
if (err != nil) { return err }
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
addQuestionDescription := getLabelCentered("Choose the type of question to add:")
|
||
|
|
||
|
iconSize := getCustomFyneSize(10)
|
||
|
|
||
|
choiceIcon, err := getFyneImageIcon("Choice")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
choiceIcon.SetMinSize(iconSize)
|
||
|
|
||
|
entryIcon, err := getFyneImageIcon("Entry")
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
entryIcon.SetMinSize(iconSize)
|
||
|
|
||
|
choiceButton := widget.NewButton("Choice", func(){
|
||
|
setBuildMateProfilePage_AddQuestionnaireQuestion_Choice(window, addQuestionToMyQuestionnaireFunction, currentPage, afterCreatePage)
|
||
|
|
||
|
})
|
||
|
entryButton := widget.NewButton("Entry", func(){
|
||
|
setBuildMateProfilePage_AddQuestionnaireQuestion_Entry(window, addQuestionToMyQuestionnaireFunction, currentPage, afterCreatePage)
|
||
|
})
|
||
|
|
||
|
choiceButtonWithIcon := container.NewVBox(choiceIcon, choiceButton)
|
||
|
entryButtonWithIcon := container.NewVBox(entryIcon, entryButton)
|
||
|
|
||
|
choicesSection := container.NewHBox(layout.NewSpacer(), choiceButtonWithIcon, entryButtonWithIcon, layout.NewSpacer())
|
||
|
|
||
|
page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), addQuestionDescription, choicesSection)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildMateProfilePage_AddQuestionnaireQuestion_Choice(window fyne.Window, addQuestionFunction func(mateQuestionnaire.QuestionObject)error, previousPage func(), afterCreatePage func()){
|
||
|
|
||
|
title := getPageTitleCentered("Add Questionnaire Question - Choice")
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
description := getLabelCentered("A choice question has multiple answers to choose from.")
|
||
|
|
||
|
enterQuestionLabel := getBoldLabelCentered(" Enter Question: ")
|
||
|
|
||
|
enterQuestionEntry := widget.NewEntry()
|
||
|
enterQuestionEntry.SetPlaceHolder("Enter question.")
|
||
|
enterQuestionEntryBoxed := getWidgetBoxed(enterQuestionEntry)
|
||
|
|
||
|
enterQuestionLabelWithEntry := getContainerCentered(container.NewGridWithColumns(1, enterQuestionLabel, enterQuestionEntryBoxed))
|
||
|
|
||
|
maximumAnswersAllowedLabel := widget.NewLabel("Maximum Answers Allowed:")
|
||
|
selectOptions := []string{"1", "2", "3", "4", "5", "6"}
|
||
|
maximumAnswersAllowedSelector := widget.NewSelect(selectOptions, nil)
|
||
|
maximumAnswersAllowedSelector.Selected = "6"
|
||
|
maximumAnswersAllowedInfoButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
|
||
|
|
||
|
dialogTitle := translate("Maximum Answers Allowed")
|
||
|
dialogMessageA := getLabelCentered(translate("Choose the maximum number of answers a user can submit."))
|
||
|
dialogMessageB := getLabelCentered(translate("For example, if you select 1, users can only select 1 choice."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
})
|
||
|
|
||
|
maximumAnswersAllowedRow := container.NewHBox(layout.NewSpacer(), maximumAnswersAllowedLabel, maximumAnswersAllowedSelector, maximumAnswersAllowedInfoButton, layout.NewSpacer())
|
||
|
|
||
|
emptyLabelA := widget.NewLabel("")
|
||
|
choiceContentLabel := getItalicLabelCentered(" Choice Content ")
|
||
|
emptyLabelB := widget.NewLabel("")
|
||
|
|
||
|
choiceIndexColumn := container.NewVBox(emptyLabelA, widget.NewSeparator())
|
||
|
choiceEntryColumn := container.NewVBox(choiceContentLabel, widget.NewSeparator())
|
||
|
clearChoiceButtonsColumn := container.NewVBox(emptyLabelB, widget.NewSeparator())
|
||
|
|
||
|
addChoiceRow := func(index string)func()string{
|
||
|
|
||
|
indexLabel := getBoldLabelCentered(index)
|
||
|
|
||
|
choiceEntry := widget.NewEntry()
|
||
|
choiceEntry.SetPlaceHolder("Enter choice...")
|
||
|
|
||
|
clearChoiceButton := widget.NewButtonWithIcon("Clear", theme.CancelIcon(), func(){
|
||
|
choiceEntry.SetText("")
|
||
|
choiceEntry.SetPlaceHolder("Enter choice...")
|
||
|
})
|
||
|
|
||
|
choiceEntryColumn.Add(choiceEntry)
|
||
|
choiceIndexColumn.Add(indexLabel)
|
||
|
clearChoiceButtonsColumn.Add(clearChoiceButton)
|
||
|
|
||
|
getChoiceFunction := func()string{
|
||
|
return choiceEntry.Text
|
||
|
}
|
||
|
|
||
|
return getChoiceFunction
|
||
|
}
|
||
|
|
||
|
getChoice1Function := addChoiceRow("1.")
|
||
|
getChoice2Function := addChoiceRow("2.")
|
||
|
getChoice3Function := addChoiceRow("3.")
|
||
|
getChoice4Function := addChoiceRow("4.")
|
||
|
getChoice5Function := addChoiceRow("5.")
|
||
|
getChoice6Function := addChoiceRow("6.")
|
||
|
|
||
|
choicesGrid := container.NewHBox(layout.NewSpacer(), choiceIndexColumn, choiceEntryColumn, clearChoiceButtonsColumn, layout.NewSpacer())
|
||
|
|
||
|
submitButton := getWidgetCentered(widget.NewButtonWithIcon("Create Question", theme.ConfirmIcon(), func(){
|
||
|
|
||
|
newQuestionContent := enterQuestionEntry.Text
|
||
|
|
||
|
if (newQuestionContent == ""){
|
||
|
dialogTitle := translate("No question provided.")
|
||
|
dialogMessage := translate("You must enter a question.")
|
||
|
dialogContent := getLabelCentered(dialogMessage)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
isAllowed := allowedText.VerifyStringIsAllowed(newQuestionContent)
|
||
|
if (isAllowed == false){
|
||
|
dialogTitle := translate("Question Is Invalid.")
|
||
|
dialogMessageA := getLabelCentered(translate("Question contains a prohibited character."))
|
||
|
dialogMessageB := getLabelCentered(translate("It must be encoded in UTF-8."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// The choice cannot contain these strings because we user them for encoding
|
||
|
unallowedStringsList := []string{"+&", "%¢"}
|
||
|
|
||
|
for _, unallowedString := range unallowedStringsList{
|
||
|
|
||
|
isContained := strings.Contains(newQuestionContent, unallowedString)
|
||
|
if (isContained == true){
|
||
|
dialogTitle := translate("Question Is Invalid.")
|
||
|
dialogMessageA := getLabelCentered(translate("Question contains prohibited string: ") + unallowedString)
|
||
|
dialogMessageB := getLabelCentered(translate("Remove this string and resubmit."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (len(newQuestionContent) > 500){
|
||
|
|
||
|
questionNumberOfBytesString := helpers.ConvertIntToString(len(newQuestionContent))
|
||
|
|
||
|
dialogTitle := translate("Question Is Invalid.")
|
||
|
dialogMessageA := getLabelCentered(translate("Question is longer than 500 bytes."))
|
||
|
dialogMessageB := getLabelCentered(translate("Your question byte count: " + questionNumberOfBytesString))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
choice1 := getChoice1Function()
|
||
|
choice2 := getChoice2Function()
|
||
|
choice3 := getChoice3Function()
|
||
|
choice4 := getChoice4Function()
|
||
|
choice5 := getChoice5Function()
|
||
|
choice6 := getChoice6Function()
|
||
|
|
||
|
choiceEntryValuesList := []string{choice1, choice2, choice3, choice4, choice5, choice6}
|
||
|
|
||
|
newChoicesList := make([]string, 0)
|
||
|
|
||
|
// We use a map to detect duplicates.
|
||
|
choicesMap := make(map[string]struct{})
|
||
|
|
||
|
for index, choiceString := range choiceEntryValuesList{
|
||
|
|
||
|
if (choiceString == ""){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
choiceIndexString := helpers.ConvertIntToString(index+1)
|
||
|
|
||
|
choiceIsAllowed := allowedText.VerifyStringIsAllowed(choiceString)
|
||
|
if (choiceIsAllowed == false){
|
||
|
dialogTitle := translate("Choice " + choiceIndexString + " Is Invalid.")
|
||
|
dialogMessageA := getLabelCentered(translate("Choice " + choiceIndexString + " contains a prohibited character."))
|
||
|
dialogMessageB := getLabelCentered(translate("It must be encoded in UTF-8."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
unallowedStringsList := []string{"+&", "%¢", "$¥"}
|
||
|
|
||
|
for _, unallowedString := range unallowedStringsList{
|
||
|
|
||
|
isContained := strings.Contains(choiceString, unallowedString)
|
||
|
if (isContained == true){
|
||
|
dialogTitle := translate("Question Is Invalid.")
|
||
|
dialogMessageA := getLabelCentered(translate("Choice " + choiceIndexString + " contains prohibited string: ") + unallowedString)
|
||
|
dialogMessageB := getLabelCentered(translate("Remove this string and resubmit."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (len(choiceString) > 100){
|
||
|
|
||
|
choiceNumberOfBytesString := helpers.ConvertIntToString(len(choiceString))
|
||
|
|
||
|
dialogTitle := translate("Choice Is Invalid.")
|
||
|
dialogMessageA := getLabelCentered(translate("Choice " + choiceIndexString + " is longer than 100 bytes."))
|
||
|
dialogMessageB := getLabelCentered(translate("Choice byte count: " + choiceNumberOfBytesString))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
_, exists := choicesMap[choiceString]
|
||
|
if (exists == true){
|
||
|
dialogTitle := translate("Duplicate Choice Exists")
|
||
|
dialogMessage := translate("Each choice must be unique.")
|
||
|
dialogContent := getLabelCentered(dialogMessage)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
choicesMap[choiceString] = struct{}{}
|
||
|
|
||
|
newChoicesList = append(newChoicesList, choiceString)
|
||
|
}
|
||
|
|
||
|
if (len(newChoicesList) < 2){
|
||
|
dialogTitle := translate("Not enough choices.")
|
||
|
dialogMessage := translate("You must enter at least 2 choices.")
|
||
|
dialogContent := getLabelCentered(dialogMessage)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
maximumAnswersAllowedString := maximumAnswersAllowedSelector.Selected
|
||
|
|
||
|
newQuestionIdentifier, err := helpers.GetNewRandomHexString(9)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
questionOptionsChoicesJoined := strings.Join(newChoicesList, "$¥")
|
||
|
questionOptions := maximumAnswersAllowedString + "#" + questionOptionsChoicesJoined
|
||
|
|
||
|
newQuestionObject := mateQuestionnaire.QuestionObject{
|
||
|
Identifier: newQuestionIdentifier,
|
||
|
Type: "Choice",
|
||
|
Content: newQuestionContent,
|
||
|
Options: questionOptions,
|
||
|
}
|
||
|
|
||
|
err = addQuestionFunction(newQuestionObject)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
afterCreatePage()
|
||
|
}))
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), enterQuestionLabelWithEntry, widget.NewSeparator(), maximumAnswersAllowedRow, widget.NewSeparator(), choicesGrid, submitButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
func setBuildMateProfilePage_AddQuestionnaireQuestion_Entry(window fyne.Window, addQuestionFunction func(mateQuestionnaire.QuestionObject)error, previousPage func(), afterCreatePage func()){
|
||
|
|
||
|
title := getPageTitleCentered(translate("Add Questionnaire Question - Entry"))
|
||
|
|
||
|
backButton := getBackButtonCentered(previousPage)
|
||
|
|
||
|
description1 := getLabelCentered("Entry questions allow responders to enter any text.")
|
||
|
description2 := getLabelCentered("Select Numeric to restrict responses to only allow numbers.")
|
||
|
description3 := getLabelCentered("Numeric allows you to filter users who respond within a desired range.")
|
||
|
|
||
|
enterQuestionLabel := getBoldLabelCentered("Enter Question:")
|
||
|
enterQuestionEntry := widget.NewMultiLineEntry()
|
||
|
enterQuestionEntry.Wrapping = 3
|
||
|
enterQuestionEntry.SetPlaceHolder("Enter question.")
|
||
|
enterQuestionEntryBoxed := getWidgetBoxed(enterQuestionEntry)
|
||
|
|
||
|
numericCheckbox := widget.NewCheck("Numeric Responses Only", nil)
|
||
|
|
||
|
submitButton := getWidgetCentered(widget.NewButtonWithIcon("Add Question", theme.ConfirmIcon(), func(){
|
||
|
|
||
|
newQuestionContent := enterQuestionEntry.Text
|
||
|
|
||
|
if (newQuestionContent == ""){
|
||
|
dialogTitle := translate("No question provided.")
|
||
|
dialogMessage := translate("You must enter a question.")
|
||
|
dialogContent := getLabelCentered(dialogMessage)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (len(newQuestionContent) > 500){
|
||
|
|
||
|
newQuestionBytesString := helpers.ConvertIntToString(len(newQuestionContent))
|
||
|
|
||
|
dialogTitle := translate("Question Is Invalid.")
|
||
|
dialogMessageA := getLabelCentered(translate("Question cannot be longer than 500 bytes."))
|
||
|
dialogMessageB := getLabelCentered(translate("Provided question byte count: " + newQuestionBytesString))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
isAllowed := allowedText.VerifyStringIsAllowed(newQuestionContent)
|
||
|
if (isAllowed == false){
|
||
|
dialogTitle := translate("Question Is Invalid.")
|
||
|
dialogMessageA := getLabelCentered(translate("Question contains a prohibited character."))
|
||
|
dialogMessageB := getLabelCentered(translate("It must be encoded in UTF-8."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// The question cannot contain these strings because we user them for encoding
|
||
|
unallowedStringsList := []string{"+&", "%¢"}
|
||
|
|
||
|
for _, unallowedString := range unallowedStringsList{
|
||
|
|
||
|
isContained := strings.Contains(newQuestionContent, unallowedString)
|
||
|
if (isContained == true){
|
||
|
dialogTitle := translate("Question Is Invalid.")
|
||
|
dialogMessageA := getLabelCentered(translate("Question contains prohibited string: ") + unallowedString)
|
||
|
dialogMessageB := getLabelCentered(translate("Remove this string and resubmit."))
|
||
|
dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
|
||
|
dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
isNumeric := numericCheckbox.Checked
|
||
|
|
||
|
newQuestionIdentifier, err := helpers.GetNewRandomHexString(10)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
getQuestionOptions := func()string{
|
||
|
if (isNumeric == true){
|
||
|
return "Numeric"
|
||
|
}
|
||
|
return "Any"
|
||
|
}
|
||
|
questionOptions := getQuestionOptions()
|
||
|
|
||
|
newQuestionObject := mateQuestionnaire.QuestionObject{
|
||
|
|
||
|
Identifier: newQuestionIdentifier,
|
||
|
Type: "Entry",
|
||
|
Content: newQuestionContent,
|
||
|
Options: questionOptions,
|
||
|
}
|
||
|
|
||
|
err = addQuestionFunction(newQuestionObject)
|
||
|
if (err != nil){
|
||
|
setErrorEncounteredPage(window, err, previousPage)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
afterCreatePage()
|
||
|
}))
|
||
|
|
||
|
submitButtonWithCheckbox := container.NewVBox(numericCheckbox, submitButton)
|
||
|
|
||
|
entryWithCheckBoxAndSubmitButton := getContainerCentered(container.NewGridWithColumns(1, enterQuestionEntryBoxed, submitButtonWithCheckbox))
|
||
|
|
||
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), enterQuestionLabel, entryWithCheckBoxAndSubmitButton)
|
||
|
|
||
|
setPageContent(page, window)
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|