package gui // matchesGui.go implements pages to view a user's mate matches import "fyne.io/fyne/v2" import "fyne.io/fyne/v2/canvas" import "fyne.io/fyne/v2/container" import "fyne.io/fyne/v2/data/binding" import "fyne.io/fyne/v2/dialog" import "fyne.io/fyne/v2/layout" import "fyne.io/fyne/v2/theme" import "fyne.io/fyne/v2/widget" import "seekia/internal/appMemory" import "seekia/internal/badgerDatabase" import "seekia/internal/desires/mateDesires" import "seekia/internal/desires/myLocalDesires" import "seekia/internal/desires/myMateDesires" import "seekia/internal/encoding" import "seekia/internal/helpers" import "seekia/internal/identity" import "seekia/internal/imagery" import "seekia/internal/myBlockedUsers" import "seekia/internal/myIdentity" import "seekia/internal/myIgnoredUsers" import "seekia/internal/myLikedUsers" import "seekia/internal/myMatches" import "seekia/internal/myMatchScore" import "seekia/internal/mySettings" import "seekia/internal/network/appNetworkType/getAppNetworkType" import "seekia/internal/profiles/attributeDisplay" import "seekia/internal/profiles/viewableProfiles" import "strings" import "time" import "errors" import "slices" import "sync" func setMatchesPage(window fyne.Window){ appMemory.SetMemoryEntry("CurrentViewedPage", "Matches") checkIfPageHasChangedFunction := func()bool{ exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage") if (exists == false || currentViewedPage != "Matches"){ return true } return false } currentPage := func(){setMatchesPage(window)} title := getPageTitleCentered("My Matches") desiresIcon, err := getFyneImageIcon("Desires") if (err != nil){ setErrorEncounteredPage(window, err, func(){setHomePage(window)}) return } statsIcon, err := getFyneImageIcon("Stats") if (err != nil){ setErrorEncounteredPage(window, err, func(){setHomePage(window)}) return } matchScoreIcon, err := getFyneImageIcon("MatchScore") if (err != nil){ setErrorEncounteredPage(window, err, func(){setHomePage(window)}) return } customizeIcon, err := getFyneImageIcon("Info") if (err != nil){ setErrorEncounteredPage(window, err, func(){setHomePage(window)}) return } desiresButton := widget.NewButton(translate("Desires"), func(){ setViewAllMyDesiresPage(window, currentPage) }) desiresButtonWithIcon := container.NewGridWithColumns(1, desiresIcon, desiresButton) customizeButton := widget.NewButton("Customize", func(){ setCustomizeMatchDisplayPage(window, currentPage) }) customizeButtonWithIcon := container.NewGridWithColumns(1, customizeIcon, customizeButton) matchScoreButton := widget.NewButton(translate("Score"), func(){ setConfigureMatchScorePage(window, currentPage) }) matchScoreButtonWithIcon := container.NewGridWithColumns(1, matchScoreIcon, matchScoreButton) statsButton := widget.NewButton(translate("Stats"), func(){ setMyMatchStatisticsPage(window, currentPage) }) statsButtonWithIcon := container.NewGridWithColumns(1, statsIcon, statsButton) buttonsRow := getContainerCentered(container.NewGridWithRows(1, matchScoreButtonWithIcon, desiresButtonWithIcon, statsButtonWithIcon, customizeButtonWithIcon)) currentSortByAttribute, err := myMatches.GetMatchesSortByAttribute() if (err != nil) { setErrorEncounteredPage(window, err, func(){setHomePage(window)}) return } sortingByLabel := getBoldLabel(translate("Sorting By:")) sortByAttributeTitle, _, _, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(currentSortByAttribute) if (err != nil){ setErrorEncounteredPage(window, err, func(){setHomePage(window)}) return } sortByButton := widget.NewButton(translate(sortByAttributeTitle), func(){ setSelectMatchesSortByAttributePage(window, currentPage) }) getSortDirectionButtonWithIcon := func()(fyne.Widget, error){ currentSortDirection, err := myMatches.GetMatchesSortDirection() if (err != nil) { return nil, err } if (currentSortDirection == "Ascending"){ ascendingButton := widget.NewButtonWithIcon(translate("Ascending"), theme.MoveUpIcon(), func(){ appMemory.SetMemoryEntry("StopBuildMatchesYesNo", "Yes") _ = mySettings.SetSetting("MatchesSortDirection", "Descending") _ = mySettings.SetSetting("MatchesSortedStatus", "No") _ = mySettings.SetSetting("MatchesViewIndex", "0") currentPage() }) return ascendingButton, nil } descendingButton := widget.NewButtonWithIcon(translate("Descending"), theme.MoveDownIcon(), func(){ appMemory.SetMemoryEntry("StopBuildMatchesYesNo", "Yes") _ = mySettings.SetSetting("MatchesSortDirection", "Ascending") _ = mySettings.SetSetting("MatchesSortedStatus", "No") _ = mySettings.SetSetting("MatchesViewIndex", "0") currentPage() }) return descendingButton, nil } sortDirectionButton, err := getSortDirectionButtonWithIcon() if (err != nil) { setErrorEncounteredPage(window, err, func(){setHomePage(window)}) return } sortByRow := container.NewHBox(layout.NewSpacer(), sortingByLabel, sortByButton, sortDirectionButton, layout.NewSpacer()) appNetworkType, err := getAppNetworkType.GetAppNetworkType() if (err != nil) { setErrorEncounteredPage(window, err, func(){setHomePage(window)}) return } matchesReady, err := myMatches.GetMatchesReadyStatus(appNetworkType) if (err != nil) { setErrorEncounteredPage(window, err, func(){setHomePage(window)}) return } if (matchesReady == false){ progressPercentageBinding := binding.NewFloat() sortingProgressBinding := binding.NewString() startUpdateMatchesAndProgressBarFunction := func(){ err := myMatches.StartUpdatingMyMatches(appNetworkType) if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } sortingProgressBindingSet := false var encounteredError error for { pageHasChanged := checkIfPageHasChangedFunction() if (pageHasChanged == true){ appMemory.SetMemoryEntry("StopBuildMatchesYesNo", "Yes") return } buildEncounteredError, errorEncounteredString, buildIsStopped, matchesAreReady, currentPercentageProgress, err := myMatches.GetMyMatchesBuildStatus(appNetworkType) if (err != nil){ encounteredError = err break } if (buildEncounteredError == true){ encounteredError = errors.New(errorEncounteredString) break } if (buildIsStopped == true){ return } if (matchesAreReady == true){ progressPercentageBinding.Set(1) // We wait so that the loading bar will appear complete. time.Sleep(100 * time.Millisecond) currentPage() return } progressPercentageBinding.Set(currentPercentageProgress) if (currentPercentageProgress >= .50 && sortingProgressBindingSet == false){ numberOfMatches, err := myMatches.GetNumberOfGeneratedMatches() if (err != nil) { encounteredError = err break } if (numberOfMatches != 0){ numberOfMatchesString := helpers.ConvertIntToString(numberOfMatches) sortingProgressBinding.Set("Sorting " + numberOfMatchesString + " Matches...") } sortingProgressBindingSet = true } time.Sleep(100 * time.Millisecond) } // This will only be reached if an error occurred errorToShow := errors.New("Error encountered while generating matches: " + encounteredError.Error()) setErrorEncounteredPage(window, errorToShow, currentPage) } loadingLabel := getBoldLabelCentered("Loading Matches...") loadingBar := getWidgetCentered(widget.NewProgressBarWithData(progressPercentageBinding)) loadingDetailsLabel := widget.NewLabelWithData(sortingProgressBinding) loadingDetailsLabel.TextStyle = getFyneTextStyle_Italic() loadingDetailsLabelCentered := getWidgetCentered(loadingDetailsLabel) page := container.NewVBox(title, widget.NewSeparator(), buttonsRow, widget.NewSeparator(), sortByRow, widget.NewSeparator(), loadingLabel, loadingBar, loadingDetailsLabelCentered) setPageContent(page, window) go startUpdateMatchesAndProgressBarFunction() return } getSplashContainer := func()(*fyne.Container, error){ getRefreshResultsButtonText := func()(string, error){ needsRefresh, err := myMatches.CheckIfMyMatchesNeedRefresh() if (err != nil) { return "", err } if (needsRefresh == false){ return "Refresh Results", nil } return "Refresh Results - Updates Available!", nil } refreshButtonText, err := getRefreshResultsButtonText() if (err != nil){ return nil, err } refreshResultsButton := getWidgetCentered(widget.NewButtonWithIcon(refreshButtonText, theme.ViewRefreshIcon(), func(){ _ = mySettings.SetSetting("MatchesGeneratedStatus", "No") _ = mySettings.SetSetting("MatchesViewIndex", "0") currentPage() })) matchesReady, currentMatchesList, err := myMatches.GetMyMatchesList(appNetworkType) if (err != nil) { return nil, err } if (matchesReady == false){ // This should not happen, as matches were found to be ready already. return nil, errors.New("Matches not ready after being ready already.") } if (len(currentMatchesList) == 0){ noMatchesFoundLabel := getBoldLabelCentered("No Matches Found") description1 := getLabelCentered("Edit your desires to find more matches.") description2 := getLabelCentered("View your desire statistics to see which desires are reducing your matches.") viewDesireStatisticsButton := getWidgetCentered(widget.NewButtonWithIcon("View Desire Statistics", theme.VisibilityIcon(), func(){ setMyMatchStatisticsPage(window, currentPage) })) noMatchesFoundContainer := container.NewVBox(noMatchesFoundLabel, refreshResultsButton, widget.NewSeparator(), description1, description2, viewDesireStatisticsButton) return noMatchesFoundContainer, nil } numberOfMatches := len(currentMatchesList) numberOfMatchesString := helpers.ConvertIntToString(numberOfMatches) getNumberOfMatchesFoundText := func()string{ if (numberOfMatches == 1){ result := translate("1 Match Found") return result } result := numberOfMatchesString + " " + translate("Matches Found") return result } numberOfMatchesFoundText := getNumberOfMatchesFoundText() numberOfMatchesFoundLabel := getBoldLabelCentered(numberOfMatchesFoundText) getBeginRestartContinueButtons := func()(*fyne.Container, error){ getMatchesViewIndex := func()(int, error){ exists, currentIndex, err := mySettings.GetSetting("MatchesViewIndex") if (err != nil) { return 0, err } if (exists == false){ return 0, nil } currentIndexInt, err := helpers.ConvertStringToInt(currentIndex) if (err != nil) { return 0, err } if (currentIndexInt > (numberOfMatches-1)){ return 0, nil } return currentIndexInt, nil } currentViewIndex, err := getMatchesViewIndex() if (err != nil) { return nil, err } if (currentViewIndex == 0){ beginButton := getWidgetCentered(widget.NewButtonWithIcon("Begin Browsing", theme.NavigateNextIcon(), func(){ setBrowseMatchesPage(window, currentPage) })) return beginButton, nil } restartButton := getWidgetCentered(widget.NewButtonWithIcon("Restart From Beginning", theme.ContentUndoIcon(), func(){ mySettings.SetSetting("MatchesViewIndex", "0") setBrowseMatchesPage(window, currentPage) })) currentViewIndexString := helpers.ConvertIntToString(currentViewIndex + 1) continueButtonText := "Continue from profile " + currentViewIndexString + " of " + numberOfMatchesString continueButton := getWidgetCentered(widget.NewButtonWithIcon(continueButtonText, theme.MailForwardIcon(), func(){ setBrowseMatchesPage(window, currentPage) })) buttons := container.NewVBox(restartButton, continueButton) return buttons, nil } beginRestartContinueButtons, err := getBeginRestartContinueButtons() if (err != nil) { return nil, err } splashPageContent := container.NewVBox(numberOfMatchesFoundLabel, widget.NewSeparator(), beginRestartContinueButtons, refreshResultsButton) return splashPageContent, nil } splashContainer, err := getSplashContainer() if (err != nil){ setErrorEncounteredPage(window, err, func(){setHomePage(window)}) return } page := container.NewVBox(title, widget.NewSeparator(), buttonsRow, widget.NewSeparator(), sortByRow, widget.NewSeparator(), splashContainer) setPageContent(page, window) } // This page should only be called if matches are ready and at least 1 exists func setBrowseMatchesPage(window fyne.Window, previousPage func()){ currentPage := func(){setBrowseMatchesPage(window, previousPage)} title := getPageTitleCentered("Browse Matches") backButton := getBackButtonCentered(previousPage) getPageContent := func()(*fyne.Container, error){ appNetworkType, err := getAppNetworkType.GetAppNetworkType() if (err != nil) { return nil, err } matchesReady, currentMatchesList, err := myMatches.GetMyMatchesList(appNetworkType) if (err != nil) { return nil, err } if (matchesReady == false) { // This should not happen, because this page should only be called if matches are already ready return nil, errors.New("setBrowseMatchesPage called when matches are not ready.") } if (len(currentMatchesList) == 0){ return nil, errors.New("setBrowseMatchesPage called with no matches found.") } numberOfMatches := len(currentMatchesList) getViewIndex := func()(int, error){ exists, currentIndexString, err := mySettings.GetSetting("MatchesViewIndex") if (err != nil) { return 0, err } if (exists == false){ return 0, nil } currentIndex, err := helpers.ConvertStringToInt(currentIndexString) if (err != nil){ return 0, errors.New("MySettings malformed: Contains invalid MatchesViewIndex: " + currentIndexString) } if (currentIndex < 0){ return 0, nil } maximumIndex := numberOfMatches-1 if (currentIndex > maximumIndex){ return maximumIndex, nil } return currentIndex, nil } currentIndex, err := getViewIndex() if (err != nil) { return nil, err } pageContent := container.NewVBox() if (numberOfMatches > 1){ getNavigationButtons := func() *fyne.Container{ getGoToBeginningButton := func()fyne.Widget{ if (currentIndex == 0){ emptyButton := widget.NewButton("", nil) return emptyButton } goToBeginningButton := widget.NewButtonWithIcon("", theme.MediaSkipPreviousIcon(), func(){ _ = mySettings.SetSetting("MatchesViewIndex", "0") currentPage() }) return goToBeginningButton } getGoToEndButton := func()fyne.Widget{ finalIndex := numberOfMatches - 1 if (currentIndex >= finalIndex){ emptyButton := widget.NewButton("", nil) return emptyButton } goToEndButton := widget.NewButtonWithIcon("", theme.MediaSkipNextIcon(), func(){ finalIndexString := helpers.ConvertIntToString(finalIndex) _ = mySettings.SetSetting("MatchesViewIndex", finalIndexString) currentPage() }) return goToEndButton } getLeftButton := func()fyne.Widget{ if (currentIndex == 0){ emptyButton := widget.NewButton("", nil) return emptyButton } leftButton := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func(){ newIndex := helpers.ConvertIntToString(currentIndex-1) _ = mySettings.SetSetting("MatchesViewIndex", newIndex) currentPage() }) return leftButton } getRightButton := func()fyne.Widget{ finalIndex := numberOfMatches - 1 if (currentIndex >= finalIndex){ emptyButton := widget.NewButton("", nil) return emptyButton } rightButton := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func(){ newIndex := helpers.ConvertIntToString(currentIndex+1) _ = mySettings.SetSetting("MatchesViewIndex", newIndex) currentPage() }) return rightButton } goToBeginningButton := getGoToBeginningButton() goToEndButton := getGoToEndButton() leftButton := getLeftButton() rightButton := getRightButton() if (numberOfMatches == 1){ buttons := getContainerCentered(container.NewGridWithRows(1, goToBeginningButton, leftButton, rightButton, goToEndButton)) return buttons } matchRank := currentIndex + 1 currentRankString := helpers.ConvertIntToString(matchRank) numberOfMatchesString := helpers.ConvertIntToString(numberOfMatches) currentRankDescription := getBoldLabel(currentRankString + "/" + numberOfMatchesString) buttons := getContainerCentered(container.NewGridWithRows(1, goToBeginningButton, leftButton, currentRankDescription, rightButton, goToEndButton)) return buttons } navButtons := getNavigationButtons() pageContent.Add(navButtons) pageContent.Add(widget.NewSeparator()) } getMatchDisplay := func()(*fyne.Container, error){ if (currentIndex >= len(currentMatchesList) || currentIndex < 0){ return nil, errors.New("Match index out of range.") } matchIdentityHash := currentMatchesList[currentIndex] userIsBlocked, _, _, _, err := myBlockedUsers.CheckIfUserIsBlocked(matchIdentityHash) if (err != nil) { return nil, err } if (userIsBlocked == true){ isBlockedDescription1 := getBoldLabelCentered("User is blocked.") isBlockedDescription2 := getLabelCentered("You have blocked this match.") isBlockedDescription3 := getLabelCentered("Refresh your matches to fix this.") refreshMatchesButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh Matches", theme.ViewRefreshIcon(), func(){ _ = mySettings.SetSetting("MatchesGeneratedStatus", "No") _ = mySettings.SetSetting("MatchesViewIndex", "0") setMatchesPage(window) })) userIsBlockedSection := container.NewVBox(isBlockedDescription1, isBlockedDescription2, isBlockedDescription3, refreshMatchesButton) return userIsBlockedSection, nil } getShowIgnoredMatchesBool := func()(bool, error){ exists, isIgnoredIsEnabled, err := myLocalDesires.GetDesire("IsIgnored_FilterAll") if (err != nil) { return false, err } if (exists == false){ // Desire is disabled. return true, nil } if (isIgnoredIsEnabled == "No"){ // Desire is disabled. // We will show ignored matches return true, nil } exists, isIgnoredDesire, err := myLocalDesires.GetDesire("IsIgnored") if (err != nil) { return false, err } if (exists == false){ // Desire does not exist. // We will show ignored matches return true, nil } // isIgnoredDesire is a list of base64 encoded values which the user wants // We see if "Yes" is contained within the list // If it is not, then the user desires only non-ignored users myDesiredChoicesList := strings.Split(isIgnoredDesire, "+") yesBase64 := encoding.EncodeBytesToBase64String([]byte("Yes")) yesExists := slices.Contains(myDesiredChoicesList, yesBase64) if (yesExists == true){ // We want to see ignored users in our matches return true, nil } return false, nil } showIgnoredMatchesBool, err := getShowIgnoredMatchesBool() if (err != nil) { return nil, err } userIsIgnored, _, _, _, err := myIgnoredUsers.CheckIfUserIsIgnored(matchIdentityHash) if (err != nil) { return nil, err } if (userIsIgnored == true && showIgnoredMatchesBool == false){ isIgnoredDescription1 := getBoldLabelCentered("User is ignored.") isIgnoredDescription2 := getLabelCentered("You have ignored this match.") isIgnoredDescription3 := getLabelCentered("Refresh your matches to fix this.") refreshMatchesButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh Matches", theme.ViewRefreshIcon(), func(){ _ = mySettings.SetSetting("MatchesGeneratedStatus", "No") _ = mySettings.SetSetting("MatchesViewIndex", "0") setMatchesPage(window) })) userIsIgnoredSection := container.NewVBox(isIgnoredDescription1, isIgnoredDescription2, isIgnoredDescription3, refreshMatchesButton) return userIsIgnoredSection, nil } profileExists, _, getAnyAttributeFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(matchIdentityHash, appNetworkType, true, false, true) if (err != nil) { return nil, err } if (profileExists == false) { matchNotFoundLabel := getBoldLabelCentered("Match Not Found") description1 := getLabelCentered("Their profile may have been deleted.") description2 := getLabelCentered("Refresh your matches to fix this.") refreshMatchesButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh Matches", theme.ViewRefreshIcon(), func(){ _ = mySettings.SetSetting("MatchesGeneratedStatus", "No") _ = mySettings.SetSetting("MatchesViewIndex", "0") setMatchesPage(window) })) matchNotFoundSection := container.NewVBox(matchNotFoundLabel, description1, description2, refreshMatchesButton) return matchNotFoundSection, nil } exists, _, userIsDisabled, err := getAnyAttributeFunction("Disabled") if (err != nil){ return nil, err } if (exists == true && userIsDisabled == "Yes"){ matchIsDisabledLabel := getBoldLabelCentered("User Is Disabled") description1 := getLabelCentered("This user has disabled their profile.") description2 := getLabelCentered("Refresh your matches to fix this.") refreshMatchesButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh Matches", theme.ViewRefreshIcon(), func(){ _ = mySettings.SetSetting("MatchesGeneratedStatus", "No") _ = mySettings.SetSetting("MatchesViewIndex", "0") setMatchesPage(window) })) matchIsDisabledSection := container.NewVBox(matchIsDisabledLabel, description1, description2, refreshMatchesButton) return matchIsDisabledSection, nil } // We see if user's newest viewable profile still fulfills our desires profilePassesDesires, err := myMateDesires.CheckIfMateProfilePassesAllMyDesires(false, "", getAnyAttributeFunction) if (err != nil) { return nil, err } if (profilePassesDesires == false){ userIsNotAMatchLabel := getBoldLabelCentered("User Is Not A Match Anymore") description1 := getLabelCentered("This user has changed their profile and they are no longer a match.") description2 := getLabelCentered("Refresh your matches to fix this.") refreshMatchesButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh Matches", theme.ViewRefreshIcon(), func(){ _ = mySettings.SetSetting("MatchesGeneratedStatus", "No") _ = mySettings.SetSetting("MatchesViewIndex", "0") setMatchesPage(window) })) userIsNotAMatchSection := container.NewVBox(userIsNotAMatchLabel, description1, description2, refreshMatchesButton) return userIsNotAMatchSection, nil } getAvatarOrImage := func()(*canvas.Image, error){ photosExist, _, photosAttribute, err := getAnyAttributeFunction("Photos") if (err != nil) { return nil, err } if (photosExist == true){ photosList := strings.Split(photosAttribute, "+") firstPhotoBase64String := photosList[0] goImageObject, err := imagery.ConvertWEBPBase64StringToCroppedDownsizedImageObject(firstPhotoBase64String) if (err != nil) { return nil, errors.New("Database corrupt: Contains profile with invalid Photos attribute") } fyneImageObject := canvas.NewImageFromImage(goImageObject) fyneImageObject.FillMode = canvas.ImageFillContain return fyneImageObject, nil } getAvatarEmojiIdentifier := func()(int, error){ exists, _, emojiIdentifier, err := getAnyAttributeFunction("Avatar") if (err != nil){ return 0, err } if (exists == false){ // This is the avatar for users who have not selected an avatar return 2929, nil } emojiIdentifierInt, err := helpers.ConvertStringToInt(emojiIdentifier) if (err != nil) { return 0, errors.New("Database corrupt: Contains user profile with invalid avatar: " + emojiIdentifier) } return emojiIdentifierInt, nil } avatarEmojiIdentifier, err := getAvatarEmojiIdentifier() if (err != nil) { return nil, err } emojiImage, err := getEmojiImageObject(avatarEmojiIdentifier) if (err != nil){ return nil, err } emojiFyneImage := canvas.NewImageFromImage(emojiImage) emojiFyneImage.FillMode = canvas.ImageFillContain return emojiFyneImage, nil } avatarOrImage, err := getAvatarOrImage() if (err != nil) { return nil, err } avatarOrImage.SetMinSize(getCustomFyneSize(10)) avatarOrImageHeightenerA := container.NewVBox(widget.NewLabel(""), widget.NewLabel(""), widget.NewLabel("")) avatarOrImageHeightenerB := container.NewVBox(widget.NewLabel(""), widget.NewLabel(""), widget.NewLabel("")) avatarOrImageHeightened := getContainerCentered(container.NewGridWithRows(1, avatarOrImageHeightenerA, avatarOrImage, avatarOrImageHeightenerB)) viewProfileButton := getWidgetCentered(widget.NewButtonWithIcon("View Profile", theme.VisibilityIcon(), func(){ setViewPeerProfilePageFromIdentityHash(window, matchIdentityHash, currentPage) })) getAttributesSection := func()(*fyne.Container, error){ getDisplayAttributesList := func()([]string, error){ // First attribute is always the "Sort By" attribute currentSortByAttribute, err := myMatches.GetMatchesSortByAttribute() if (err != nil) { return nil, err } exists, currentAttributesListString, err := mySettings.GetSetting("CustomMatchDisplayAttributesList") if (err != nil) { return nil, err } if (exists == false){ newDisplayAttributesList := []string{currentSortByAttribute} return newDisplayAttributesList, nil } currentAttributesList := strings.Split(currentAttributesListString, ",") displayAttributesListPruned, _ := helpers.DeleteAllMatchingItemsFromList(currentAttributesList, currentSortByAttribute) newDisplayAttributesList := []string{currentSortByAttribute} newDisplayAttributesList = append(newDisplayAttributesList, displayAttributesListPruned...) return newDisplayAttributesList, nil } displayAttributesList, err := getDisplayAttributesList() if (err != nil){ return nil, err } attributesSection := container.NewVBox() for _, attributeName := range displayAttributesList{ attributeTitle, _, formatValueFunction, attributeUnits, missingValueText, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName) if (err != nil) { return nil, err } attributeTitleLabel := getBoldLabel(attributeTitle + ":") getAttributeValueText := func()(string, error){ attributeValueExists, _, attributeValue, err := getAnyAttributeFunction(attributeName) if (err != nil) { return "", err } if (attributeValueExists == false){ return missingValueText, nil } formattedValue, err := formatValueFunction(attributeValue) if (err != nil) { return "", err } result := formattedValue + attributeUnits return result, nil } attributeValueText, err := getAttributeValueText() if (err != nil) { return nil, err } attributeValueLabel := widget.NewLabel(attributeValueText) attributeRow := container.NewHBox(layout.NewSpacer(), attributeTitleLabel, attributeValueLabel, layout.NewSpacer()) attributesSection.Add(attributeRow) } return attributesSection, nil } attributesSection, err := getAttributesSection() if (err != nil) { return nil, err } getLikeOrUnlikeButtonWithIcon := func()(*fyne.Container, error){ userIsLiked, _, err := myLikedUsers.CheckIfUserIsLiked(matchIdentityHash) if (err != nil) { return nil, err } if (userIsLiked == true){ unlikeIcon, err := getFyneImageIcon("Unlike") if (err != nil) { return nil, err } unlikeButton := widget.NewButton("Unlike", func(){ setConfirmUnlikeUserPage(window, matchIdentityHash, currentPage, currentPage) }) unlikeButtonWithIcon := container.NewGridWithColumns(1, unlikeIcon, unlikeButton) return unlikeButtonWithIcon, nil } likeIcon, err := getFyneImageIcon("Like") if (err != nil) { return nil, err } likeButton := widget.NewButton("Like", func(){ setConfirmLikeUserPage(window, matchIdentityHash, currentPage, currentPage) }) likeButtonWithIcon := container.NewGridWithColumns(1, likeIcon, likeButton) return likeButtonWithIcon, nil } likeOrUnlikeButtonWithIcon, err := getLikeOrUnlikeButtonWithIcon() if (err != nil) { return nil, err } getIgnoreOrUnignoreButtonWithIcon := func()(*fyne.Container, error){ if (userIsIgnored == true){ unignoreIcon := widget.NewIcon(theme.VisibilityIcon()) unignoreButton := widget.NewButton("Unignore", func(){ setConfirmUnignoreUserPage(window, matchIdentityHash, currentPage, currentPage) }) unignoreButtonWithIcon := container.NewGridWithColumns(1, unignoreIcon, unignoreButton) return unignoreButtonWithIcon, nil } ignoreIcon := widget.NewIcon(theme.VisibilityOffIcon()) ignoreButton := widget.NewButton("Ignore", func(){ setConfirmIgnoreUserPage(window, matchIdentityHash, currentPage, currentPage) }) ignoreButtonWithIcon := container.NewGridWithColumns(1, ignoreIcon, ignoreButton) return ignoreButtonWithIcon, nil } ignoreOrUnignoreButtonWithIcon, err := getIgnoreOrUnignoreButtonWithIcon() if (err != nil) { return nil, err } greetIcon, err := getFyneImageIcon("Greet") if (err != nil) { return nil, err } greetButton := widget.NewButton("Greet", func(){ setConfirmGreetOrRejectUserPage(window, "Greet", matchIdentityHash, currentPage, currentPage) }) greetButtonWithIcon := container.NewGridWithColumns(1, greetIcon, greetButton) moreIcon, err := getFyneImageIcon("Plus") if (err != nil) { return nil, err } moreButton := widget.NewButton("More", func(){ setViewPeerActionsPage(window, matchIdentityHash, currentPage) }) moreButtonWithIcon := container.NewGridWithColumns(1, moreIcon, moreButton) actionsRow := getContainerCentered(container.NewGridWithRows(1, likeOrUnlikeButtonWithIcon, ignoreOrUnignoreButtonWithIcon, greetButtonWithIcon, moreButtonWithIcon)) matchDisplay := container.NewVBox(avatarOrImageHeightened, viewProfileButton, widget.NewSeparator(), attributesSection, widget.NewSeparator(), actionsRow) return matchDisplay, nil } matchDisplay, err := getMatchDisplay() if (err != nil) { return nil, err } pageContent.Add(matchDisplay) return pageContent, nil } pageContent, err := getPageContent() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), pageContent) setPageContent(page, window) } func setSelectMatchesSortByAttributePage(window fyne.Window, previousPage func()){ appMemory.SetMemoryEntry("CurrentViewedPage", "SortBySelectPage_Matches") title := getPageTitleCentered("Select Sort By Attribute") backButton := getBackButtonCentered(previousPage) description := getLabelCentered("Choose the attribute to sort your matches by.") getPageContent := func()(*fyne.Container, error){ generalAttributeButtonsGrid := container.NewGridWithColumns(1) physicalAttributeButtonsGrid := container.NewGridWithColumns(1) lifestyleAttributeButtonsGrid := container.NewGridWithColumns(1) mentalAttributeButtonsGrid := container.NewGridWithColumns(1) addAttributeSelectButton := func(attributeType string, attributeName string, sortDirection string)error{ attributeTitle, _, _, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName) if (err != nil) { return err } attributeButton := widget.NewButton(attributeTitle, func(){ _ = mySettings.SetSetting("MatchesSortedStatus", "No") _ = mySettings.SetSetting("MatchesSortByAttribute", attributeName) _ = mySettings.SetSetting("MatchesSortDirection", sortDirection) _ = mySettings.SetSetting("MatchesViewIndex", "0") previousPage() }) if (attributeType == "General"){ generalAttributeButtonsGrid.Add(attributeButton) } else if (attributeType == "Physical"){ physicalAttributeButtonsGrid.Add(attributeButton) } else if (attributeType == "Lifestyle"){ lifestyleAttributeButtonsGrid.Add(attributeButton) } else if (attributeType == "Mental"){ mentalAttributeButtonsGrid.Add(attributeButton) } else { return errors.New("addSelectButton called with invalid attributeType: " + attributeType) } return nil } generalLabel := getBoldLabelCentered("General") err := addAttributeSelectButton("General", "MatchScore", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("General", "Distance", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("General", "SearchTermsCount", "Descending") if (err != nil) { return nil, err } physicalLabel := getBoldLabelCentered("Physical") err = addAttributeSelectButton("Physical", "Age", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "Height", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "BodyFat", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "BodyMuscle", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "SkinColor", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "HairTexture", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "RacialSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "EyeColorSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "EyeColorGenesSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "HairColorSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "HairColorGenesSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "SkinColorSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "SkinColorGenesSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "HairTextureSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "HairTextureGenesSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "FacialStructureGenesSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "23andMe_AncestralSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "23andMe_MaternalHaplogroupSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "23andMe_PaternalHaplogroupSimilarity", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringProbabilityOfAnyMonogenicDisease", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "TotalPolygenicDiseaseRiskScore", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringTotalPolygenicDiseaseRiskScore", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "AutismRiskScore", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringAutismRiskScore", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "ObesityRiskScore", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringObesityRiskScore", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringBlueEyesProbability", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringGreenEyesProbability", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringHazelEyesProbability", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringBrownEyesProbability", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringLactoseToleranceProbability", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringStraightHairProbability", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringCurlyHairProbability", "Descending") if (err != nil) { return nil, err } // Numeric Traits: err = addAttributeSelectButton("Physical", "HomosexualnessScore", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringHomosexualnessScore", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "PredictedHeight", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "OffspringPredictedHeight", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Physical", "23andMe_NeanderthalVariants", "Descending") if (err != nil) { return nil, err } lifestyleLabel := getBoldLabelCentered("Lifestyle") err = addAttributeSelectButton("Lifestyle", "WealthInGold", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "Fame", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "FruitRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "VegetablesRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "NutsRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "GrainsRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "DairyRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "SeafoodRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "BeefRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "PorkRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "PoultryRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "EggsRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "BeansRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "AlcoholFrequency", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "TobaccoFrequency", "Ascending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Lifestyle", "CannabisFrequency", "Ascending") if (err != nil) { return nil, err } mentalLabel := getBoldLabelCentered("Mental") err = addAttributeSelectButton("Mental", "PetsRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Mental", "CatsRating", "Descending") if (err != nil) { return nil, err } err = addAttributeSelectButton("Mental", "DogsRating", "Descending") if (err != nil) { return nil, err } generalAttributeButtonsGridCentered := getContainerCentered(generalAttributeButtonsGrid) physicalAttributeButtonsGridCentered := getContainerCentered(physicalAttributeButtonsGrid) lifestyleAttributeButtonsGridCentered := getContainerCentered(lifestyleAttributeButtonsGrid) mentalAttributeButtonsGridCentered := getContainerCentered(mentalAttributeButtonsGrid) pageContent := container.NewVBox(generalLabel, widget.NewSeparator(), generalAttributeButtonsGridCentered, widget.NewSeparator(), physicalLabel, widget.NewSeparator(), physicalAttributeButtonsGridCentered, widget.NewSeparator(), lifestyleLabel, widget.NewSeparator(), lifestyleAttributeButtonsGridCentered, widget.NewSeparator(), mentalLabel, widget.NewSeparator(), mentalAttributeButtonsGridCentered) return pageContent, nil } pageContent, err := getPageContent() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), pageContent) setPageContent(page, window) } func setMyMatchStatisticsPage(window fyne.Window, previousPage func()){ pageIdentifier, err := helpers.GetNewRandomHexString(16) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } appMemory.SetMemoryEntry("CurrentViewedPage", pageIdentifier) checkIfPageHasChangedFunction := func()bool{ exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage") if (exists == true && currentViewedPage == pageIdentifier){ return false } return true } currentPage := func(){setMyMatchStatisticsPage(window, previousPage)} title := getPageTitleCentered("My Match Statistics") backButton := getBackButtonCentered(previousPage) description := getLabelCentered("Below are statistics about your matches.") accuracyWarningButton := getWidgetCentered(widget.NewButtonWithIcon("Statistics Warning", theme.WarningIcon(), func(){ setMateDesireStatisticsWarningPage(window, currentPage) })) totalMateUsersBinding := binding.NewString() totalBlockedUsersBinding := binding.NewString() numberOfMatchesBinding := binding.NewString() matchPercentageBinding := binding.NewString() totalMateUsersDescription := widget.NewLabel("Total Mate Users:") totalMateUsersLabel := widget.NewLabelWithData(totalMateUsersBinding) totalMateUsersLabel.TextStyle = getFyneTextStyle_Bold() totalMateUsersRow := container.NewHBox(layout.NewSpacer(), totalMateUsersDescription, totalMateUsersLabel, layout.NewSpacer()) totalBlockedUsersDescription := widget.NewLabel("Total Blocked Users:") totalBlockedUsersLabel := widget.NewLabelWithData(totalBlockedUsersBinding) totalBlockedUsersLabel.TextStyle = getFyneTextStyle_Bold() totalBlockedUsersRow := container.NewHBox(layout.NewSpacer(), totalBlockedUsersDescription, totalBlockedUsersLabel, layout.NewSpacer()) numberOfMatchesDescription := widget.NewLabel("Number of Matches:") numberOfMatchesLabel := widget.NewLabelWithData(numberOfMatchesBinding) numberOfMatchesLabel.TextStyle = getFyneTextStyle_Bold() numberOfMatchesRow := container.NewHBox(layout.NewSpacer(), numberOfMatchesDescription, numberOfMatchesLabel, layout.NewSpacer()) matchPercentageDescription := widget.NewLabel("Match Percentage:") matchPercentageLabel := widget.NewLabelWithData(matchPercentageBinding) matchPercentageLabel.TextStyle = getFyneTextStyle_Bold() matchPercentageRow := container.NewHBox(layout.NewSpacer(), matchPercentageDescription, matchPercentageLabel, layout.NewSpacer()) appNetworkType, err := getAppNetworkType.GetAppNetworkType() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } updateBindingsFunction := func(){ var resultsReadyBoolMutex sync.RWMutex resultsReadyBool := false updateBindingsWithLoadingText := func(){ secondsElapsed := 0 for{ resultsReadyBoolMutex.RLock() resultsReady := resultsReadyBool resultsReadyBoolMutex.RUnlock() if (resultsReady == true){ return } pageHasChanged := checkIfPageHasChangedFunction() if (pageHasChanged == true){ return } if (secondsElapsed % 3 == 0){ totalMateUsersBinding.Set("Loading.") numberOfMatchesBinding.Set("Loading.") matchPercentageBinding.Set("Loading.") } else if (secondsElapsed %3 == 1){ totalMateUsersBinding.Set("Loading..") numberOfMatchesBinding.Set("Loading..") matchPercentageBinding.Set("Loading..") } else { totalMateUsersBinding.Set("Loading...") numberOfMatchesBinding.Set("Loading...") matchPercentageBinding.Set("Loading...") } time.Sleep(time.Second) secondsElapsed += 1 } } go updateBindingsWithLoadingText() updateBindingsWithResultFunction := func()error{ // We use below to make sure we do not count ourselves as a peer profile myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Mate") if (err != nil) { return err } mateIdentityHashesList, err := badgerDatabase.GetAllProfileIdentityHashes("Mate") if (err != nil) { return err } numberOfUsersWithEnabledViewableProfile := 0 numberOfBlockedUsers := 0 numberOfMatches := 0 for _, userIdentityHash := range mateIdentityHashesList{ if (myIdentityExists == true && userIdentityHash == myIdentityHash){ // We don't count ourselves as a potential match continue } profileExists, _, getAnyUserProfileAttributeFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(userIdentityHash, appNetworkType, true, false, false) if (err != nil) { return err } if (profileExists == false) { continue } exists, _, _, err := getAnyUserProfileAttributeFunction("Disabled") if (err != nil) { return err } if (exists == true){ // Profile is disabled continue } numberOfUsersWithEnabledViewableProfile += 1 userIsBlocked, _, _, _, err := myBlockedUsers.CheckIfUserIsBlocked(userIdentityHash) if (err != nil) { return err } if (userIsBlocked == true){ numberOfBlockedUsers += 1 continue } passesMyDesires, err := myMateDesires.CheckIfMateProfilePassesAllMyDesires(false, "", getAnyUserProfileAttributeFunction) if (err != nil) { return err } if (passesMyDesires == true){ numberOfMatches += 1 } } resultsReadyBoolMutex.Lock() resultsReadyBool = true resultsReadyBoolMutex.Unlock() getMatchPercentage := func()float64{ if (numberOfUsersWithEnabledViewableProfile == 0){ return 0 } matchPercentage := (float64(numberOfMatches)/float64(numberOfUsersWithEnabledViewableProfile)) * 100 return matchPercentage } matchPercentage := getMatchPercentage() totalUsersString := helpers.ConvertIntToString(numberOfUsersWithEnabledViewableProfile) totalBlockedUsersString := helpers.ConvertIntToString(numberOfBlockedUsers) numberOfMatchesString := helpers.ConvertIntToString(numberOfMatches) matchPercentageString := helpers.ConvertFloat64ToStringRounded(matchPercentage, 1) totalMateUsersBinding.Set(totalUsersString) totalBlockedUsersBinding.Set(totalBlockedUsersString) numberOfMatchesBinding.Set(numberOfMatchesString) matchPercentageBinding.Set(matchPercentageString + "%") return nil } err := updateBindingsWithResultFunction() if (err != nil){ pageHasChanged := checkIfPageHasChangedFunction() if (pageHasChanged == false){ setErrorEncounteredPage(window, err, previousPage) } } } viewAllDesireStatisticsButton := getWidgetCentered(widget.NewButtonWithIcon("View All My Desire Statistics", theme.VisibilityIcon(), func(){ setViewAllMyDesireStatisticsPage(window, false, 0, 0, nil, currentPage) })) page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), accuracyWarningButton, widget.NewSeparator(), totalMateUsersRow, totalBlockedUsersRow, numberOfMatchesRow, matchPercentageRow, viewAllDesireStatisticsButton) setPageContent(page, window) go updateBindingsFunction() } func setCustomizeMatchDisplayPage(window fyne.Window, previousPage func()){ appMemory.SetMemoryEntry("CurrentViewedPage", "CustomizeMatchDisplay") currentPage := func(){setCustomizeMatchDisplayPage(window, previousPage)} title := getPageTitleCentered("Customize Match Display") backButton := getBackButtonCentered(previousPage) description1 := getLabelCentered("Select the attributes to show when browsing matches.") description2 := getLabelCentered("A maximum of 5 attributes can be selected.") getCustomDisplayAttributesList := func()([]string, error){ exists, currentAttributesListString, err := mySettings.GetSetting("CustomMatchDisplayAttributesList") if (err != nil) { return nil, err } if (exists == false){ emptyList := make([]string, 0) return emptyList, nil } currentAttributesList := strings.Split(currentAttributesListString, ",") return currentAttributesList, nil } customDisplayAttributesList, err := getCustomDisplayAttributesList() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } addAttributeButton := getWidgetCentered(widget.NewButtonWithIcon("Add Attribute", theme.ContentAddIcon(), func(){ if (len(customDisplayAttributesList) >= 5){ dialogTitle := translate("Attribute Limit Reached.") dialogMessageA := getLabelCentered("You can only select 5 attributes.") dialogContent := container.NewVBox(dialogMessageA) dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window) return } setAddAttributeToCustomMatchDisplayPage(window, currentPage, currentPage) })) if (len(customDisplayAttributesList) == 0){ noAttributesSelectedLabel := getBoldLabelCentered("No Attributes Selected") page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), noAttributesSelectedLabel, addAttributeButton) setPageContent(page, window) return } currentAttributesLabel := getItalicLabelCentered("Current Attributes:") getCurrentAttributesGrid := func()(*fyne.Container, error){ attributeIndexesColumn := container.NewVBox() attributeTitlesColumn := container.NewVBox() attributeRemoveButtonsColumn := container.NewVBox() for index, attributeName := range customDisplayAttributesList{ attributeIndexString := helpers.ConvertIntToString(index+1) + "." attributeTitle, _, _, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName) if (err != nil) { return nil, err } attributeIndexLabel := getBoldLabelCentered(attributeIndexString) attributeTitleLabel := getBoldLabelCentered(attributeTitle) deleteAttributeButton := widget.NewButtonWithIcon("", theme.DeleteIcon(), func(){ newAttributesList, _ := helpers.DeleteAllMatchingItemsFromList(customDisplayAttributesList, attributeName) if (len(newAttributesList) == 0){ err := mySettings.DeleteSetting("CustomMatchDisplayAttributesList") if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } currentPage() return } newAttributesListString := strings.Join(newAttributesList, ",") err := mySettings.SetSetting("CustomMatchDisplayAttributesList", newAttributesListString) if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) attributeIndexesColumn.Add(attributeIndexLabel) attributeTitlesColumn.Add(attributeTitleLabel) attributeRemoveButtonsColumn.Add(deleteAttributeButton) attributeIndexesColumn.Add(widget.NewSeparator()) attributeTitlesColumn.Add(widget.NewSeparator()) attributeRemoveButtonsColumn.Add(widget.NewSeparator()) } attributesGrid := container.NewHBox(layout.NewSpacer(), attributeIndexesColumn, attributeTitlesColumn, attributeRemoveButtonsColumn, layout.NewSpacer()) return attributesGrid, nil } currentAttributesGrid, err := getCurrentAttributesGrid() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), addAttributeButton, widget.NewSeparator(), currentAttributesLabel, currentAttributesGrid) setPageContent(page, window) } func setAddAttributeToCustomMatchDisplayPage(window fyne.Window, previousPage func(), nextPage func()){ currentPage := func(){setAddAttributeToCustomMatchDisplayPage(window, previousPage, nextPage)} title := getPageTitleCentered("Add Attribute") backButton := getBackButtonCentered(previousPage) description := getLabelCentered("Choose the attribute to display.") getPageContent := func()(*fyne.Container, error){ getCustomDisplayAttributesList := func()([]string, error){ exists, currentAttributesListString, err := mySettings.GetSetting("CustomMatchDisplayAttributesList") if (err != nil) { return nil, err } if (exists == false){ emptyList := make([]string, 0) return emptyList, nil } currentAttributesList := strings.Split(currentAttributesListString, ",") return currentAttributesList, nil } customDisplayAttributesList, err := getCustomDisplayAttributesList() if (err != nil){ return nil, err } if (len(customDisplayAttributesList) >= 5){ return nil, errors.New("setAddAttributeToCustomMatchDisplayPage called when attributesList is full.") } generalAttributeButtonsGrid := container.NewGridWithColumns(1) physicalAttributeButtonsGrid := container.NewGridWithColumns(1) lifestyleAttributeButtonsGrid := container.NewGridWithColumns(1) mentalAttributeButtonsGrid := container.NewGridWithColumns(1) addAttributeButton := func(attributeName string, attributeType string)error{ isSelected := slices.Contains(customDisplayAttributesList, attributeName) if (isSelected == true){ // Attribute has already been selected // We will not show it return nil } attributeTitle, _, _, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName) if (err != nil) { return err } attributeButton := widget.NewButton(attributeTitle, func(){ newAttributesList := append(customDisplayAttributesList, attributeName) newAttributesListString := strings.Join(newAttributesList, ",") err := mySettings.SetSetting("CustomMatchDisplayAttributesList", newAttributesListString) if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } nextPage() }) if (attributeType == "General"){ generalAttributeButtonsGrid.Add(attributeButton) } else if (attributeType == "Physical"){ physicalAttributeButtonsGrid.Add(attributeButton) } else if (attributeType == "Lifestyle"){ lifestyleAttributeButtonsGrid.Add(attributeButton) } else if (attributeType == "Mental"){ mentalAttributeButtonsGrid.Add(attributeButton) } else { return errors.New("addAttributeButton called with invalid attributeType: " + attributeType) } return nil } generalAttributeNamesList := []string{ "MatchScore", "Username", "Sexuality", "Distance", "HasMessagedMe", "IHaveMessaged", "HasRejectedMe", "IsLiked", "IsMyContact", "SearchTermsCount", "PrimaryLocationCountry", } physicalAttributeNamesList := []string{ "Age", "Height", "Sex", "HairColor", "HairTexture", "EyeColor", "SkinColor", "RacialSimilarity", "HasHIV", "HasGenitalHerpes", "BodyFat", "BodyMuscle", "23andMe_MaternalHaplogroup", "23andMe_PaternalHaplogroup", "23andMe_NeanderthalVariants", "EyeColorSimilarity", "EyeColorGenesSimilarity", "HairColorSimilarity", "HairColorGenesSimilarity", "SkinColorSimilarity", "SkinColorGenesSimilarity", "HairTextureSimilarity", "HairTextureGenesSimilarity", "FacialStructureGenesSimilarity", "23andMe_AncestralSimilarity", "23andMe_MaternalHaplogroupSimilarity", "23andMe_PaternalHaplogroupSimilarity", "OffspringProbabilityOfAnyMonogenicDisease", "TotalPolygenicDiseaseRiskScore", "OffspringTotalPolygenicDiseaseRiskScore", "AutismRiskScore", "OffspringAutismRiskScore", "ObesityRiskScore", "OffspringObesityRiskScore", "PredictedEyeColor", "OffspringBlueEyesProbability", "OffspringGreenEyesProbability", "OffspringHazelEyesProbability", "OffspringBrownEyesProbability", "PredictedLactoseTolerance", "OffspringLactoseToleranceProbability", "PredictedHairTexture", "OffspringStraightHairProbability", "OffspringCurlyHairProbability", "HomosexualnessScore", "OffspringHomosexualnessScore", "PredictedHeight", "OffspringPredictedHeight", } lifestyleAttributeNamesList := []string{ "Fame", "WealthInGold", "AlcoholFrequency", "TobaccoFrequency", "CannabisFrequency", "FruitRating", "VegetablesRating", "NutsRating", "GrainsRating", "DairyRating", "SeafoodRating", "BeefRating", "PorkRating", "PoultryRating", "EggsRating", "BeansRating", } mentalAttributeNamesList := []string{ "GenderIdentity", "PetsRating", "DogsRating", "CatsRating", } for _, attributeName := range generalAttributeNamesList{ err = addAttributeButton(attributeName, "General") if (err != nil) { return nil, err } } for _, attributeName := range physicalAttributeNamesList{ err = addAttributeButton(attributeName, "Physical") if (err != nil) { return nil, err } } for _, attributeName := range lifestyleAttributeNamesList{ err = addAttributeButton(attributeName, "Lifestyle") if (err != nil) { return nil, err } } for _, attributeName := range mentalAttributeNamesList{ err = addAttributeButton(attributeName, "Mental") if (err != nil) { return nil, err } } generalLabel := getBoldLabelCentered("General") physicalLabel := getBoldLabelCentered("Physical") lifestyleLabel := getBoldLabelCentered("Lifestyle") mentalLabel := getBoldLabelCentered("Mental") generalAttributeButtonsGridCentered := getContainerCentered(generalAttributeButtonsGrid) physicalAttributeButtonsGridCentered := getContainerCentered(physicalAttributeButtonsGrid) lifestyleAttributeButtonsGridCentered := getContainerCentered(lifestyleAttributeButtonsGrid) mentalAttributeButtonsGridCentered := getContainerCentered(mentalAttributeButtonsGrid) pageContent := container.NewVBox(generalLabel, widget.NewSeparator(), generalAttributeButtonsGridCentered, widget.NewSeparator(), physicalLabel, widget.NewSeparator(), physicalAttributeButtonsGridCentered, widget.NewSeparator(), lifestyleLabel, widget.NewSeparator(), lifestyleAttributeButtonsGridCentered, widget.NewSeparator(), mentalLabel, widget.NewSeparator(), mentalAttributeButtonsGridCentered) return pageContent, nil } pageContent, err := getPageContent() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), pageContent) setPageContent(page, window) } func setConfigureMatchScorePage(window fyne.Window, previousPage func()){ setLoadingScreen(window, "Configure Match Score", "Loading Configure Match Score page...") currentPage := func(){setConfigureMatchScorePage(window, previousPage)} title := getPageTitleCentered("Configure Match Score") backButton := getBackButtonCentered(previousPage) description1 := getLabelCentered("Customize how a user's match score is calculated.") description2 := getLabelCentered("Each desire's points will be added to a user's match score if they fulfills the desire.") description3 := getLabelCentered("Increase the Point value for desires that are more important to you.") viewMatchScoreDistributionButton := getWidgetCentered(widget.NewButtonWithIcon("View Match Score Distribution", theme.VisibilityIcon(), func(){ setViewUserAttributeStatisticsPage_BarChart(window, "Mate", "MatchScore", "Number Of Users", " users", true, false, false, nil, false, nil, nil, currentPage) })) //TODO: Split into multiple pages and add Navigation buttons getDesirePointsGrid := func()(*fyne.Container, error){ desireNameLabel := getItalicLabelCentered("Desire Name") pointsLabel := getItalicLabelCentered("Points") emptyLabel := widget.NewLabel("") desireTitleColumn := container.NewVBox(desireNameLabel, widget.NewSeparator()) desirePointsColumn := container.NewVBox(pointsLabel, widget.NewSeparator()) editPointsButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator()) allMyDesiresList := myMateDesires.GetAllMyDesiresList(false) for _, desireName := range allMyDesiresList{ desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName) if (err != nil) { return nil, err } desireTitleTranslated := translate(desireTitle) desireTitleLabel := getBoldLabelCentered(desireTitleTranslated) desirePoints, err := myMatchScore.GetMyMatchScoreDesirePoints(desireName) if (err != nil) { return nil, err } desirePointsString := helpers.ConvertIntToString(desirePoints) desirePointsLabel := getBoldLabelCentered(desirePointsString) editPointsButton := widget.NewButtonWithIcon("", theme.DocumentCreateIcon(), func(){ setEditMatchScoreDesirePointsPage(window, desireName, currentPage) }) desireTitleColumn.Add(desireTitleLabel) desirePointsColumn.Add(desirePointsLabel) editPointsButtonsColumn.Add(editPointsButton) desireTitleColumn.Add(widget.NewSeparator()) desirePointsColumn.Add(widget.NewSeparator()) editPointsButtonsColumn.Add(widget.NewSeparator()) } desirePointsGrid := container.NewHBox(layout.NewSpacer(), desireTitleColumn, desirePointsColumn, editPointsButtonsColumn, layout.NewSpacer()) return desirePointsGrid, nil } desirePointsGrid, err := getDesirePointsGrid() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), viewMatchScoreDistributionButton, widget.NewSeparator(), desirePointsGrid) setPageContent(page, window) } func setEditMatchScoreDesirePointsPage(window fyne.Window, desireName string, previousPage func()){ currentPage := func(){setEditMatchScoreDesirePointsPage(window, desireName, previousPage)} title := getPageTitleCentered("Edit Attribute Points") backButton := getBackButtonCentered(previousPage) description1 := getLabelCentered("Edit your desire's points.") description2 := getLabelCentered("These will be added to a user's match score if they fulfill the desire.") desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } desireNameLabel := widget.NewLabel("Desire Name:") desireTitleLabel := getBoldLabel(desireTitle) desireTitleRow := container.NewHBox(layout.NewSpacer(), desireNameLabel, desireTitleLabel, layout.NewSpacer()) desirePoints, err := myMatchScore.GetMyMatchScoreDesirePoints(desireName) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } desirePointsString := helpers.ConvertIntToString(desirePoints) desirePointsTitle := getBoldLabelCentered("Desire Points:") desirePointsLabel := getBoldLabelCentered(desirePointsString) getDecreasePointsButton := func()fyne.Widget{ if (desirePoints > 1){ decreaseButton := widget.NewButton("-", func(){ err := myMatchScore.SetMyMatchScoreDesirePoints(desireName, desirePoints-1) if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) return decreaseButton } emptyButton := widget.NewButton("", nil) return emptyButton } getIncreasePointsButton := func()fyne.Widget{ if (desirePoints < 100){ increaseButton := widget.NewButton("+", func(){ err := myMatchScore.SetMyMatchScoreDesirePoints(desireName, desirePoints+1) if (err != nil) { setErrorEncounteredPage(window, err, currentPage) return } currentPage() }) return increaseButton } emptyButton := widget.NewButton("", nil) return emptyButton } decreasePointsButton := getDecreasePointsButton() increasePointsButton := getIncreasePointsButton() decreaseIncreaseButtons := getContainerCentered(container.NewGridWithColumns(2, decreasePointsButton, increasePointsButton)) page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), desireTitleRow, widget.NewSeparator(), desirePointsTitle, desirePointsLabel, decreaseIncreaseButtons) setPageContent(page, window) } // This page will explain a user's match score by describing which desires the user passes func setViewUserMatchScoreBreakdownPage(window fyne.Window, userIdentityHash [16]byte, previousPage func()){ userIdentityType, err := identity.GetIdentityTypeFromIdentityHash(userIdentityHash) if (err != nil){ userIdentityHashHex := encoding.EncodeBytesToHexString(userIdentityHash[:]) setErrorEncounteredPage(window, errors.New("setViewUserMatchScoreBreakdownPage called with invalid userIdentityHash: " + userIdentityHashHex), previousPage) return } if (userIdentityType != "Mate"){ setErrorEncounteredPage(window, errors.New("setViewUserMatchScoreBreakdownPage called with non-mate identity hash."), previousPage) return } currentPage := func(){setViewUserMatchScoreBreakdownPage(window, userIdentityHash, previousPage)} title := getPageTitleCentered("User Match Score Breakdown") backButton := getBackButtonCentered(previousPage) appNetworkType, err := getAppNetworkType.GetAppNetworkType() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } profileExists, _, getAnyUserProfileAttributeFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(userIdentityHash, appNetworkType, true, false, true) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } if (profileExists == false) { // Profile must have been deleted or it is not viewable description1 := getBoldLabelCentered("User's profile is not found.") description2 := getLabelCentered("Thus, the user's match score breakdown cannot be shown.") description3 := getLabelCentered("Their profile was either banned or deleted.") description4 := getLabelCentered("Deletion should only happen if the user is no longer a match.") page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4) setPageContent(page, window) return } exists, _, userIsDisabled, err := getAnyUserProfileAttributeFunction("Disabled") if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } if (exists == true && userIsDisabled == "Yes"){ // User's newest viewable profile is disabled. description1 := getBoldLabelCentered("User's profile is disabled.") description2 := getLabelCentered("Thus, the user's match score breakdown cannot be shown.") page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2) setPageContent(page, window) return } // This bool will track if we have any desires at all anyPreferenceExists := false userMatchScore := 0 // This map stores the number of points we added for each desire // Map Structure: Desire name -> Points we added desirePointsMap := make(map[string]int) // This is a list of desires which the user fulfills fulfilledDesiresList := make([]string, 0) // This is a list of desires which the user does not fulfill unfulfilledDesiresList := make([]string, 0) // This is a list of desires for which we do not know if the user fulfills our preference, not caused by them not responding unknownStatusDesiresList := make([]string, 0) // This is a list of desires for which the user has no response noResponseExistsDesiresList := make([]string, 0) // This is a list of desires which we have no preference for noPreferenceExistsDesiresList := make([]string, 0) allMyDesiresList := myMateDesires.GetAllMyDesiresList(false) for _, desireName := range allMyDesiresList{ myDesireExists, statusIsKnown, userFulfillsDesire, err := myMateDesires.CheckIfMateProfileFulfillsMyDesire(desireName, getAnyUserProfileAttributeFunction) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } if (myDesireExists == false){ noPreferenceExistsDesiresList = append(noPreferenceExistsDesiresList, desireName) continue } anyPreferenceExists = true if (statusIsKnown == false){ noResponseIsPossible, _ := mateDesires.CheckIfDesireAllowsRequireResponse(desireName) if (noResponseIsPossible == false){ // Whether or not the user responded does not effect the desire's status is known status // An example is OffspringProbabilityOfAnyMonogenicDisease, which requires information from us *and* the user unknownStatusDesiresList = append(unknownStatusDesiresList, desireName) continue } noResponseExistsDesiresList = append(noResponseExistsDesiresList, desireName) continue } if (userFulfillsDesire == false){ unfulfilledDesiresList = append(unfulfilledDesiresList, desireName) continue } fulfilledDesiresList = append(fulfilledDesiresList, desireName) pointsToAdd, err := myMatchScore.GetMyMatchScoreDesirePoints(desireName) if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } desirePointsMap[desireName] += pointsToAdd userMatchScore += pointsToAdd } if (anyPreferenceExists == false){ description1 := getBoldLabelCentered("You have not added any desires.") description2 := getLabelCentered("Thus, the match score for all users will be 0.") description3 := getLabelCentered("Visit the Desires page to enter some desires.") page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3) setPageContent(page, window) return } matchScoreTitle := widget.NewLabel("User Match Score:") matchScoreString := helpers.ConvertIntToString(userMatchScore) matchScoreLabel := getBoldLabel(matchScoreString) matchScoreRow := container.NewHBox(layout.NewSpacer(), matchScoreTitle, matchScoreLabel, layout.NewSpacer()) getMatchScoreBreakdownGrid := func()(*fyne.Container, error){ desireNameHeader := getItalicLabelCentered("Desire Name") userFulfillsDesireHeader := getItalicLabelCentered("User Fulfills Desire") pointsAddedHeader := getItalicLabelCentered("Points Added") desireNameColumn := container.NewVBox(desireNameHeader, widget.NewSeparator()) userFulfillsDesireColumn := container.NewVBox(userFulfillsDesireHeader, widget.NewSeparator()) pointsAddedColumn := container.NewVBox(pointsAddedHeader, widget.NewSeparator()) for _, desireName := range fulfilledDesiresList{ desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName) if (err != nil) { return nil, err } desireTitleLabel := getBoldLabelCentered(desireTitle) userFulfillsDesireLabel := getBoldLabelCentered(translate("Yes")) pointsAdded, exists := desirePointsMap[desireName] if (exists == false){ return nil, errors.New("desirePointsMap missing desire: " + desireName) } pointsAddedString := helpers.ConvertIntToString(pointsAdded) pointsAddedLabel := getBoldLabelCentered("+" + pointsAddedString) desireNameColumn.Add(desireTitleLabel) userFulfillsDesireColumn.Add(userFulfillsDesireLabel) pointsAddedColumn.Add(pointsAddedLabel) desireNameColumn.Add(widget.NewSeparator()) userFulfillsDesireColumn.Add(widget.NewSeparator()) pointsAddedColumn.Add(widget.NewSeparator()) } for _, desireName := range unfulfilledDesiresList{ desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName) if (err != nil) { return nil, err } desireTitleLabel := getBoldLabelCentered(desireTitle) userFulfillsDesireLabel := getBoldLabelCentered(translate("No")) pointsAddedLabel := getBoldLabelCentered("0") desireNameColumn.Add(desireTitleLabel) userFulfillsDesireColumn.Add(userFulfillsDesireLabel) pointsAddedColumn.Add(pointsAddedLabel) desireNameColumn.Add(widget.NewSeparator()) userFulfillsDesireColumn.Add(widget.NewSeparator()) pointsAddedColumn.Add(widget.NewSeparator()) } for _, desireName := range unknownStatusDesiresList{ desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName) if (err != nil) { return nil, err } desireTitleLabel := getBoldLabelCentered(desireTitle) userFulfillsDesireLabel := getItalicLabelCentered(translate("Unknown")) pointsAddedLabel := getBoldLabelCentered("0") desireNameColumn.Add(desireTitleLabel) userFulfillsDesireColumn.Add(userFulfillsDesireLabel) pointsAddedColumn.Add(pointsAddedLabel) desireNameColumn.Add(widget.NewSeparator()) userFulfillsDesireColumn.Add(widget.NewSeparator()) pointsAddedColumn.Add(widget.NewSeparator()) } for _, desireName := range noResponseExistsDesiresList{ desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName) if (err != nil) { return nil, err } desireTitleLabel := getBoldLabelCentered(desireTitle) userFulfillsDesireLabel := getItalicLabelCentered(translate("No Response")) pointsAddedLabel := getBoldLabelCentered("0") desireNameColumn.Add(desireTitleLabel) userFulfillsDesireColumn.Add(userFulfillsDesireLabel) pointsAddedColumn.Add(pointsAddedLabel) desireNameColumn.Add(widget.NewSeparator()) userFulfillsDesireColumn.Add(widget.NewSeparator()) pointsAddedColumn.Add(widget.NewSeparator()) } matchScoreBreakdownGrid := container.NewHBox(layout.NewSpacer(), desireNameColumn, userFulfillsDesireColumn, pointsAddedColumn, layout.NewSpacer()) return matchScoreBreakdownGrid, nil } matchScoreBreakdownGrid, err := getMatchScoreBreakdownGrid() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } page := container.NewVBox(title, backButton, widget.NewSeparator(), matchScoreRow, widget.NewSeparator(), matchScoreBreakdownGrid) numberOfNoPreferenceDesires := len(noPreferenceExistsDesiresList) if (numberOfNoPreferenceDesires != 0){ numberOfNoPreferenceDesiresString := helpers.ConvertIntToString(numberOfNoPreferenceDesires) description1 := getLabelCentered("You have " + numberOfNoPreferenceDesiresString + " desires with no preference.") description2 := getLabelCentered("These desires will have no impact on match scores.") viewMyNoPreferenceDesiresButton := getWidgetCentered(widget.NewButtonWithIcon("View My No Preference Desires", theme.VisibilityIcon(), func(){ setViewMyNoPreferenceDesiresPage(window, noPreferenceExistsDesiresList, currentPage) })) page.Add(widget.NewSeparator()) page.Add(description1) page.Add(description2) page.Add(viewMyNoPreferenceDesiresButton) } setPageContent(page, window) } func setViewMyNoPreferenceDesiresPage(window fyne.Window, myNoPreferenceDesiresList []string, previousPage func()){ title := getPageTitleCentered("My No Preference Desires") backButton := getBackButtonCentered(previousPage) description := getLabelCentered("Below is a list of your desires for which you have no provided no preference.") page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator()) for _, desireName := range myNoPreferenceDesiresList{ desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } desireTitleLabel := getBoldLabelCentered(desireTitle) page.Add(desireTitleLabel) } setPageContent(page, window) }