package gui // viewHostsGui.go implements a page to browse hosts import "fyne.io/fyne/v2" import "fyne.io/fyne/v2/container" import "fyne.io/fyne/v2/widget" import "fyne.io/fyne/v2/layout" import "fyne.io/fyne/v2/theme" import "fyne.io/fyne/v2/data/binding" import "seekia/internal/appMemory" import "seekia/internal/helpers" import "seekia/internal/identity" import "seekia/internal/mySettings" import "seekia/internal/network/appNetworkType/getAppNetworkType" import "seekia/internal/network/viewedHosts" import "seekia/internal/profiles/viewableProfiles" import "time" import "errors" func setViewHostsPage(window fyne.Window, previousPage func()){ appMemory.SetMemoryEntry("CurrentViewedPage", "ViewHosts") checkIfPageHasChangedFunction := func()bool{ exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage") if (exists == false || currentViewedPage != "ViewHosts"){ return true } return false } //TODO: Check if moderator/host mode is enabled // If not, many attributes cannot be displayed, and the list will be empty/outdated // Require either mode to be enabled to view hosts currentPage := func(){setViewHostsPage(window, previousPage)} title := getPageTitleCentered("Viewing Hosts") backButton := getBackButtonCentered(previousPage) filtersIcon, err := getFyneImageIcon("Desires") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } filtersButton := widget.NewButton("Filters", func(){ //TODO showUnderConstructionDialog(window) }) filtersButtonWithIcon := container.NewGridWithRows(2, filtersIcon, filtersButton) statsIcon, err := getFyneImageIcon("Stats") if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } statsButton := widget.NewButton("Stats", func(){ //TODO showUnderConstructionDialog(window) }) statsButtonWithIcon := container.NewGridWithRows(2, statsIcon, statsButton) pageButtonsRow := getContainerCentered(container.NewGridWithRows(1, filtersButtonWithIcon, statsButtonWithIcon)) currentSortByAttribute, err := viewedHosts.GetViewedHostsSortByAttribute() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } sortingByLabel := getBoldLabel(translate("Sorting By:")) getSortByAttributeTitle := func()string{ if (currentSortByAttribute == "BanAdvocates"){ result := translate("Ban Advocates") return result } result := translate(currentSortByAttribute) return result } sortByAttributeTitle := getSortByAttributeTitle() sortByAttributeButton := widget.NewButton(sortByAttributeTitle, func(){ setSelectViewedHostsSortByAttributePage(window, currentPage) }) getSortDirectionButtonWithIcon := func()(fyne.Widget, error){ currentSortDirection, err := viewedHosts.GetViewedHostsSortDirection() if (err != nil) { return nil, err } if (currentSortDirection == "Ascending"){ button := widget.NewButtonWithIcon(translate("Ascending"), theme.MoveUpIcon(), func(){ appMemory.SetMemoryEntry("StopBuildViewedHostsYesNo", "Yes") mySettings.SetSetting("ViewedHostsSortDirection", "Descending") mySettings.SetSetting("ViewedHostsSortedStatus", "No") mySettings.SetSetting("ViewedHostsViewIndex", "0") currentPage() }) return button, nil } button := widget.NewButtonWithIcon(translate("Descending"), theme.MoveDownIcon(), func(){ appMemory.SetMemoryEntry("StopBuildViewedHostsYesNo", "Yes") mySettings.SetSetting("ViewedHostsSortDirection", "Ascending") mySettings.SetSetting("ViewedHostsSortedStatus", "No") mySettings.SetSetting("ViewedHostsViewIndex", "0") currentPage() }) return button, nil } sortByDirectionButton, err := getSortDirectionButtonWithIcon() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } sortByRow := container.NewHBox(layout.NewSpacer(), sortingByLabel, sortByAttributeButton, sortByDirectionButton, layout.NewSpacer()) appNetworkType, err := getAppNetworkType.GetAppNetworkType() if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } viewedHostsReady, err := viewedHosts.GetViewedHostsAreReadyStatus(appNetworkType) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } if (viewedHostsReady == false){ progressPercentageBinding := binding.NewFloat() sortingDetailsBinding := binding.NewString() startUpdateHostsAndProgressBarFunction := func(){ err := viewedHosts.StartUpdatingViewedHosts(appNetworkType) if (err != nil) { setErrorEncounteredPage(window, err, previousPage) return } sortingDetailsBindingSet := false var encounteredError error for { pageHasChanged := checkIfPageHasChangedFunction() if (pageHasChanged == true){ appMemory.SetMemoryEntry("StopBuildViewedHostsYesNo", "Yes") return } buildEncounteredError, errorEncounteredString, buildIsStopped, hostsAreReady, currentPercentageProgress, err := viewedHosts.GetViewedHostsBuildStatus(appNetworkType) if (err != nil){ encounteredError = err break } if (buildEncounteredError == true){ encounteredError = errors.New(errorEncounteredString) break } if (buildIsStopped == true){ return } if (hostsAreReady == 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 && sortingDetailsBindingSet == false){ numberOfHosts, err := viewedHosts.GetNumberOfGeneratedViewedHosts() if (err != nil) { encounteredError = err break } if (numberOfHosts != 0){ numberOfHostsString := helpers.ConvertIntToString(numberOfHosts) sortingDetailsBinding.Set("Sorting " + numberOfHostsString + " Hosts...") } sortingDetailsBindingSet = true } time.Sleep(100 * time.Millisecond) } // This is only reached if an error is encountered during build errorToShow := errors.New("Error encountered during build of viewed hosts: " + encounteredError.Error()) setErrorEncounteredPage(window, errorToShow, previousPage) } loadingLabel := getBoldLabelCentered("Loading Hosts...") loadingBar := getWidgetCentered(widget.NewProgressBarWithData(progressPercentageBinding)) loadingDetailsLabel := widget.NewLabelWithData(sortingDetailsBinding) loadingDetailsLabel.TextStyle = getFyneTextStyle_Italic() loadingDetailsLabelCentered := getWidgetCentered(loadingDetailsLabel) page := container.NewVBox(title, backButton, widget.NewSeparator(), pageButtonsRow, widget.NewSeparator(), sortByRow, widget.NewSeparator(), loadingLabel, loadingBar, loadingDetailsLabelCentered) setPageContent(page, window) go startUpdateHostsAndProgressBarFunction() return } getResultsContainer := func()(*fyne.Container, error){ getRefreshResultsButtonText := func()(string, error){ needsRefresh, err := viewedHosts.CheckIfViewedHostsNeedsRefresh() 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("ViewedHostsGeneratedStatus", "No") mySettings.SetSetting("ViewedHostsViewIndex", "0") currentPage() })) listIsReady, currentViewedHostsList, err := viewedHosts.GetViewedHostsList(appNetworkType) if (err != nil) { return nil, err } if (listIsReady == false){ return nil, errors.New("Viewed hosts list not ready after generation completed.") } numberOfViewedHosts := len(currentViewedHostsList) if (numberOfViewedHosts == 0){ noHostsFoundLabel := getBoldLabelCentered("No Hosts Found") numberOfFilters, err := viewedHosts.GetNumberOfActiveHostFilters() if (err != nil) { return nil, err } if (numberOfFilters == 0){ noHostsFoundLabelWithRefreshButton := container.NewVBox(noHostsFoundLabel, refreshResultsButton) return noHostsFoundLabelWithRefreshButton, nil } numberOfFiltersString := helpers.ConvertIntToString(numberOfFilters) getActiveFiltersText := func()string{ if (numberOfFilters == 1){ return "active filter" } return "active filters" } activeFiltersText := getActiveFiltersText() activeFiltersLabel := getLabelCentered(numberOfFiltersString + " " + activeFiltersText) noHostsFoundWithFiltersInfo := container.NewVBox(noHostsFoundLabel, activeFiltersLabel, refreshResultsButton) return noHostsFoundWithFiltersInfo, nil } getCurrentViewIndex := func()(int, error){ exists, viewIndexString, err := mySettings.GetSetting("ViewedHostsViewIndex") if (err != nil) { return 0, err } if (exists == false){ return 0, nil } viewIndex, err := helpers.ConvertStringToInt(viewIndexString) if (err != nil){ return 0, errors.New("MySettings malformed: Invalid ViewedHostsViewIndex: " + viewIndexString) } if (viewIndex < 0) { return 0, nil } maximumViewIndex := numberOfViewedHosts-1 if (viewIndex > maximumViewIndex){ return maximumViewIndex, nil } return viewIndex, nil } viewIndex, err := getCurrentViewIndex() if (err != nil) { return nil, err } getNavigateToBeginningButton := func()fyne.Widget{ if (numberOfViewedHosts <= 5 || viewIndex == 0){ emptyButton := widget.NewButton(" ", nil) return emptyButton } goToBeginningButton := widget.NewButtonWithIcon("", theme.MediaSkipPreviousIcon(), func(){ mySettings.SetSetting("ViewedHostsViewIndex", "0") currentPage() }) return goToBeginningButton } getNavigateToEndButton := func()fyne.Widget{ emptyButton := widget.NewButton(" ", nil) if (numberOfViewedHosts <= 5){ return emptyButton } finalPageMinimumIndex := numberOfViewedHosts - 5 if (viewIndex >= finalPageMinimumIndex){ return emptyButton } goToEndButton := widget.NewButtonWithIcon("", theme.MediaSkipNextIcon(), func(){ finalPageIndexString := helpers.ConvertIntToString(finalPageMinimumIndex) mySettings.SetSetting("ViewedHostsViewIndex", finalPageIndexString) currentPage() }) return goToEndButton } getNavigateLeftButton := func()fyne.Widget{ if (numberOfViewedHosts <= 5 || viewIndex == 0){ emptyButton := widget.NewButton(" ", nil) return emptyButton } button := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func(){ newIndex := helpers.ConvertIntToString(viewIndex-5) mySettings.SetSetting("ViewedHostsViewIndex", newIndex) currentPage() }) return button } getNavigateRightButton := func()fyne.Widget{ emptyButton := widget.NewButton(" ", nil) if (numberOfViewedHosts <= 5){ return emptyButton } finalPageMinimumIndex := numberOfViewedHosts - 5 if (viewIndex >= finalPageMinimumIndex){ return emptyButton } button := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func(){ newIndex := helpers.ConvertIntToString(viewIndex+5) mySettings.SetSetting("ViewedHostsViewIndex", newIndex) currentPage() }) return button } getViewingHostsInfoRow := func()*fyne.Container{ numberOfViewedHostsString := helpers.ConvertIntToString(numberOfViewedHosts) getHostOrHostsText := func()string{ if (numberOfViewedHosts == 1){ return "Host" } return "Hosts" } hostOrHostsText := getHostOrHostsText() numberOfViewedHostsLabel := getBoldLabel("Viewing " + numberOfViewedHostsString + " " + hostOrHostsText) if (numberOfViewedHosts <= 5){ viewingHostsInfoRow := getWidgetCentered(numberOfViewedHostsLabel) return viewingHostsInfoRow } navigateToBeginningButton := getNavigateToBeginningButton() navigateToEndButton := getNavigateToEndButton() navigateLeftButton := getNavigateLeftButton() navigateRightButton := getNavigateRightButton() viewingHostsInfoRow := container.NewHBox(layout.NewSpacer(), navigateToBeginningButton, navigateLeftButton, numberOfViewedHostsLabel, navigateRightButton, navigateToEndButton, layout.NewSpacer()) return viewingHostsInfoRow } viewingHostsInfoRow := getViewingHostsInfoRow() hostResultsContainer := container.NewVBox() if (viewIndex == 0){ hostResultsContainer.Add(refreshResultsButton) hostResultsContainer.Add(widget.NewSeparator()) } emptyLabelA := widget.NewLabel("") nameLabel := getItalicLabelCentered("Name") identityHashTitle := getItalicLabelCentered("Identity Hash") featuredAttributeTitle := getItalicLabelCentered(sortByAttributeTitle) emptyLabelB := widget.NewLabel("") resultIndexColumn := container.NewVBox(emptyLabelA, widget.NewSeparator()) nameColumn := container.NewVBox(nameLabel, widget.NewSeparator()) identityHashColumn := container.NewVBox(identityHashTitle, widget.NewSeparator()) featuredAttributeColumn := container.NewVBox(featuredAttributeTitle, widget.NewSeparator()) viewButtonsColumn := container.NewVBox(emptyLabelB, widget.NewSeparator()) viewIndexOnwardsHostIdentityHashesList := currentViewedHostsList[viewIndex:] for index, hostIdentityHash := range viewIndexOnwardsHostIdentityHashesList{ resultIndex := viewIndex + index + 1 resultIndexString := helpers.ConvertIntToString(resultIndex) resultIndexBoldLabel := getBoldLabel(resultIndexString + ".") profileExists, _, getAnyHostAttributeFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(hostIdentityHash, appNetworkType, true, true, true) if (err != nil) { return nil, err } if (profileExists == false) { // Profile has expired since results were generated continue } getUserName := func()(string, error){ exists, _, username, err := getAnyHostAttributeFunction("Username") if (err != nil) { return "", err } if (exists == false) { return "Anonymous", nil } theirUsernameTrimmed, _, err := helpers.TrimAndFlattenString(username, 15) if (err != nil) { return "", err } return theirUsernameTrimmed, nil } currentTheirUsername, err := getUserName() if (err != nil) { return nil, err } userNameLabel := getBoldLabelCentered(currentTheirUsername) hostIdentityHashString, _, err := identity.EncodeIdentityHashBytesToString(hostIdentityHash) if (err != nil) { return nil, err } theirIdentityHashTrimmed, _, err := helpers.TrimAndFlattenString(hostIdentityHashString, 10) if (err != nil) { return nil, err } theirIdentityHashLabel := getBoldLabelCentered(theirIdentityHashTrimmed) getFeaturedAttributeValue := func()(string, error){ exists, _, featuredAttributeValue, err := getAnyHostAttributeFunction(currentSortByAttribute) if (err != nil) { return "", err } if (exists == false){ return "Unknown", nil } return featuredAttributeValue, nil } featuredAttributeValue, err := getFeaturedAttributeValue() if (err != nil) { return nil, err } featuredAttributeValueLabel := getBoldLabelCentered(featuredAttributeValue) viewHostButton := widget.NewButtonWithIcon("View", theme.VisibilityIcon(), func(){ setViewHostDetailsPage(window, hostIdentityHash, currentPage) }) resultIndexColumn.Add(resultIndexBoldLabel) nameColumn.Add(userNameLabel) identityHashColumn.Add(theirIdentityHashLabel) featuredAttributeColumn.Add(featuredAttributeValueLabel) viewButtonsColumn.Add(viewHostButton) resultIndexColumn.Add(widget.NewSeparator()) nameColumn.Add(widget.NewSeparator()) identityHashColumn.Add(widget.NewSeparator()) featuredAttributeColumn.Add(widget.NewSeparator()) viewButtonsColumn.Add(widget.NewSeparator()) if (index >= 4){ break } } hostResultsGrid := container.NewHBox(layout.NewSpacer(), resultIndexColumn, nameColumn, identityHashColumn, featuredAttributeColumn, viewButtonsColumn, layout.NewSpacer()) hostResultsContainer.Add(hostResultsGrid) viewedHostContainerScrollable := container.NewVScroll(hostResultsContainer) viewedHostContainerBoxed := getWidgetBoxed(viewedHostContainerScrollable) resultsContainer := container.NewBorder(viewingHostsInfoRow, nil, nil, nil, viewedHostContainerBoxed) return resultsContainer, nil } resultsContainer, err := getResultsContainer() if (err != nil){ setErrorEncounteredPage(window, err, previousPage) return } header := container.NewVBox(title, backButton, widget.NewSeparator(), pageButtonsRow, widget.NewSeparator(), sortByRow, widget.NewSeparator()) page := container.NewBorder(header, nil, nil, nil, resultsContainer) setPageContent(page, window) } func setSelectViewedHostsSortByAttributePage(window fyne.Window, previousPage func()){ appMemory.SetMemoryEntry("CurrentViewedPage", "ViewedHostsSortBySelect") title := getPageTitleCentered("Select Sort By Attribute") backButton := getBackButtonCentered(previousPage) description := getLabelCentered("Select the attribute to sort the hosts by.") getSelectButton := func(attributeTitle string, attributeName string, sortDirection string) fyne.Widget{ button := widget.NewButton(attributeTitle, func(){ mySettings.SetSetting("ViewedHostsSortedStatus", "No") mySettings.SetSetting("ViewedHostsSortByAttribute", attributeName) mySettings.SetSetting("ViewedHostsSortDirection", sortDirection) mySettings.SetSetting("ViewedHostsViewIndex", "0") previousPage() }) return button } //TODO: Add more attributes banAdvocatesButton := getSelectButton("Ban Advocates", "BanAdvocates", "Ascending") buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, banAdvocatesButton)) content := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), buttonsGrid) setPageContent(content, window) }