seekia/gui/buildProfileGui_General.go

3623 lines
128 KiB
Go
Raw Normal View History

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(){
2024-08-11 14:31:40 +02:00
newList, deletedAny := helpers.DeleteAllMatchingItemsFromList(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)
}