package gui // buildProfileGui_Physical.go implements pages to build the Physical portion of a user's mate profile 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/geneticReferences/traits" import "seekia/resources/geneticReferences/monogenicDiseases" import "seekia/resources/geneticReferences/polygenicDiseases" import "seekia/internal/allowedText" import "seekia/internal/genetics/companyAnalysis" import "seekia/internal/genetics/myChosenAnalysis" import "seekia/internal/genetics/myGenomes" import "seekia/internal/genetics/myPeople" import "seekia/internal/globalSettings" import "seekia/internal/helpers" import "seekia/internal/profiles/myLocalProfiles" import "slices" import "strings" import "errors" func setBuildMateProfileCategoryPage_Physical(window fyne.Window, previousPage func()){ currentPage := func(){ setBuildMateProfileCategoryPage_Physical(window, previousPage) } title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) sexButton := widget.NewButton(translate("Sex"), func(){ setBuildMateProfilePage_Sex(window, currentPage) }) ageButton := widget.NewButton(translate("Age"), func(){ setBuildMateProfilePage_Age(window, currentPage) }) ancestryCompositionButton := widget.NewButton("Ancestry Composition", func(){ setBuildMateProfilePage_23andMe_AncestryComposition(window, currentPage) }) neanderthalVariantsButton := widget.NewButton("Neanderthal Variants", func(){ setBuildMateProfilePage_23andMe_NeanderthalVariants(window, currentPage) }) maternalHaplogroupButton := widget.NewButton("Maternal Haplogroup", func(){ setBuildMateProfilePage_23andMe_Haplogroup(window, "Maternal", currentPage) }) paternalHaplogroupButton := widget.NewButton("Paternal Haplogroup", func(){ setBuildMateProfilePage_23andMe_Haplogroup(window, "Paternal", currentPage) }) geneticInformationButton := widget.NewButton(translate("Genetic Information"), func(){ setBuildMateProfilePage_GeneticInformation(window, currentPage) }) heightButton := widget.NewButton(translate("Height"), func(){ setBuildMateProfilePage_Height(window, currentPage) }) bodyButton := widget.NewButton(translate("Body"), func(){ setBuildMateProfilePage_Body(window, currentPage) }) eyeColorButton := widget.NewButton(translate("Eye Color"), func(){ setBuildMateProfilePage_EyeColor(window, currentPage) }) hairColorButton := widget.NewButton(translate("Hair Color"), func(){ setBuildMateProfilePage_HairColor(window, currentPage) }) hairTextureButton := widget.NewButton(translate("Hair Texture"), func(){ setBuildMateProfilePage_HairTexture(window, currentPage) }) skinColorButton := widget.NewButton(translate("Skin Color"), func(){ setBuildMateProfilePage_SkinColor(window, currentPage) }) infectionsButton := widget.NewButton(translate("Infections"), func(){ setBuildMateProfilePage_Infections(window, currentPage) }) buttonsGrid := container.NewGridWithColumns(1, sexButton, ageButton, ancestryCompositionButton, neanderthalVariantsButton, maternalHaplogroupButton, paternalHaplogroupButton, geneticInformationButton, heightButton, bodyButton, eyeColorButton, hairColorButton, hairTextureButton, skinColorButton, infectionsButton) buttonsGridCentered := getContainerCentered(buttonsGrid) buttonsGridPadded := container.NewPadded(buttonsGridCentered) page := container.NewVBox(title, backButton, widget.NewSeparator(), buttonsGridPadded) setPageContent(page, window) } func setBuildMateProfilePage_Sex(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_Sex(window, previousPage)} pageTitle := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) pageSubtitle := getPageSubtitleCentered(translate("Sex")) sexDescription := widget.NewLabel(translate("What is your physical sex?")) sexHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setSexExplainerPage(window, currentPage) }) sexDescriptionRow := container.NewHBox(layout.NewSpacer(), sexDescription, sexHelpButton, layout.NewSpacer()) option1Translated := translate("Male") option2Translated := translate("Female") option3Translated := translate("Intersex Male") option4Translated := translate("Intersex Female") option5Translated := translate("Intersex") untranslatedOptionsMap := map[string]string{ option1Translated: "Male", option2Translated: "Female", option3Translated: "Intersex Male", option4Translated: "Intersex Female", option5Translated: "Intersex", } sexSelectorOptions := []string{option1Translated, option2Translated, option3Translated, option4Translated, option5Translated} sexSelector := widget.NewRadioGroup(sexSelectorOptions, func(response string){ if (response == ""){ err := myLocalProfiles.DeleteProfileData("Mate", "Sex") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } return } responseUntranslated, exists := untranslatedOptionsMap[response] if (exists == false){ setErrorEncounteredPage(window, errors.New("untranslatedOptionsMap missing response: " + response), currentPage) return } err := myLocalProfiles.SetProfileData("Mate", "Sex", responseUntranslated) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } }) exists, currentSex, err := myLocalProfiles.GetProfileData("Mate", "Sex") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (exists == true){ if (currentSex != "Male" && currentSex != "Female" && currentSex != "Intersex Male" && currentSex != "Intersex Female" && currentSex != "Intersex"){ setErrorEncounteredPage(window, errors.New("MyLocalProfiles contains invalid Sex: " + currentSex), previousPage) return } sexSelector.Selected = translate(currentSex) } sexSelectorCentered := getWidgetCentered(sexSelector) sexNoResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "Sex") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() })) page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), sexDescriptionRow, sexSelectorCentered, sexNoResponseButton) setPageContent(page, window) } func setBuildMateProfilePage_23andMe_AncestryComposition(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_23andMe_AncestryComposition(window, previousPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered("23andMe Ancestry Composition") description := getLabelCentered("Enter your 23andMe ancestry location composition.") exists, myAncestryCompositionAttribute, err := myLocalProfiles.GetProfileData("Mate", "23andMe_AncestryComposition") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (exists == false){ noCompositionFoundLabel := getBoldLabelCentered("No ancestry composition exists.") addCompositionButton := getWidgetCentered(widget.NewButtonWithIcon("Add Composition", theme.ContentAddIcon(), func(){ emptyMapA := make(map[string]float64) emptyMapB := make(map[string]float64) emptyMapC := make(map[string]float64) setBuildMateProfilePage_23andMe_EditAncestryComposition(window, emptyMapA, emptyMapB, emptyMapC, currentPage, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), noCompositionFoundLabel, addCompositionButton) 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_23andMe_AncestryComposition", newVisibilityStatus) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } }) exists, visibilityStatus, err := myLocalProfiles.GetProfileData("Mate", "VisibilityStatus_23andMe_AncestryComposition") 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()) attributeIsValid, myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap, err := companyAnalysis.ReadAncestryCompositionAttribute_23andMe(true, myAncestryCompositionAttribute) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } if (attributeIsValid == false){ setErrorEncounteredPage(window, errors.New("MyLocalProfiles 23andMe ancestry composition attribute is invalid."), currentPage) return } editCompositionButton := widget.NewButtonWithIcon("Edit", theme.DocumentCreateIcon(), func(){ setBuildMateProfilePage_23andMe_EditAncestryComposition(window, myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap, currentPage, currentPage) }) noResponseButton := widget.NewButtonWithIcon("No Response", theme.CancelIcon(), func(){ setBuildMateProfilePage_23andMe_ConfirmDeleteAncestryComposition(window, currentPage, currentPage) }) buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, editCompositionButton, noResponseButton)) myCompositionLabel := getBoldLabelCentered("My Composition:") myCompositionDisplay, err := get23andMeAncestryCompositionDisplay(myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } header := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), visibilitySelectorRow, widget.NewSeparator(), buttonsGrid, widget.NewSeparator(), myCompositionLabel) page := container.NewBorder(header, nil, nil, nil, myCompositionDisplay) setPageContent(page, window) } func setBuildMateProfilePage_23andMe_ConfirmDeleteAncestryComposition(window fyne.Window, previousPage func(), nextPage func()){ currentPage := func(){setBuildMateProfilePage_23andMe_ConfirmDeleteAncestryComposition(window, previousPage, nextPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) description1 := getBoldLabelCentered("Delete Ancestry Composition?") description2 := getLabelCentered("Confirm to delete your 23andMe ancestry composition?") confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Confirm", theme.ConfirmIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "23andMe_AncestryComposition") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } nextPage() })) page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, confirmButton) setPageContent(page, window) } func setBuildMateProfilePage_23andMe_EditAncestryComposition(window fyne.Window, myContinentPercentagesMap map[string]float64, myRegionPercentagesMap map[string]float64, mySubregionPercentagesMap map[string]float64, previousPage func(), afterCompletePage func()){ currentPage := func(){setBuildMateProfilePage_23andMe_EditAncestryComposition(window, myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap, previousPage, afterCompletePage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButtonFunction := func(){ confirmDialogCallbackFunction := func(response bool){ if (response == true){ previousPage() } } dialogTitle := translate("Confirm Go Back") dialogMessageA := getLabelCentered("Confirm to go back?") dialogMessageB := getLabelCentered("You will lose any changes you made.") dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustomConfirm(dialogTitle, translate("Yes"), translate("No"), dialogContent, confirmDialogCallbackFunction, window) } backButton := getBackButtonCentered(backButtonFunction) subtitle := getPageSubtitleCentered("Build 23andMe Ancestry Composition") // We use this function to make changes and return to this page currentPageWithNewPercentagesFunction := func(newContinentPercentagesMap map[string]float64, newRegionPercentagesMap map[string]float64, newSubregionPercentagesMap map[string]float64){ setBuildMateProfilePage_23andMe_EditAncestryComposition(window, newContinentPercentagesMap, newRegionPercentagesMap, newSubregionPercentagesMap, previousPage, afterCompletePage) } addLocationButton := widget.NewButtonWithIcon("Add Location", theme.ContentAddIcon(), func(){ setBuildMateProfilePage_23andMe_AncestryComposition_AddLocation(window, false, "", false, "", false, "", myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap, currentPage, currentPageWithNewPercentagesFunction) }) if (len(myContinentPercentagesMap) == 0 && len(myRegionPercentagesMap) == 0 && len(mySubregionPercentagesMap) == 0){ addLocationButtonCentered := getWidgetCentered(addLocationButton) noCompositionExistsLabel := getBoldLabelCentered("No locations added.") page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), addLocationButtonCentered, widget.NewSeparator(), noCompositionExistsLabel) setPageContent(page, window) return } deleteLocationButton := widget.NewButtonWithIcon("Delete Location", theme.DeleteIcon(), func(){ setBuildMateProfilePage_23andMe_AncestryComposition_DeleteLocation(window, "Continent", myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap, currentPage, currentPageWithNewPercentagesFunction) }) addAndDeleteLocationButtons := getContainerCentered(container.NewGridWithRows(1, addLocationButton, deleteLocationButton)) submitButton := getWidgetCentered(widget.NewButtonWithIcon("Submit", theme.ConfirmIcon(), func(){ inputsAreValid, newAncestryCompositionAttribute, err := companyAnalysis.CreateAncestryCompositionAttribute_23andMe(myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } if (inputsAreValid == false){ dialogTitle := translate("Invalid Ancestry Composition") dialogMessageA := getBoldLabelCentered(translate("The ancestry composition you entered is invalid.")) dialogMessageB := getLabelCentered(translate("All continent percentages must add to 100.")) dialogMessageC := getLabelCentered(translate("All region percentages must add up to their continent percentage.")) dialogMessageD := getLabelCentered(translate("All subregion percentages must add up to their region percentage.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC, dialogMessageD) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) return } setBuildMateProfilePage_23andMe_ConfirmUpdateAncestryComposition(window, newAncestryCompositionAttribute, currentPage, afterCompletePage) })) emptyLabel := widget.NewLabel("") footer := container.NewVBox(widget.NewSeparator(), submitButton, emptyLabel) myCompositionTree, err := get23andMeAncestryCompositionDisplay(myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } header := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), addAndDeleteLocationButtons, widget.NewSeparator()) page := container.NewBorder(header, footer, nil, nil, myCompositionTree) setPageContent(page, window) } func setBuildMateProfilePage_23andMe_ConfirmUpdateAncestryComposition(window fyne.Window, newAncestryCompositionAttribute string, previousPage func(), nextPage func()){ currentPage := func(){setBuildMateProfilePage_23andMe_ConfirmUpdateAncestryComposition(window, newAncestryCompositionAttribute, previousPage, nextPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) description1 := getBoldLabelCentered("Add Ancestry Composition?") description2 := getLabelCentered("Confirm update your ancestry composition?") description3 := getLabelCentered("This will be visible on your profile.") confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Confirm", theme.ConfirmIcon(), func(){ err := myLocalProfiles.SetProfileData("Mate", "23andMe_AncestryComposition", newAncestryCompositionAttribute) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } nextPage() })) page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, confirmButton) setPageContent(page, window) } func setBuildMateProfilePage_23andMe_AncestryComposition_AddLocation( window fyne.Window, continentProvided bool, currentContinent string, regionProvided bool, currentRegion string, subregionProvided bool, currentSubregion string, myContinentPercentagesMap map[string]float64, myRegionPercentagesMap map[string]float64, mySubregionPercentagesMap map[string]float64, previousPage func(), nextPage func(map[string]float64, map[string]float64, map[string]float64)){ title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered("Add 23andMe Ancestry Location") getPageContent := func()(*fyne.Container, error){ continentLabel := getBoldLabel("Continent:") allContinentsList := companyAnalysis.GetAncestryContinentsList_23andMe() handleContinentSelectFunction := func(newContinent string){ if (continentProvided == true && newContinent == currentContinent){ return } setBuildMateProfilePage_23andMe_AncestryComposition_AddLocation(window, true, newContinent, false, "", false, "", myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap, previousPage, nextPage) } continentSelector := widget.NewSelect(allContinentsList, handleContinentSelectFunction) if (continentProvided == true){ continentSelector.Selected = currentContinent } continentRow := container.NewHBox(layout.NewSpacer(), continentLabel, continentSelector, layout.NewSpacer()) if (continentProvided == false){ pageContent := container.NewVBox(continentRow) return pageContent, nil } percentageLabel := getBoldLabelCentered("Percentage:") percentageEntry := widget.NewEntry() percentageEntry.SetPlaceHolder("Enter percentage.") percentageRow := getContainerCentered(container.NewGridWithRows(1, percentageLabel, percentageEntry)) showInvalidPercentageDialog := func(){ dialogTitle := translate("Invalid Percentage") dialogMessageA := getLabelCentered(translate("The location percentage is invalid.")) dialogMessageB := getLabelCentered(translate("You must enter a number between 0 and 100.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) } regionsList, err := companyAnalysis.GetAncestryContinentRegionsList_23andMe(currentContinent) if (err != nil) { return nil, err } if (len(regionsList) == 0){ submitButton := getWidgetCentered(widget.NewButtonWithIcon("Submit", theme.ConfirmIcon(), func(){ newPercentageRaw := percentageEntry.Text newPercentage := strings.TrimSuffix(newPercentageRaw, "%") newPercentageFloat64, err := helpers.ConvertStringToFloat64(newPercentage) if (err != nil){ showInvalidPercentageDialog() return } if (newPercentageFloat64 <= 0 || newPercentageFloat64 > 100){ showInvalidPercentageDialog() return } addContinentFunction := func(){ myContinentPercentagesMap[currentContinent] = newPercentageFloat64 nextPage(myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap) } existingPercentage, exists := myContinentPercentagesMap[currentContinent] if (exists == false){ addContinentFunction() return } // We allow user to confirm they want to overwrite existing value currentPercentageString := helpers.ConvertFloat64ToStringRounded(existingPercentage, 1) confirmDialogCallbackFunction := func(response bool){ if (response == true){ addContinentFunction() } } dialogTitle := translate("Confirm Overwrite Existing") dialogMessageA := getLabelCentered("You already have a percentage for " + currentContinent) dialogMessageB := getLabelCentered("Current Percentage: " + currentPercentageString + "%") dialogMessageC := getLabelCentered("Confirm to overwrite this percentage?") dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC) dialog.ShowCustomConfirm(dialogTitle, translate("Yes"), translate("No"), dialogContent, confirmDialogCallbackFunction, window) })) pageContent := container.NewVBox(continentRow, widget.NewSeparator(), percentageRow, submitButton) return pageContent, nil } regionLabel := getBoldLabel("Region:") handleRegionSelectFunction := func(newRegion string){ if (regionProvided == true && currentRegion == newRegion){ return } setBuildMateProfilePage_23andMe_AncestryComposition_AddLocation(window, true, currentContinent, true, newRegion, false, "", myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap, previousPage, nextPage) } regionSelector := widget.NewSelect(regionsList, handleRegionSelectFunction) if (regionProvided == true){ regionSelector.Selected = currentRegion } regionRow := container.NewHBox(layout.NewSpacer(), regionLabel, regionSelector, layout.NewSpacer()) if (regionProvided == false){ pageContent := container.NewVBox(continentRow, widget.NewSeparator(), regionRow) return pageContent, nil } subregionsList, err := companyAnalysis.GetAncestryRegionSubregionsList_23andMe(currentContinent, currentRegion) if (err != nil) { return nil, err } if (len(subregionsList) == 0){ submitButton := getWidgetCentered(widget.NewButtonWithIcon("Submit", theme.ConfirmIcon(), func(){ newPercentageRaw := percentageEntry.Text newPercentage := strings.TrimSuffix(newPercentageRaw, "%") newPercentageFloat64, err := helpers.ConvertStringToFloat64(newPercentage) if (err != nil){ showInvalidPercentageDialog() return } if (newPercentageFloat64 <= 0 || newPercentageFloat64 > 100){ showInvalidPercentageDialog() return } addRegionFunction := func(){ myRegionPercentagesMap[currentRegion] = newPercentageFloat64 nextPage(myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap) } existingPercentage, exists := myRegionPercentagesMap[currentRegion] if (exists == false){ addRegionFunction() return } // We allow user to confirm they want to overwrite existing value currentPercentageString := helpers.ConvertFloat64ToStringRounded(existingPercentage, 1) confirmDialogCallbackFunction := func(response bool){ if (response == true){ addRegionFunction() } } dialogTitle := translate("Confirm Overwrite Existing") dialogMessageA := getLabelCentered("You already have a percentage for " + currentRegion) dialogMessageB := getLabelCentered("Current Percentage: " + currentPercentageString + "%") dialogMessageC := getLabelCentered("Confirm to overwrite this percentage?") dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC) dialog.ShowCustomConfirm(dialogTitle, translate("Yes"), translate("No"), dialogContent, confirmDialogCallbackFunction, window) })) pageContent := container.NewVBox(continentRow, widget.NewSeparator(), regionRow, widget.NewSeparator(), percentageRow, submitButton) return pageContent, nil } subregionLabel := getBoldLabel("Subregion:") handleSubregionSelectFunction := func(newSubregion string){ if (subregionProvided == true && currentSubregion == newSubregion){ return } setBuildMateProfilePage_23andMe_AncestryComposition_AddLocation(window, true, currentContinent, true, currentRegion, true, newSubregion, myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap, previousPage, nextPage) } subregionSelector := widget.NewSelect(subregionsList, handleSubregionSelectFunction) if (subregionProvided == true){ subregionSelector.Selected = currentSubregion } subregionRow := container.NewHBox(layout.NewSpacer(), subregionLabel, subregionSelector, layout.NewSpacer()) submitButton := getWidgetCentered(widget.NewButtonWithIcon("Submit", theme.ConfirmIcon(), func(){ if (subregionProvided == false){ dialogTitle := translate("Missing Subregion") dialogMessage := getLabelCentered(translate("You must select a subregion.")) dialogContent := container.NewVBox(dialogMessage) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) return } newPercentageRaw := percentageEntry.Text newPercentage := strings.TrimSuffix(newPercentageRaw, "%") newPercentageFloat64, err := helpers.ConvertStringToFloat64(newPercentage) if (err != nil){ showInvalidPercentageDialog() return } if (newPercentageFloat64 <= 0 || newPercentageFloat64 > 100){ showInvalidPercentageDialog() return } addSubregionFunction := func(){ mySubregionPercentagesMap[currentSubregion] = newPercentageFloat64 nextPage(myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap) } existingPercentage, exists := mySubregionPercentagesMap[currentSubregion] if (exists == false){ addSubregionFunction() return } // We allow user to confirm they want to overwrite existing value currentPercentageString := helpers.ConvertFloat64ToStringRounded(existingPercentage, 1) confirmDialogCallbackFunction := func(response bool){ if (response == true){ addSubregionFunction() } } dialogTitle := translate("Confirm Overwrite Existing") dialogMessageA := getLabelCentered("You already have a percentage for " + currentSubregion) dialogMessageB := getLabelCentered("Current Percentage: " + currentPercentageString + "%") dialogMessageC := getLabelCentered("Confirm to overwrite this percentage?") dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC) dialog.ShowCustomConfirm(dialogTitle, translate("Yes"), translate("No"), dialogContent, confirmDialogCallbackFunction, window) })) pageContent := container.NewVBox(continentRow, widget.NewSeparator(), regionRow, widget.NewSeparator(), subregionRow, widget.NewSeparator(), percentageRow, submitButton) return pageContent, nil } pageContent, err := getPageContent() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), pageContent) setPageContent(page, window) } func setBuildMateProfilePage_23andMe_AncestryComposition_DeleteLocation( window fyne.Window, locationType string, myContinentPercentagesMap map[string]float64, myRegionPercentagesMap map[string]float64, mySubregionPercentagesMap map[string]float64, previousPage func(), nextPage func(map[string]float64, map[string]float64, map[string]float64)){ if (locationType != "Continent" && locationType != "Region" && locationType != "Subregion"){ setErrorEncounteredPage(window, errors.New("setBuildMateProfilePage_23andMe_AncestryComposition_DeleteLocation called with invalid locationType: " + locationType), previousPage) return } title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) description := getLabelCentered("Select a location to delete.") locationTypeLabel := getBoldLabelCentered("Location Type") locationTypesList := []string{"Continent", "Region", "Subregion"} handleLocationTypeSelectFunction := func(newLocationType string){ if (newLocationType == locationType){ return } setBuildMateProfilePage_23andMe_AncestryComposition_DeleteLocation(window, newLocationType, myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap, previousPage, nextPage) } locationTypeSelector := widget.NewSelect(locationTypesList, handleLocationTypeSelectFunction) locationTypeSelector.Selected = locationType locationTypeSelectorCentered := getWidgetCentered(locationTypeSelector) // This function either returns a "No (locationType) Exists" label or the locations grid. getLocationsSection := func()(*fyne.Container, error){ if (locationType == "Continent" && len(myContinentPercentagesMap) == 0){ locationsSection := getBoldLabelCentered("No continents exist.") return locationsSection, nil } if (locationType == "Region" && len(myRegionPercentagesMap) == 0){ locationsSection := getBoldLabelCentered("No regions exist.") return locationsSection, nil } if (locationType == "Subregion" && len(mySubregionPercentagesMap) == 0){ locationsSection := getBoldLabelCentered("No subregions exist.") return locationsSection, nil } locationLabel := getItalicLabelCentered(locationType) percentageLabel := getItalicLabelCentered("Percentage") emptyLabel := widget.NewLabel("") locationNameColumn := container.NewVBox(locationLabel, widget.NewSeparator()) locationPercentageColumn := container.NewVBox(percentageLabel, widget.NewSeparator()) deleteButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) addLocationRow := func(locationName string, locationPercentage float64)error{ locationNameLabel := getBoldLabelCentered(locationName) locationPercentageString := helpers.ConvertFloat64ToStringRounded(locationPercentage, 1) locationPercentageFormatted := locationPercentageString + "%" locationPercentageLabel := getBoldLabelCentered(locationPercentageFormatted) deleteLocationButton := widget.NewButtonWithIcon("", theme.DeleteIcon(), func(){ if (locationType == "Continent"){ delete(myContinentPercentagesMap, locationName) } else if (locationType == "Region"){ delete(myRegionPercentagesMap, locationName) } else if (locationType == "Subregion") { delete(mySubregionPercentagesMap, locationName) } nextPage(myContinentPercentagesMap, myRegionPercentagesMap, mySubregionPercentagesMap) }) locationNameColumn.Add(locationNameLabel) locationPercentageColumn.Add(locationPercentageLabel) deleteButtonsColumn.Add(deleteLocationButton) locationNameColumn.Add(widget.NewSeparator()) locationPercentageColumn.Add(widget.NewSeparator()) deleteButtonsColumn.Add(widget.NewSeparator()) return nil } if (locationType == "Continent"){ for continentName, continentPercentage := range myContinentPercentagesMap{ err := addLocationRow(continentName, continentPercentage) if (err != nil){ return nil, err } } } else if (locationType == "Region"){ for regionName, regionPercentage := range myRegionPercentagesMap{ err := addLocationRow(regionName, regionPercentage) if (err != nil){ return nil, err } } } else if (locationType == "Subregion"){ for subregionName, subregionPercentage := range mySubregionPercentagesMap{ err := addLocationRow(subregionName, subregionPercentage) if (err != nil){ return nil, err } } } locationsGrid := container.NewHBox(layout.NewSpacer(), locationNameColumn, locationPercentageColumn, deleteButtonsColumn, layout.NewSpacer()) return locationsGrid, nil } locationsSection, err := getLocationsSection() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), locationTypeLabel, locationTypeSelectorCentered, widget.NewSeparator(), locationsSection) setPageContent(page, window) } func setBuildMateProfilePage_23andMe_NeanderthalVariants(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_23andMe_NeanderthalVariants(window, previousPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) pageSubtitle := getPageSubtitleCentered(translate("23andMe - Neanderthal Variants")) description := getLabelCentered(translate("Enter how many neanderthal variants you have.")) neanderthalVariantsEntry := widget.NewEntry() variantCountExists, currentNeanderthalVariants, err := myLocalProfiles.GetProfileData("Mate", "23andMe_NeanderthalVariants") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (variantCountExists == true){ isValid, err := helpers.VerifyStringIsIntWithinRange(currentNeanderthalVariants, 0, 7462) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (isValid == false){ setErrorEncounteredPage(window, errors.New("MyLocalProfile contains invalid 23andMe neanderthalvariants."), previousPage) return } neanderthalVariantsEntry.SetText(currentNeanderthalVariants) } else { neanderthalVariantsEntry.SetPlaceHolder(translate("Enter Variant Count")) } getCurrentNeanderthalVariantsCountLabel := func()fyne.Widget{ if (variantCountExists == false){ result := translate("No Response") variantsLabel := getBoldItalicLabel(result) return variantsLabel } variantsLabel := getBoldLabel(currentNeanderthalVariants) return variantsLabel } currentVariantCountLabel := getCurrentNeanderthalVariantsCountLabel() myVariantCountLabel := widget.NewLabel("My Variant Count:") currentVariantsCountRow := container.NewHBox(layout.NewSpacer(), myVariantCountLabel, currentVariantCountLabel, layout.NewSpacer()) submitChangesButton := widget.NewButtonWithIcon("Submit", theme.ConfirmIcon(), func(){ newVariantsCount := neanderthalVariantsEntry.Text isValid, err := helpers.VerifyStringIsIntWithinRange(newVariantsCount, 0, 7462) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } if (isValid == false){ neanderthalVariantsEntry.SetText("") neanderthalVariantsEntry.SetPlaceHolder(translate("Enter Variant Count")) dialogTitle := translate("Invalid Variant Count") dialogMessageA := getLabelCentered(translate("Invalid neanderthal variants count entered.")) dialogMessageB := getLabelCentered(translate("You must provide a number between 0 and 7462 variants.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) return } err = myLocalProfiles.SetProfileData("Mate", "23andMe_NeanderthalVariants", newVariantsCount) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) variantsEntryWithSubmitButton := getContainerCentered(container.NewGridWithRows(1, neanderthalVariantsEntry, submitChangesButton)) noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "23andMe_NeanderthalVariants") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() })) page := container.NewVBox(title, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description, widget.NewSeparator(), currentVariantsCountRow, widget.NewSeparator(), variantsEntryWithSubmitButton, noResponseButton) setPageContent(page, window) } func setBuildMateProfilePage_23andMe_Haplogroup(window fyne.Window, maternalOrPaternal string, previousPage func()){ if (maternalOrPaternal != "Maternal" && maternalOrPaternal != "Paternal"){ setErrorEncounteredPage(window, errors.New("setBuildMateProfilePage_23andMe_Haplogroup called with invalid maternalOrPaternal: " + maternalOrPaternal), previousPage) return } currentPage := func(){setBuildMateProfilePage_23andMe_Haplogroup(window, maternalOrPaternal, previousPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) pageSubtitle := getPageSubtitleCentered(translate("23andMe - " + maternalOrPaternal + " Haplogroup")) maternalOrPaternalLowercase := strings.ToLower(maternalOrPaternal) description1 := getLabelCentered("Choose your " + maternalOrPaternalLowercase + " haplogroup.") description2 := getLabelCentered("If it is not listed, enter it in manually.") getKnownHaplogroupsList := func()[]string{ if (maternalOrPaternal == "Maternal"){ maternalHaplogroupsList := companyAnalysis.GetKnownMaternalHaplogroupsList_23andMe() return maternalHaplogroupsList } paternalHaplogroupsList := companyAnalysis.GetKnownPaternalHaplogroupsList_23andMe() return paternalHaplogroupsList } knownHaplogroupsList := getKnownHaplogroupsList() haplogroupEntry := widget.NewSelectEntry(knownHaplogroupsList) attributeName := "23andMe_" + maternalOrPaternal + "Haplogroup" haplogroupExists, currentHaplogroup, err := myLocalProfiles.GetProfileData("Mate", attributeName) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (haplogroupExists == true){ if (len(currentHaplogroup) > 25){ setErrorEncounteredPage(window, errors.New("MyLocalProfiles contains invalid haplogroup: " + currentHaplogroup), previousPage) return } haplogroupEntry.SetText(currentHaplogroup) } else { haplogroupEntry.SetPlaceHolder(translate("Select or enter haplogroup...")) } getCurrentHaplogroupLabel := func()fyne.Widget{ if (haplogroupExists == false){ result := translate("No Response") haplogroupLabel := getBoldItalicLabel(result) return haplogroupLabel } haplogroupLabel := getBoldLabel(currentHaplogroup) return haplogroupLabel } currentHaplogroupLabel := getCurrentHaplogroupLabel() myHaplogroupLabel := widget.NewLabel("My " + maternalOrPaternal + " Haplogroup:") currentHaplogroupRow := container.NewHBox(layout.NewSpacer(), myHaplogroupLabel, currentHaplogroupLabel, layout.NewSpacer()) submitChangesButton := widget.NewButtonWithIcon("Submit", theme.ConfirmIcon(), func(){ newHaplogroup := haplogroupEntry.Text if (newHaplogroup == ""){ title := translate("Invalid Haplogroup") dialogMessageA := getLabelCentered(translate("Invalid haplogroup entered.")) dialogMessageB := getLabelCentered(translate("Your haplogroup cannot be empty.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } if (len(newHaplogroup) > 25){ haplogroupEntry.SetText("") haplogroupEntry.SetPlaceHolder(translate("Select or enter haplogroup...")) title := translate("Invalid Haplogroup") dialogMessageA := getLabelCentered(translate("Invalid haplogroup entered.")) dialogMessageB := getLabelCentered(translate("It must be less than 25 bytes.")) dialogMessageC := getLabelCentered(translate("Contact the Seekia developers if this is too short to fit your haplogroup.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } isAllowed := allowedText.VerifyStringIsAllowed(newHaplogroup) if (isAllowed == false){ title := translate("Invalid Haplogroup") dialogMessageA := getLabelCentered(translate("Your haplogroup 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(newHaplogroup) if (containsTabsOrNewlines == true){ title := translate("Invalid Haplogroup") dialogMessageA := getLabelCentered(translate("Your haplogroup contains a tab or a newline.")) dialogMessageB := getLabelCentered(translate("Remove the character and resubmit.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } err := myLocalProfiles.SetProfileData("Mate", attributeName, newHaplogroup) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) haplogroupEntryWithSubmitButton := getContainerCentered(container.NewGridWithRows(1, haplogroupEntry, submitChangesButton)) noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", attributeName) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() })) page := container.NewVBox(title, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), currentHaplogroupRow, widget.NewSeparator(), haplogroupEntryWithSubmitButton, noResponseButton) setPageContent(page, window) } func setBuildMateProfilePage_Age(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_Age(window, previousPage)} pageTitle := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) pageSubtitle := getPageSubtitleCentered(translate("Age")) description1 := getLabelCentered(translate("Enter your age. You must be at least 18.")) description2 := getLabelCentered(translate("You should update your age as you get older.")) ageEntry := widget.NewEntry() ageExists, currentAge, err := myLocalProfiles.GetProfileData("Mate", "Age") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (ageExists == true){ isValid, err := helpers.VerifyStringIsIntWithinRange(currentAge, 18, 150) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (isValid == false){ setErrorEncounteredPage(window, errors.New("MyLocalProfiles contains invalid age."), previousPage) return } ageEntry.SetText(currentAge) } else { ageEntry.SetPlaceHolder(translate("Enter Age")) } getCurrentAgeLabel := func()fyne.Widget{ if (ageExists == false){ result := translate("No Response") ageLabel := getBoldItalicLabel(result) return ageLabel } ageLabel := getBoldLabel(currentAge) return ageLabel } currentAgeLabel := getCurrentAgeLabel() myAgeLabel := widget.NewLabel("My Age:") currentAgeRow := container.NewHBox(layout.NewSpacer(), myAgeLabel, currentAgeLabel, layout.NewSpacer()) submitChangesButton := widget.NewButtonWithIcon("Submit", theme.ConfirmIcon(), func(){ newAge := ageEntry.Text isValid, err := helpers.VerifyStringIsIntWithinRange(newAge, 18, 150) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } if (isValid == false){ ageEntry.SetText("") ageEntry.SetPlaceHolder(translate("Enter Age")) title := translate("Invalid Age") dialogMessageA := getLabelCentered(translate("Invalid age entered.")) dialogMessageB := getLabelCentered(translate("You must be at least 18 to use Seekia.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } err = myLocalProfiles.SetProfileData("Mate", "Age", newAge) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) ageEntryWithSubmitButton := getContainerCentered(container.NewGridWithRows(1, ageEntry, submitChangesButton)) noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "Age") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() })) page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), currentAgeRow, widget.NewSeparator(), ageEntryWithSubmitButton, noResponseButton) setPageContent(page, window) } func setBuildMateProfilePage_GeneticInformation(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_GeneticInformation(window, previousPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered(translate("Genetic Information")) description1 := widget.NewLabel("You can display your genetic information on your profile.") shareGeneticInfoHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setProfileGenomeInfoExplainerPage(window, currentPage) }) description1Row := container.NewHBox(layout.NewSpacer(), description1, shareGeneticInfoHelpButton, layout.NewSpacer()) description2 := getLabelCentered("You must link your genome person to your profile.") description3 := getLabelCentered("You can hide some or all of your genetic information.") myGenomePersonIdentifierExists, myGenomePersonIdentifier, err := myLocalProfiles.GetProfileData("Mate", "GenomePersonIdentifier") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (myGenomePersonIdentifierExists == true){ personFound, _, _, _, err := myPeople.GetPersonInfo(myGenomePersonIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (personFound == false){ // Genome person was deleted // We will delete the missing person and refresh the page // We show a loading screen so the user can tell if this wrongfully keeps happening setLoadingScreen(window, "Build Mate Profile - Physical", "Deleting old genome person identifier...") err := myLocalProfiles.DeleteProfileData("Mate", "GenomePersonIdentifier") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } currentPage() return } } myPersonLabel := widget.NewLabel("My Person:") getCurrentPersonNameLabel := func()(fyne.Widget, error){ if (myGenomePersonIdentifierExists == false){ noneLabel := getBoldItalicLabel(translate("None")) return noneLabel, nil } personFound, personName, _, _, err := myPeople.GetPersonInfo(myGenomePersonIdentifier) if (err != nil){ return nil, err } if (personFound == false){ return nil, errors.New("Genome person not found after being found already.") } personNameLabel := getBoldLabel(personName) return personNameLabel, nil } currentPersonNameLabel, err := getCurrentPersonNameLabel() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } currentPersonNameRow := container.NewHBox(layout.NewSpacer(), myPersonLabel, currentPersonNameLabel, layout.NewSpacer()) getChooseOrChangeButtonTextAndIcon := func()(string, fyne.Resource){ if (myGenomePersonIdentifierExists == false){ result := translate("Choose Person") return result, theme.NavigateNextIcon() } result := translate("Change Person") return result, theme.DocumentCreateIcon() } chooseOrChangeButtonText, chooseOrChangeButtonIcon := getChooseOrChangeButtonTextAndIcon() chooseOrChangePersonButton := getWidgetCentered(widget.NewButtonWithIcon(chooseOrChangeButtonText, chooseOrChangeButtonIcon, func(){ setBuildMateProfilePage_EditGenomePerson(window, currentPage, currentPage) })) if (myGenomePersonIdentifierExists == false){ page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1Row, description2, description3, widget.NewSeparator(), currentPersonNameRow, chooseOrChangePersonButton) setPageContent(page, window) return } currentlySharingLabel := getItalicLabelCentered("Currently Sharing:") //Outputs: // -int: Total number of monogenic diseases // -int: Number of monogenic diseases being shared // -error getNumberOfMonogenicDiseasesBeingSharedInfo := func()(int, int, error){ monogenicDiseaseNamesList, err := monogenicDiseases.GetMonogenicDiseaseNamesList() if (err != nil) { return 0, 0, err } totalMonogenicDiseases := len(monogenicDiseaseNamesList) totalMonogenicDiseasesBeingShared := 0 for _, diseaseName := range monogenicDiseaseNamesList{ shareDiseaseInfoAttributeName := "ShareMonogenicDiseaseInfo_" + diseaseName currentSharePreferenceExists, currentSharePreference, err := myLocalProfiles.GetProfileData("Mate", shareDiseaseInfoAttributeName) if (err != nil){ return 0, 0, err } if (currentSharePreferenceExists == true && currentSharePreference == "Yes"){ totalMonogenicDiseasesBeingShared += 1 } } return totalMonogenicDiseases, totalMonogenicDiseasesBeingShared, nil } //Outputs: // -int: Total number of polygenic diseases // -int: Number of polygenic diseases being shared // -error getNumberOfPolygenicDiseasesBeingSharedInfo := func()(int, int, error){ polygenicDiseaseNamesList, err := polygenicDiseases.GetPolygenicDiseaseNamesList() if (err != nil) { return 0, 0, err } totalPolygenicDiseases := len(polygenicDiseaseNamesList) totalPolygenicDiseasesBeingShared := 0 for _, diseaseName := range polygenicDiseaseNamesList{ sharePolygenicDiseaseInfoAttributeName := "SharePolygenicDiseaseInfo_" + diseaseName currentSharePreferenceExists, currentSharePreference, err := myLocalProfiles.GetProfileData("Mate", sharePolygenicDiseaseInfoAttributeName) if (err != nil){ return 0, 0, err } if (currentSharePreferenceExists == true && currentSharePreference == "Yes"){ totalPolygenicDiseasesBeingShared += 1 } } return totalPolygenicDiseases, totalPolygenicDiseasesBeingShared, nil } //Outputs: // -int: Total number of traits // -int: Number of traits being shared // -error getNumberOfTraitsBeingSharedInfo := func()(int, int, error){ traitNamesList, err := traits.GetTraitNamesList() if (err != nil) { return 0, 0, err } totalTraits := len(traitNamesList) totalTraitsBeingShared := 0 for _, traitName := range traitNamesList{ shareTraitInfoAttributeName := "ShareTraitInfo_" + traitName currentSharePreferenceExists, currentSharePreference, err := myLocalProfiles.GetProfileData("Mate", shareTraitInfoAttributeName) if (err != nil){ return 0, 0, err } if (currentSharePreferenceExists == true && currentSharePreference == "Yes"){ totalTraitsBeingShared += 1 } } return totalTraits, totalTraitsBeingShared, nil } totalMonogenicDiseases, numberOfMonogenicDiseasesBeingShared, err := getNumberOfMonogenicDiseasesBeingSharedInfo() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } totalPolygenicDiseases, numberOfPolygenicDiseasesBeingShared, err := getNumberOfPolygenicDiseasesBeingSharedInfo() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } totalTraits, numberOfTraitsBeingShared, err := getNumberOfTraitsBeingSharedInfo() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } totalMonogenicDiseasesString := helpers.ConvertIntToString(totalMonogenicDiseases) numberOfMonogenicDiseasesBeingSharedString := helpers.ConvertIntToString(numberOfMonogenicDiseasesBeingShared) numberOfMonogenicDiseasesBeingSharedLabel := getBoldLabelCentered("Monogenic Diseases: " + numberOfMonogenicDiseasesBeingSharedString + "/" + totalMonogenicDiseasesString) totalPolygenicDiseasesString := helpers.ConvertIntToString(totalPolygenicDiseases) numberOfPolygenicDiseasesBeingSharedString := helpers.ConvertIntToString(numberOfPolygenicDiseasesBeingShared) numberOfPolygenicDiseasesBeingSharedLabel := getBoldLabelCentered("Polygenic Diseases: " + numberOfPolygenicDiseasesBeingSharedString + "/" + totalPolygenicDiseasesString) totalTraitsString := helpers.ConvertIntToString(totalTraits) numberOfTraitsBeingSharedString := helpers.ConvertIntToString(numberOfTraitsBeingShared) numberOfTraitsBeingSharedLabel := getBoldLabelCentered("Traits: " + numberOfTraitsBeingSharedString + "/" + totalTraitsString) changeVisibilityButton := getWidgetCentered(widget.NewButtonWithIcon("Change Visibility", theme.DocumentCreateIcon(), func(){ setBuildMateProfilePage_ChangeGeneticInformationVisibility(window, "MonogenicDiseases", currentPage) })) currentGenomeLabel := widget.NewLabel("Current Genome:") getCurrentGenomeText := func()(string, error){ myGenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(myGenomePersonIdentifier) if (err != nil) { return "", err } if (len(myGenomesMapList) == 0){ genomeText := translate("None") return genomeText, nil } if (len(myGenomesMapList) == 1){ myGenomeMap := myGenomesMapList[0] myGenomeCompanyName, exists := myGenomeMap["CompanyName"] if (exists == false){ return "", errors.New("MyGenomesMapList is malformed: Genome map missing CompanyName") } return myGenomeCompanyName, nil } currentCombinedGenomeToUse, err := myChosenAnalysis.GetMyCombinedGenomeToUse() if (err != nil){ return "", err } result := translate(currentCombinedGenomeToUse) return result, nil } currentGenomeText, err := getCurrentGenomeText() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } currentGenomeTextLabel := getBoldLabel(currentGenomeText) currentGenomeRow := container.NewHBox(layout.NewSpacer(), currentGenomeLabel, currentGenomeTextLabel, layout.NewSpacer()) chooseGenomeButton := getWidgetCentered(widget.NewButtonWithIcon("Choose Genome", theme.DocumentCreateIcon(), func(){ setBuildMateProfilePage_ChooseGeneticInformationGenome(window, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1Row, description2, description3, widget.NewSeparator(), currentPersonNameRow, chooseOrChangePersonButton, widget.NewSeparator(), currentlySharingLabel, numberOfMonogenicDiseasesBeingSharedLabel, numberOfPolygenicDiseasesBeingSharedLabel, numberOfTraitsBeingSharedLabel, changeVisibilityButton, widget.NewSeparator(), currentGenomeRow, chooseGenomeButton) setPageContent(page, window) } func setBuildMateProfilePage_EditGenomePerson(window fyne.Window, previousPage func(), nextPage func()){ currentPage := func(){setBuildMateProfilePage_EditGenomePerson(window, previousPage, nextPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered("Choose Genome Person") description1 := getLabelCentered("Choose your genome person.") description2 := getLabelCentered("You must first import your genome(s) on the Genetics page.") description3 := getLabelCentered("As you import or remove genomes, the changes will be reflected in your profile.") description4 := getLabelCentered("To avoid sharing information, you can hide some or all of your genetic information.") description5 := getLabelCentered("Linking your genome person will enable you to view offspring analyses when viewing matches.") myGenomePersonIdentifierExists, currentGenomePersonIdentifier, err := myLocalProfiles.GetProfileData("Mate", "GenomePersonIdentifier") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (myGenomePersonIdentifierExists == true){ personFound, _, _, _, err := myPeople.GetPersonInfo(currentGenomePersonIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (personFound == false){ // Genome person was deleted // We will delete the missing person and refresh the page // We show a loading screen so the user can tell if this wrongfully keeps happening setLoadingScreen(window, "Build Mate Profile - Physical", "Deleting old genome person identifier...") err := myLocalProfiles.DeleteProfileData("Mate", "GenomePersonIdentifier") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } currentPage() return } } getCurrentPersonNameLabel := func()(fyne.Widget, error){ if (myGenomePersonIdentifierExists == false){ result := translate("None") noResponseLabel := getBoldItalicLabel(result) return noResponseLabel, nil } personFound, personName, _, _, err := myPeople.GetPersonInfo(currentGenomePersonIdentifier) if (err != nil){ return nil, err } if (personFound == false){ return nil, errors.New("Genome person not found after being found already.") } personNameLabel := getBoldLabel(personName) return personNameLabel, nil } currentPersonNameLabel, err := getCurrentPersonNameLabel() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } currentPersonLabel := widget.NewLabel("Current Person:") currentPersonNameRow := container.NewHBox(layout.NewSpacer(), currentPersonLabel, currentPersonNameLabel, layout.NewSpacer()) getSelectGenomePersonSection := func()(*fyne.Container, error){ allMyGenomePeopleMapList, err := myPeople.GetMyGenomePeopleMapList() if (err != nil){ return nil, err } if (len(allMyGenomePeopleMapList) == 0){ noPeopleExistLabel := getBoldLabel("No People Exist.") createAPersonLabel := getLabelCentered("Create a person and import their genomes on the Genetics page.") selectPersonSection := container.NewVBox(noPeopleExistLabel, createAPersonLabel) return selectPersonSection, nil } // Map Structure: Person Name -> Person Identifier allGenomePersonNamesMap := make(map[string]string) for _, personMap := range allMyGenomePeopleMapList{ personName, exists := personMap["PersonName"] if (exists == false){ return nil, errors.New("MyGenomePeople map list is malformed: Item missing PersonName") } personIdentifier, exists := personMap["PersonIdentifier"] if (exists == false){ return nil, errors.New("MyGenomePeople map list is malformed: Item missing PersonIdentifier") } _, exists = allGenomePersonNamesMap[personName] if (exists == true){ return nil, errors.New("MyGenomePeople map list is malformed: Duplicate person names exist.") } allGenomePersonNamesMap[personName] = personIdentifier } allGenomePersonNamesList := helpers.GetListOfMapKeys(allGenomePersonNamesMap) personSelector := widget.NewSelect(allGenomePersonNamesList, nil) if (myGenomePersonIdentifierExists == true){ personFound, personName, _, _, err := myPeople.GetPersonInfo(currentGenomePersonIdentifier) if (err != nil){ return nil, err } if (personFound == false){ return nil, errors.New("Genome person not found after being found already.") } personSelector.Selected = personName } else { personSelector.PlaceHolder = translate("Select Person...") } submitButton := widget.NewButtonWithIcon("Submit", theme.ConfirmIcon(), func(){ newPersonName := personSelector.Selected if (newPersonName == ""){ dialogTitle := translate("No Person Chosen") dialogMessageA := getLabelCentered(translate("No person was chosen.")) dialogMessageB := getLabelCentered(translate("Select a person from the selector.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) return } newPersonIdentifier, exists := allGenomePersonNamesMap[newPersonName] if (exists == false){ setErrorEncounteredPage(window, errors.New("New genome person identifier not found."), currentPage) return } err := myLocalProfiles.SetProfileData("Mate", "GenomePersonIdentifier", newPersonIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } nextPage() }) personSelectorWithSubmitButtonRow := container.NewHBox(layout.NewSpacer(), personSelector, submitButton, layout.NewSpacer()) removePersonButton := getWidgetCentered(widget.NewButtonWithIcon(translate("Remove Person"), theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "GenomePersonIdentifier") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } nextPage() })) selectGenomePersonSection := container.NewVBox(personSelectorWithSubmitButtonRow, removePersonButton) return selectGenomePersonSection, nil } selectGenomePersonSection, err := getSelectGenomePersonSection() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, widget.NewSeparator(), currentPersonNameRow, widget.NewSeparator(), selectGenomePersonSection) setPageContent(page, window) } func setBuildMateProfilePage_ChangeGeneticInformationVisibility(window fyne.Window, informationCategory string, previousPage func()){ if (informationCategory != "MonogenicDiseases" && informationCategory != "PolygenicDiseases" && informationCategory != "Traits"){ setErrorEncounteredPage(window, errors.New("setBuildMateProfilePage_ChangeGeneticInformationVisibility called with invalid informationCategory: " + informationCategory), previousPage) return } currentPage := func(){setBuildMateProfilePage_ChangeGeneticInformationVisibility(window, informationCategory, previousPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered("Change Genetic Information Visibility") description1 := getLabelCentered("Choose which information from your genome you want to share on your profile.") description2 := getLabelCentered("You will still always be able to view offspring analyses for your matches.") privacyWarningButton := getWidgetCentered(widget.NewButtonWithIcon("Privacy Warning", theme.WarningIcon(), func(){ setMateProfileGeneticInformationPrivacyWarningPage(window, currentPage) })) currentCategoryLabel := getItalicLabelCentered("Current Category:") monogenicDiseasesTranslated := translate("Monogenic Diseases") polygenicDiseasesTranslated := translate("Polygenic Diseases") traitsTranslated := translate("Traits") categoriesList := []string{monogenicDiseasesTranslated, polygenicDiseasesTranslated, traitsTranslated} handleSelectFunction := func(newChoice string){ getNewCategory := func()string{ if (newChoice == monogenicDiseasesTranslated){ return "MonogenicDiseases" } if (newChoice == polygenicDiseasesTranslated){ return "PolygenicDiseases" } return "Traits" } newCategory := getNewCategory() setBuildMateProfilePage_ChangeGeneticInformationVisibility(window, newCategory, previousPage) } categorySelector := widget.NewSelect(categoriesList, handleSelectFunction) if (informationCategory == "MonogenicDiseases"){ categorySelector.Selected = monogenicDiseasesTranslated } else if (informationCategory == "PolygenicDiseases"){ categorySelector.Selected = polygenicDiseasesTranslated } else { categorySelector.Selected = traitsTranslated } categorySelectorCentered := getWidgetCentered(categorySelector) // This returns a list of all MonogenicDiseaseNames, PolygenicDiseaseNames, or TraitNames getItemNamesList := func()([]string, error){ if (informationCategory == "MonogenicDiseases"){ monogenicDiseaseNamesList, err := monogenicDiseases.GetMonogenicDiseaseNamesList() if (err != nil) { return nil, err } return monogenicDiseaseNamesList, nil } if (informationCategory == "PolygenicDiseases"){ polygenicDiseaseNamesList, err := polygenicDiseases.GetPolygenicDiseaseNamesList() if (err != nil) { return nil, err } return polygenicDiseaseNamesList, nil } // informationCategory == "Traits" traitNamesList, err := traits.GetTraitNamesList() if (err != nil) { return nil, err } return traitNamesList, nil } allItemNamesList, err := getItemNamesList() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } getCategoryTypeShareInfoPrefix := func()string{ if (informationCategory == "MonogenicDiseases"){ return "ShareMonogenicDiseaseInfo_" } if (informationCategory == "PolygenicDiseases"){ return "SharePolygenicDiseaseInfo_" } return "ShareTraitInfo_" } categoryTypeShareInfoPrefix := getCategoryTypeShareInfoPrefix() setAllItemShareAttributesToValue := func(yesOrNo string)error{ if (yesOrNo != "Yes" && yesOrNo != "No"){ return errors.New("setAllItemShareAttributesToValue called with invalid yesOrNo: " + yesOrNo) } for _, itemName := range allItemNamesList{ itemAttributeName := categoryTypeShareInfoPrefix + itemName err := myLocalProfiles.SetProfileData("Mate", itemAttributeName, yesOrNo) if (err != nil) { return err } } return nil } shareAllButton := widget.NewButtonWithIcon("Share All", theme.VisibilityIcon(), func(){ err := setAllItemShareAttributesToValue("Yes") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) hideAllButton := widget.NewButtonWithIcon("Hide All", theme.VisibilityOffIcon(), func(){ err := setAllItemShareAttributesToValue("No") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) shareAndHideButtons := getContainerCentered(container.NewGridWithRows(1, shareAllButton, hideAllButton)) getCategoryItemsGrid := func()(*fyne.Container, error){ getItemTypeName := func()string{ if (informationCategory == "MonogenicDiseases"){ result := translate("Monogenic Disease") return result } if (informationCategory == "PolygenicDiseases"){ result := translate("Polygenic Disease") return result } result := translate("Trait") return result } itemTypeName := getItemTypeName() itemTypeNameLabel := getItalicLabelCentered(itemTypeName + " " + translate("Name")) shareInformationLabel := getItalicLabelCentered(translate("Share Information?")) itemNameColumn := container.NewVBox(itemTypeNameLabel, widget.NewSeparator()) itemChecksColumn := container.NewVBox(shareInformationLabel, widget.NewSeparator()) for _, itemName := range allItemNamesList{ itemNameLabel := getBoldLabelCentered(itemName) itemAttributeName := categoryTypeShareInfoPrefix + itemName itemCheck := widget.NewCheck("", func(newResponse bool){ newValueString := helpers.ConvertBoolToYesOrNoString(newResponse) err := myLocalProfiles.SetProfileData("Mate", itemAttributeName, newValueString) if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } }) currentSharePreferenceExists, currentSharePreference, err := myLocalProfiles.GetProfileData("Mate", itemAttributeName) if (err != nil){ return nil, err } if (currentSharePreferenceExists == true && currentSharePreference == "Yes"){ itemCheck.Checked = true } itemCheckCentered := getWidgetCentered(itemCheck) itemNameColumn.Add(itemNameLabel) itemChecksColumn.Add(itemCheckCentered) itemNameColumn.Add(widget.NewSeparator()) itemChecksColumn.Add(widget.NewSeparator()) } itemsGrid := container.NewHBox(layout.NewSpacer(), itemNameColumn, itemChecksColumn, layout.NewSpacer()) return itemsGrid, nil } categoryItemsGrid, err := getCategoryItemsGrid() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, privacyWarningButton, widget.NewSeparator(), currentCategoryLabel, categorySelectorCentered, widget.NewSeparator(), shareAndHideButtons, widget.NewSeparator(), categoryItemsGrid) setPageContent(page, window) } func setMateProfileGeneticInformationPrivacyWarningPage(window fyne.Window, previousPage func()){ title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered("Genetic Information Privacy Warning") description1 := getLabelCentered("By sharing genome information, users can read an analysis of an offspring created from you and them.") description2 := getLabelCentered("When you share Polygenic Disease and Trait information, you are sharing specific locations on your genome.") description3 := getLabelCentered("These locations represent a tiny fraction of your entire genome.") description4 := getLabelCentered("These locations can still be used to create a unique genetic fingerprint, which can identify you.") description5 := getLabelCentered("People will also be able to read your trait and polygenic disease risk information.") description6 := getLabelCentered("For example, if you share Breast Cancer, users will know your Breast Cancer risk.") description7 := getLabelCentered("When sharing Monogenic Disease information, you do not share specific locations.") description8 := getLabelCentered("You instead share the probability of passing a monogenic disease variant to your offspring.") page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, description6, description7, description8) setPageContent(page, window) } func setBuildMateProfilePage_ChooseGeneticInformationGenome(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_ChooseGeneticInformationGenome(window, previousPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered("Choose Genetic Information Genome") description1 := getLabelCentered("A single genome from your genetic analysis will be used for genetic calculations with other users.") description2 := getLabelCentered("Your profile will also contain information from this genome if you choose to share any information.") description3 := getLabelCentered("If you have only imported one genome, that will be the genome whose information is used.") description4 := getLabelCentered("If you have multiple genomes, only 1 of the two combined genomes will be used.") description5 := getLabelCentered("You can choose below which genome you want to use if you have imported multiple genomes.") currentGenomeLabel := widget.NewLabel("Current Genome:") myGenomePersonIdentifierExists, myGenomePersonIdentifier, err := myLocalProfiles.GetProfileData("Mate", "GenomePersonIdentifier") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (myGenomePersonIdentifierExists == false){ setErrorEncounteredPage(window, errors.New("setBuildMateProfilePage_ChooseGeneticInformationGenome called when genome person is not established."), previousPage) return } currentCombinedGenomeToUse, err := myChosenAnalysis.GetMyCombinedGenomeToUse() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } getCurrentGenomeText := func()(string, error){ myGenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(myGenomePersonIdentifier) if (err != nil) { return "", err } if (len(myGenomesMapList) == 0){ genomeText := translate("None") return genomeText, nil } if (len(myGenomesMapList) == 1){ myGenomeMap := myGenomesMapList[0] myGenomeCompanyName, exists := myGenomeMap["CompanyName"] if (exists == false){ return "", errors.New("MyGenomesMapList is malformed: Genome map missing CompanyName") } return myGenomeCompanyName, nil } result := translate(currentCombinedGenomeToUse) return result, nil } currentGenomeText, err := getCurrentGenomeText() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } currentGenomeTextLabel := getBoldLabel(currentGenomeText) currentGenomeRow := container.NewHBox(layout.NewSpacer(), currentGenomeLabel, currentGenomeTextLabel, layout.NewSpacer()) combinedGenomeToUseLabel := getItalicLabel("Combined Genome To Use:") option1Translated := translate("Only Include Shared") option2Translated := translate("Only Exclude Conflicts") untranslatedOptionsMap := map[string]string{ option1Translated: "Only Include Shared", option2Translated: "Only Exclude Conflicts", } selectorOptionsList := []string{option1Translated, option2Translated} combinedGenomeSelector := widget.NewSelect(selectorOptionsList, func(newSelection string){ newSelectionUntranslated, exists := untranslatedOptionsMap[newSelection] if (exists == false){ setErrorEncounteredPage(window, errors.New("untranslatedOptionsMap missing selection: " + newSelection), currentPage) return } err = myChosenAnalysis.SetMyCombinedGenomeToUse(newSelectionUntranslated) if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) combinedGenomeSelector.Selected = translate(currentCombinedGenomeToUse) combinedGenomeHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){ setCombinedGenomesExplainerPage(window, currentPage) }) combinedGenomeSelectorWithHelpButton := container.NewBorder(nil, nil, nil, combinedGenomeHelpButton, combinedGenomeSelector) combinedGenomeSelectorWithLabel := getContainerCentered(container.NewGridWithColumns(1, combinedGenomeToUseLabel, combinedGenomeSelectorWithHelpButton)) page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, description5, widget.NewSeparator(), currentGenomeRow, widget.NewSeparator(), combinedGenomeSelectorWithLabel) setPageContent(page, window) } func setBuildMateProfilePage_Height(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_Height(window, previousPage)} pageTitle := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) pageSubtitle := getPageSubtitleCentered(translate("Height")) description := getLabelCentered(translate("Enter your height.")) getCurrentMetricOrImperial := func()(string, error){ exists, metricOrImperial, err := globalSettings.GetSetting("MetricOrImperial") if (err != nil) { return "", err } if (exists == false){ return "Metric", nil } if (metricOrImperial != "Metric" && metricOrImperial != "Imperial"){ return "", errors.New("Malformed globalSettings: Invalid metricOrImperial: " + metricOrImperial) } return metricOrImperial, nil } currentMetricOrImperial, err := getCurrentMetricOrImperial() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } changeMetricOrImperialButton, err := getMetricImperialSwitchButton(window, currentPage) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } currentUnitsLabel := getItalicLabel(translate("Current Units:")) metricOrImperialRow := container.NewHBox(layout.NewSpacer(), currentUnitsLabel, changeMetricOrImperialButton, layout.NewSpacer()) currentHeightExists, currentHeightCentimeters, err := myLocalProfiles.GetProfileData("Mate", "Height") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } getCurrentHeightLabel := func()(fyne.Widget, error){ if (currentHeightExists == false){ labelText := translate("No Response") label := getBoldItalicLabel(labelText) return label, nil } myHeightCentimetersFloat64, err := helpers.ConvertStringToFloat64(currentHeightCentimeters) if (err != nil){ return nil, errors.New("MyLocalProfiles malformed: Contains invalid Height") } if (currentMetricOrImperial == "Metric"){ myHeightRounded := helpers.ConvertFloat64ToStringRounded(myHeightCentimetersFloat64, 2) centimetersTranslated := translate("centimeters") formattedResult := myHeightRounded + " " + centimetersTranslated label := getBoldLabel(formattedResult) return label, nil } feetInchesString, err := helpers.ConvertCentimetersToFeetInchesTranslatedString(myHeightCentimetersFloat64) if (err != nil) { return nil, err } label := getBoldLabel(feetInchesString) return label, nil } currentHeightLabel, err := getCurrentHeightLabel() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } myHeightLabel := widget.NewLabel("My Height:") currentHeightRow := container.NewHBox(layout.NewSpacer(), myHeightLabel, currentHeightLabel, layout.NewSpacer()) getEnterHeightRow := func()(*fyne.Container, error){ if (currentMetricOrImperial == "Metric"){ centimetersEntry := widget.NewEntry() if (currentHeightExists == false){ centimetersEntry.SetPlaceHolder(translate("Enter Centimeters")) } else { centimetersEntry.SetText(currentHeightCentimeters) } centimetersSubmitButton := widget.NewButtonWithIcon(translate("Submit"), theme.ConfirmIcon(), func(){ newHeightCentimeters := centimetersEntry.Text isValid, err := helpers.VerifyStringIsFloatWithinRange(newHeightCentimeters, 30, 400) if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } if (isValid == false){ centimetersEntry.SetText("") centimetersEntry.SetPlaceHolder(translate("Enter Centimeters")) title := translate("Invalid Height") dialogMessageA := getLabelCentered(translate("Invalid height entered.")) dialogMessageB := getLabelCentered(translate("Height must be between 30 and 400 centimeters.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } err = myLocalProfiles.SetProfileData("Mate", "Height", newHeightCentimeters) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) centimetersLabel := widget.NewLabel(translate("Centimeters")) centimetersEntryWithLabel := container.NewGridWithRows(1, centimetersEntry, centimetersLabel) enterHeightRow := container.NewHBox(layout.NewSpacer(), centimetersEntryWithLabel, centimetersSubmitButton, layout.NewSpacer()) return enterHeightRow, nil } // MetricOrImperial == "Imperial" /// Feet + Inches feetLabel := widget.NewLabel(translate("Feet")) inchesLabel := widget.NewLabel(translate("Inches")) feetEntry := widget.NewEntry() inchesEntry := widget.NewEntry() if (currentHeightExists == false){ feetEntry.SetPlaceHolder(translate("Enter Feet")) inchesEntry.SetPlaceHolder(translate("Enter Inches")) } else { isValid, err := helpers.VerifyStringIsFloatWithinRange(currentHeightCentimeters, 30, 400) if (err != nil) { return nil, err } if (isValid == false){ return nil, errors.New("MyLocalProfiles is malformed: Contains invalid height.") } currentHeightCentimetersFloat64, err := helpers.ConvertStringToFloat64(currentHeightCentimeters) if (err != nil) { return nil, err } currentHeightFeet, currentHeightInches, err := helpers.ConvertCentimetersToFeetInches(currentHeightCentimetersFloat64) if (err != nil) { return nil, err } currentHeightFeetString := helpers.ConvertIntToString(currentHeightFeet) currentHeightInchesString := helpers.ConvertFloat64ToStringRounded(currentHeightInches, 1) feetEntry.SetText(currentHeightFeetString) inchesEntry.SetText(currentHeightInchesString) } feetInchesSubmitButton := widget.NewButtonWithIcon(translate("Submit"), theme.ConfirmIcon(), func(){ newHeightFeet := feetEntry.Text newHeightInches := inchesEntry.Text newHeightFeetInt, err := helpers.ConvertStringToInt(newHeightFeet) if (err != nil){ title := translate("Invalid Height") dialogMessageA := getLabelCentered(translate("Invalid feet entered.")) dialogMessageB := getLabelCentered(translate("Feet must be a number.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } newHeightInchesFloat64, err := helpers.ConvertStringToFloat64(newHeightInches) if (err != nil){ title := translate("Invalid Height") dialogMessageA := getLabelCentered(translate("Invalid inches entered.")) dialogMessageB := getLabelCentered(translate("Inches must be a number.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } newHeightCentimeters, err := helpers.ConvertFeetInchesToCentimeters(newHeightFeetInt, newHeightInchesFloat64) if (err != nil){ title := translate("Invalid Height") dialogMessageA := getLabelCentered(translate("Invalid height entered.")) dialogMessageB := getLabelCentered(translate("Height must be between 30 and 400 centimeters.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } if (newHeightCentimeters < 30 || newHeightCentimeters > 400){ title := translate("Invalid Height") dialogMessageA := getLabelCentered(translate("Invalid height entered.")) dialogMessageB := getLabelCentered(translate("Height must be between 30 and 400 centimeters.")) dialogContent := container.NewVBox(dialogMessageA, dialogMessageB) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } newHeightCentimetersString := helpers.ConvertFloat64ToString(newHeightCentimeters) err = myLocalProfiles.SetProfileData("Mate", "Height", newHeightCentimetersString) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) feetEntryWithLabel := container.NewGridWithRows(1, feetEntry, feetLabel) inchesEntryWithLabel := container.NewGridWithRows(1, inchesEntry, inchesLabel) feetInchesEntryRow := getContainerCentered(container.NewHBox(layout.NewSpacer(), feetEntryWithLabel, inchesEntryWithLabel, feetInchesSubmitButton, layout.NewSpacer())) return feetInchesEntryRow, nil } enterHeightRow, err := getEnterHeightRow() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "Height") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() })) page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description, widget.NewSeparator(), metricOrImperialRow, widget.NewSeparator(), currentHeightRow, widget.NewSeparator(), enterHeightRow, noResponseButton) setPageContent(page, window) } func setBuildMateProfilePage_Body(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_Body(window, previousPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) subtitle := getPageSubtitleCentered("Body") description1 := getLabelCentered("Describe your body.") description2 := getLabelCentered("Choose a value between 1 and 4 to describe each characteristic.") description3 := getLabelCentered("1 = least fat/muscular human, 4 = most fat/muscular human.") fatLabel := getBoldLabelCentered("Fat") fatSelector := widget.NewSelect([]string{"1", "2", "3", "4"}, func(newChoice string){ err := myLocalProfiles.SetProfileData("Mate", "BodyFat", newChoice) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } }) fatSelector.PlaceHolder = "Select one..." fatNoResponseButton := widget.NewButtonWithIcon("No Response", theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "BodyFat") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) fatColumn := container.NewVBox(fatLabel, fatSelector, fatNoResponseButton) currentFatValueExists, currentFatValue, err := myLocalProfiles.GetProfileData("Mate", "BodyFat") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (currentFatValueExists == true){ fatSelector.Selected = currentFatValue } muscleLabel := getBoldLabelCentered("Muscle") muscleSelector := widget.NewSelect([]string{"1", "2", "3", "4"}, func(newChoice string){ err := myLocalProfiles.SetProfileData("Mate", "BodyMuscle", newChoice) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } }) muscleSelector.PlaceHolder = "Select one..." muscleNoResponseButton := widget.NewButtonWithIcon("No Response", theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "BodyMuscle") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) muscleColumn := container.NewVBox(muscleLabel, muscleSelector, muscleNoResponseButton) currentMuscleValueExists, currentMuscleValue, err := myLocalProfiles.GetProfileData("Mate", "BodyMuscle") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (currentMuscleValueExists == true){ muscleSelector.Selected = currentMuscleValue } pageColumns := container.NewHBox(layout.NewSpacer(), widget.NewSeparator(), fatColumn, widget.NewSeparator(), muscleColumn, widget.NewSeparator(), layout.NewSpacer()) page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), pageColumns) setPageContent(page, window) } func setBuildMateProfilePage_EyeColor(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_EyeColor(window, previousPage)} pageTitle := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) pageSubtitle := getPageSubtitleCentered(translate("Eye Color")) description1 := getLabelCentered(translate("Select your eye color.")) description2 := getLabelCentered(translate("Select more than 1 color if your eyes contain multiple colors.")) eyeColorExists, currentEyeColorAttribute, err := myLocalProfiles.GetProfileData("Mate", "EyeColor") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } getCurrentEyeColorList := func()[]string{ if (eyeColorExists == false){ emptyList := make([]string, 0) return emptyList } currentEyeColorList := strings.Split(currentEyeColorAttribute, "+") return currentEyeColorList } currentEyeColorList := getCurrentEyeColorList() if (len(currentEyeColorList) > 4){ setErrorEncounteredPage(window, errors.New("MyLocalProfiles contains invalid eye color attribute"), previousPage) return } getCurrentEyeColorLabel := func()fyne.Widget{ if (eyeColorExists == false){ result := translate("No Response") eyeColorLabel := getBoldItalicLabel(result) return eyeColorLabel } currentEyeColorFormatted := strings.Join(currentEyeColorList, ", ") eyeColorLabel := getBoldLabel(currentEyeColorFormatted) return eyeColorLabel } currentEyeColorLabel := getCurrentEyeColorLabel() myEyeColorLabel := widget.NewLabel("My Eye Color:") currentEyeColorRow := container.NewHBox(layout.NewSpacer(), myEyeColorLabel, currentEyeColorLabel, layout.NewSpacer()) getEyeColorColumn := func(colorName string, colorsList []string)(*fyne.Container, error){ colorColumn := container.NewGridWithColumns(1) for _, colorCode := range colorsList{ colorSquare, err := getColorSquareAsFyneImage(colorCode) if (err != nil){ return nil, err } colorSquare.FillMode = canvas.ImageFillStretch colorColumn.Add(colorSquare) } chooseColorCheck := widget.NewCheck(colorName, func(newChoice bool){ getNewAttributeList := func()[]string{ if (newChoice == false){ newList, _ := helpers.DeleteAllMatchingItemsFromList(currentEyeColorList, colorName) return newList } newList := helpers.AddItemToStringListAndAvoidDuplicate(currentEyeColorList, colorName) return newList } newAttributeList := getNewAttributeList() if (len(newAttributeList) == 0){ err := myLocalProfiles.DeleteProfileData("Mate", "EyeColor") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() return } newEyeColorAttribute := strings.Join(newAttributeList, "+") err := myLocalProfiles.SetProfileData("Mate", "EyeColor", newEyeColorAttribute) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) colorSelected := slices.Contains(currentEyeColorList, colorName) if (colorSelected == true){ chooseColorCheck.Checked = true } colorColumn.Add(chooseColorCheck) return colorColumn, nil } //TODO: Add Gray blueColorsList := []string{"4d71a6", "9fa8a8", "345269"} greenColorsList := []string{"333b26", "6d712b", "505216"} amberColorsList := []string{"6e5c18", "694c19", "684818"} brownColorsList := []string{"522613", "47240f", "2a110c"} blueEyeColorColumn, err := getEyeColorColumn("Blue", blueColorsList) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } greenEyeColorColumn, err := getEyeColorColumn("Green", greenColorsList) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } amberEyeColorColumn, err := getEyeColorColumn("Amber", amberColorsList) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } brownEyeColorColumn, err := getEyeColorColumn("Brown", brownColorsList) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } selectEyeColorGrid := getContainerCentered(container.NewGridWithColumns(4, blueEyeColorColumn, greenEyeColorColumn, amberEyeColorColumn, brownEyeColorColumn)) noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "EyeColor") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() })) page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), currentEyeColorRow, widget.NewSeparator(), selectEyeColorGrid, noResponseButton) setPageContent(page, window) } func setBuildMateProfilePage_HairColor(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_HairColor(window, previousPage)} pageTitle := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) pageSubtitle := getPageSubtitleCentered(translate("Hair Color")) description1 := getLabelCentered(translate("Select your natural hair color.")) description2 := getLabelCentered(translate("Select 2 colors if your hair color is an average of two colors.")) //TODO: What if hair is gray/white // Users should probably select the color their hair was when they were a young adult. We should add gray or white as an option? hairColorExists, currentHairColorAttribute, err := myLocalProfiles.GetProfileData("Mate", "HairColor") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } getCurrentHairColorList := func()[]string{ if (hairColorExists == false){ emptyList := make([]string, 0) return emptyList } currentHairColorList := strings.Split(currentHairColorAttribute, "+") return currentHairColorList } currentHairColorList := getCurrentHairColorList() if (len(currentHairColorList) > 4){ setErrorEncounteredPage(window, errors.New("MyLocalProfiles contains invalid hair color attribute: " + currentHairColorAttribute), previousPage) return } getCurrentHairColorLabel := func()fyne.Widget{ if (hairColorExists == false){ result := translate("No Response") hairColorLabel := getBoldItalicLabel(result) return hairColorLabel } currentHairColorFormatted := strings.Join(currentHairColorList, "-") hairColorLabel := getBoldLabel(currentHairColorFormatted) return hairColorLabel } currentHairColorLabel := getCurrentHairColorLabel() myHairColorLabel := widget.NewLabel("My Hair Color:") currentHairColorRow := container.NewHBox(layout.NewSpacer(), myHairColorLabel, currentHairColorLabel, layout.NewSpacer()) getHairColorColumn := func(colorName string, colorsList []string)(*fyne.Container, error){ colorColumn := container.NewGridWithColumns(1) for _, colorCode := range colorsList{ colorSquare, err := getColorSquareAsFyneImage(colorCode) if (err != nil){ return nil, err } colorSquare.FillMode = canvas.ImageFillStretch colorColumn.Add(colorSquare) } chooseColorCheck := widget.NewCheck(colorName, func(newChoice bool){ getNewAttributeList := func()[]string{ if (newChoice == false){ newList, _ := helpers.DeleteAllMatchingItemsFromList(currentHairColorList, colorName) return newList } newList := helpers.AddItemToStringListAndAvoidDuplicate(currentHairColorList, colorName) return newList } newAttributeList := getNewAttributeList() if (len(newAttributeList) > 2){ currentPage() title := translate("Too Many Colors") dialogMessageA := getLabelCentered(translate("You can only select two colors.")) dialogContent := container.NewVBox(dialogMessageA) dialog.ShowCustom(title, translate("Close"), dialogContent, window) return } if (len(newAttributeList) == 0){ err := myLocalProfiles.DeleteProfileData("Mate", "HairColor") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() return } newHairColorAttribute := strings.Join(newAttributeList, "+") err := myLocalProfiles.SetProfileData("Mate", "HairColor", newHairColorAttribute) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) colorSelected := slices.Contains(currentHairColorList, colorName) if (colorSelected == true){ chooseColorCheck.Checked = true } colorColumn.Add(chooseColorCheck) return colorColumn, nil } blackColorsList := []string{"242424", "1d1719", "070300"} brownColorsList := []string{"5d3629", "362219", "472c24"} blondeColorsList := []string{"efd8a6", "edd096", "a57d4d"} orangeColorsList := []string{"f5743a", "8c5125", "883713"} blackHairColorColumn, err := getHairColorColumn("Black", blackColorsList) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } brownHairColorColumn, err := getHairColorColumn("Brown", brownColorsList) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } blondeHairColorColumn, err := getHairColorColumn("Blonde", blondeColorsList) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } orangeHairColorColumn, err := getHairColorColumn("Orange", orangeColorsList) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } selectHairColorGrid := getContainerCentered(container.NewGridWithColumns(4, blackHairColorColumn, brownHairColorColumn, blondeHairColorColumn, orangeHairColorColumn)) noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "HairColor") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() })) page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), currentHairColorRow, widget.NewSeparator(), selectHairColorGrid, noResponseButton) setPageContent(page, window) } func setBuildMateProfilePage_HairTexture(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_HairTexture(window, previousPage)} title := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) pageSubtitle := getPageSubtitleCentered(translate("Hair Texture")) description := getLabelCentered(translate("Enter your natural hair texture.")) option1Translated := translate("1 - Straight Hair") option2Translated := translate("2 - Slightly Wavy Hair") option3Translated := translate("3 - Wavy Hair") option4Translated := translate("4 - Big Curls") option5Translated := translate("5 - Small Curls") option6Translated := translate("6 - Very Tight Curls") hairTextureOptionsMap := map[string]string{ option1Translated: "1", option2Translated: "2", option3Translated: "3", option4Translated: "4", option5Translated: "5", option6Translated: "6", } hairTextureSelectorOptionsList := []string{option1Translated, option2Translated, option3Translated, option4Translated, option5Translated, option6Translated} hairTextureSelector := widget.NewRadioGroup(hairTextureSelectorOptionsList, func(response string){ if (response == ""){ myLocalProfiles.DeleteProfileData("Mate", "HairTexture") return } attributeValue, exists := hairTextureOptionsMap[response] if (exists == false){ setErrorEncounteredPage(window, errors.New("hairTextureOptionsMap missing translated option: " + response), currentPage) return } myLocalProfiles.SetProfileData("Mate", "HairTexture", attributeValue) }) exists, currentHairTexture, err := myLocalProfiles.GetProfileData("Mate", "HairTexture") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (exists == true){ isValid, err := helpers.VerifyStringIsIntWithinRange(currentHairTexture, 1, 6) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (isValid == false){ setErrorEncounteredPage(window, errors.New("MyLocalProfiles contains invalid hair texture: " + currentHairTexture), previousPage) return } if (currentHairTexture == "1"){ hairTextureSelector.Selected = option1Translated } else if (currentHairTexture == "2"){ hairTextureSelector.Selected = option2Translated } else if (currentHairTexture == "3"){ hairTextureSelector.Selected = option3Translated } else if (currentHairTexture == "4"){ hairTextureSelector.Selected = option4Translated } else if (currentHairTexture == "5"){ hairTextureSelector.Selected = option5Translated } else if (currentHairTexture == "6"){ hairTextureSelector.Selected = option6Translated } } hairTextureSelectorCentered := getWidgetCentered(hairTextureSelector) noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ myLocalProfiles.DeleteProfileData("Mate", "HairTexture") currentPage() })) page := container.NewVBox(title, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description, widget.NewSeparator(), hairTextureSelectorCentered, noResponseButton) setPageContent(page, window) } func setBuildMateProfilePage_SkinColor(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_SkinColor(window, previousPage)} pageTitle := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) pageSubtitle := getPageSubtitleCentered(translate("Skin Color")) description1 := getLabelCentered(translate("Select your skin color.")) description2 := getLabelCentered(translate("This may be difficult because skin color changes under different conditions.")) description3 := getLabelCentered(translate("Try your best to choose the best option.")) description4 := getLabelCentered(translate("Users are reminded of this when choosing their skin color desires.")) mySkinColorExists, currentSkinColorAttribute, err := myLocalProfiles.GetProfileData("Mate", "SkinColor") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (mySkinColorExists == true){ currentSkinColorInt, err := helpers.ConvertStringToInt(currentSkinColorAttribute) if (err != nil){ setErrorEncounteredPage(window, errors.New("MyLocalProfiles SkinColor is invalid: " + currentSkinColorAttribute), previousPage) return } if (currentSkinColorInt < 1 || currentSkinColorInt > 6){ setErrorEncounteredPage(window, errors.New("MyLocalProfiles SkinColor is invalid: " + currentSkinColorAttribute), previousPage) return } } getCurrentSkinColorLabel := func()fyne.Widget{ if (mySkinColorExists == false){ result := translate("No Response") skinColorLabel := getBoldItalicLabel(result) return skinColorLabel } skinColorLabel := getBoldLabel(currentSkinColorAttribute) return skinColorLabel } currentSkinColorLabel := getCurrentSkinColorLabel() mySkinColorLabel := widget.NewLabel("My Skin Color:") currentSkinColorRow := container.NewHBox(layout.NewSpacer(), mySkinColorLabel, currentSkinColorLabel, layout.NewSpacer()) getSkinColorColumn := func(colorIdentifier string, colorCode string)(*fyne.Container, error){ colorSquare, err := getColorSquareAsFyneImage(colorCode) if (err != nil){ return nil, err } colorSquare.FillMode = canvas.ImageFillStretch chooseColorCheck := widget.NewCheck(colorIdentifier, func(newChoice bool){ err := myLocalProfiles.DeleteProfileData("Mate", "SkinColor") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } err = myLocalProfiles.SetProfileData("Mate", "SkinColor", colorIdentifier) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) if (colorIdentifier == currentSkinColorAttribute){ chooseColorCheck.Checked = true } colorColumn := container.NewGridWithColumns(1, colorSquare, chooseColorCheck) return colorColumn, nil } skinColorColumn_1, err := getSkinColorColumn("1", "f4e3da") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } skinColorColumn_2, err := getSkinColorColumn("2", "f5d6b9") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } skinColorColumn_3, err := getSkinColorColumn("3", "dabe91") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } skinColorColumn_4, err := getSkinColorColumn("4", "ba9175") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } skinColorColumn_5, err := getSkinColorColumn("5", "916244") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } skinColorColumn_6, err := getSkinColorColumn("6", "744d2d") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } selectSkinColorGrid := getContainerCentered(container.NewGridWithColumns(6, skinColorColumn_1, skinColorColumn_2, skinColorColumn_3, skinColorColumn_4, skinColorColumn_5, skinColorColumn_6)) noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", "SkinColor") if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() })) page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, description3, description4, widget.NewSeparator(), currentSkinColorRow, widget.NewSeparator(), selectSkinColorGrid, noResponseButton) setPageContent(page, window) } func setBuildMateProfilePage_Infections(window fyne.Window, previousPage func()){ currentPage := func(){setBuildMateProfilePage_Infections(window, previousPage)} pageTitle := getPageTitleCentered(translate("Build Mate Profile - Physical")) backButton := getBackButtonCentered(previousPage) pageSubtitle := getPageSubtitleCentered(translate("Infections")) description := getLabelCentered(translate("Select the infections that you have.")) infectionNameColumn := container.NewVBox(widget.NewSeparator()) radioButtonsColumn := container.NewVBox(widget.NewSeparator()) noResponseButtonsColumn := container.NewVBox(widget.NewSeparator()) addInfectionRow := func(infectionName string, infectionAttributeName string)error{ infectionNameLabel := getBoldLabelCentered(infectionName) yesTranslated := translate("Yes") noTranslated := translate("No") untranslatedOptionsMap := map[string]string{ yesTranslated: "Yes", noTranslated: "No", } optionsList := []string{yesTranslated, noTranslated} yesNoSelector := widget.NewRadioGroup(optionsList, func(response string){ if (response == ""){ _ = myLocalProfiles.DeleteProfileData("Mate", infectionAttributeName) return } responseUntranslated, exists := untranslatedOptionsMap[response] if (exists == false){ setErrorEncounteredPage(window, errors.New("untranslatedOptionsMap missing response: " + response), currentPage) return } _ = myLocalProfiles.SetProfileData("Mate", infectionAttributeName, responseUntranslated) }) yesNoSelector.Horizontal = true myCurrentStatusExists, myCurrentInfectionStatus, err := myLocalProfiles.GetProfileData("Mate", infectionAttributeName) if (err != nil){ return err } if (myCurrentStatusExists == true){ yesNoSelector.Selected = translate(myCurrentInfectionStatus) } noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ err := myLocalProfiles.DeleteProfileData("Mate", infectionAttributeName) if (err != nil){ setErrorEncounteredPage(window, err, currentPage) return } currentPage() })) infectionNameColumn.Add(infectionNameLabel) radioButtonsColumn.Add(yesNoSelector) noResponseButtonsColumn.Add(noResponseButton) infectionNameColumn.Add(widget.NewSeparator()) radioButtonsColumn.Add(widget.NewSeparator()) noResponseButtonsColumn.Add(widget.NewSeparator()) return nil } err := addInfectionRow("HIV", "HasHIV") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } err = addInfectionRow("Genital Herpes", "HasGenitalHerpes") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } infectionsGrid := container.NewHBox(layout.NewSpacer(), infectionNameColumn, radioButtonsColumn, noResponseButtonsColumn, layout.NewSpacer()) page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description, widget.NewSeparator(), infectionsGrid) setPageContent(page, window) }