438 lines
16 KiB
Go
438 lines
16 KiB
Go
|
|
package gui
|
|
|
|
// downloadGui.go implements pages to monitor manual downloads
|
|
// These are downloads whose status and progress the user is able to monitor
|
|
|
|
import "fyne.io/fyne/v2"
|
|
import "fyne.io/fyne/v2/widget"
|
|
import "fyne.io/fyne/v2/theme"
|
|
import "fyne.io/fyne/v2/container"
|
|
import "fyne.io/fyne/v2/data/binding"
|
|
|
|
import "seekia/internal/helpers"
|
|
import "seekia/internal/network/appNetworkType/getAppNetworkType"
|
|
import "seekia/internal/network/manualDownloads"
|
|
import "seekia/internal/appMemory"
|
|
|
|
import "errors"
|
|
import "time"
|
|
|
|
|
|
func setDownloadMissingUserProfilePage(window fyne.Window, profileAuthorIdentityHash [16]byte, getViewableOnly bool, stopDownloadOnPageExit bool, previousPage func(), nextPage func(), exitPage func()){
|
|
|
|
pageTitleText := "Download User Profile"
|
|
description1Text := "The user's profile is missing."
|
|
|
|
//Outputs:
|
|
// -bool: Any hosts found
|
|
// -[][23]byte: Download identifiers list
|
|
// -error
|
|
startNewDownloadFunction := func()(bool, [][23]byte, error){
|
|
|
|
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
|
|
if (err != nil) { return false, nil, err }
|
|
|
|
anyHostsFound, processIdentifier, err := manualDownloads.StartNewestUserProfileDownload(profileAuthorIdentityHash, appNetworkType, getViewableOnly, 1, 10)
|
|
if (err != nil) { return false, nil, err }
|
|
if (anyHostsFound == false){
|
|
emptyList := make([][23]byte, 0)
|
|
return false, emptyList, nil
|
|
}
|
|
|
|
processIdentifiersList := [][23]byte{processIdentifier}
|
|
|
|
return true, processIdentifiersList, nil
|
|
}
|
|
|
|
anyHostsFound, processIdentifiersList, err := startNewDownloadFunction()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPage)
|
|
return
|
|
}
|
|
|
|
noHostsFound := !anyHostsFound
|
|
|
|
setMonitorManualDownloadsPage(window, pageTitleText, "Profile", description1Text, noHostsFound, processIdentifiersList, startNewDownloadFunction, stopDownloadOnPageExit, 1, previousPage, nextPage, exitPage)
|
|
}
|
|
|
|
//Inputs:
|
|
// -fyne.Window:
|
|
// -string
|
|
// -string
|
|
// -string
|
|
// -bool: True if no hosts are found
|
|
// -[][23]byte: List of process identifiers to monitor
|
|
// -func()(bool, [][23]byte, error): Function to retry all downloads
|
|
// -bool: Any hosts found
|
|
// -[][23]byte: New process identifiers of new download
|
|
// -error
|
|
// -bool: Stop download on page exit
|
|
// -int: expectedSuccessfulDownloadsPerProcess
|
|
// -func(): The page to visit if back button is pressed.
|
|
// -func(): The page to visit after a successful download
|
|
// -func()
|
|
func setMonitorManualDownloadsPage(window fyne.Window,
|
|
pageTitleText string,
|
|
downloadType string,
|
|
description1Text string,
|
|
noHostsFound bool,
|
|
processIdentifiersList [][23]byte,
|
|
startNewDownloadFunction func()(bool, [][23]byte, error),
|
|
stopDownloadOnPageExit bool,
|
|
expectedSuccessfulDownloadsPerProcess int,
|
|
previousPage func(),
|
|
afterCompletionPage func(),
|
|
exitPage func()){
|
|
|
|
currentPage := func(){setMonitorManualDownloadsPage(window, pageTitleText, downloadType, description1Text, noHostsFound, processIdentifiersList, startNewDownloadFunction, stopDownloadOnPageExit, expectedSuccessfulDownloadsPerProcess, previousPage, afterCompletionPage, exitPage)}
|
|
|
|
title := getPageTitleCentered(pageTitleText)
|
|
|
|
previousPageWithCancel := func(){
|
|
if (stopDownloadOnPageExit == true && noHostsFound == false){
|
|
for _, processIdentifier := range processIdentifiersList{
|
|
manualDownloads.EndProcess(processIdentifier)
|
|
}
|
|
}
|
|
appMemory.DeleteMemoryEntry("CurrentViewedPage")
|
|
previousPage()
|
|
}
|
|
|
|
if (downloadType != "Profile" && downloadType != "Message"){
|
|
//TODO: Replace downloadType with custom descriptions for missing hosts, download in progress, and content may not exist
|
|
setErrorEncounteredPage(window, errors.New("setMonitorProfileDownloadPage called with invalid downloadType: " + downloadType), previousPageWithCancel)
|
|
return
|
|
}
|
|
|
|
backButton := getBackButtonCentered(previousPageWithCancel)
|
|
|
|
pageIdentifier, err := helpers.GetNewRandomHexString(16)
|
|
if (err != nil) {
|
|
setErrorEncounteredPage(window, err, previousPageWithCancel)
|
|
return
|
|
}
|
|
|
|
appMemory.SetMemoryEntry("CurrentViewedPage", pageIdentifier)
|
|
|
|
checkIfPageHasChangedFunction := func()bool{
|
|
exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage")
|
|
if (exists == true && currentViewedPage == pageIdentifier){
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
description1 := getLabelCentered(description1Text)
|
|
|
|
retryFunction := func(){
|
|
newDownloadAnyHostsFound, newProcessIdentifiersList, err := startNewDownloadFunction()
|
|
if (err != nil){
|
|
setErrorEncounteredPage(window, err, previousPageWithCancel)
|
|
return
|
|
}
|
|
|
|
newDownloadNoHostsFound := !newDownloadAnyHostsFound
|
|
|
|
setMonitorManualDownloadsPage(window, pageTitleText, downloadType, description1Text, newDownloadNoHostsFound, newProcessIdentifiersList, startNewDownloadFunction, stopDownloadOnPageExit, expectedSuccessfulDownloadsPerProcess, previousPage, afterCompletionPage, exitPage)
|
|
}
|
|
|
|
if (noHostsFound == true){
|
|
|
|
description2 := getLabelCentered(downloadType + " download failed because no available hosts were found.")
|
|
description3 := getLabelCentered("Please wait for Seekia to find more hosts.")
|
|
description4 := getLabelCentered("This should take less than 1 minute.")
|
|
|
|
retryingInSecondsBinding := binding.NewString()
|
|
|
|
startRetryCountdownFunction := func(){
|
|
|
|
secondsRemaining := 30
|
|
for {
|
|
|
|
secondsRemainingString := helpers.ConvertIntToString(secondsRemaining)
|
|
|
|
if (secondsRemaining != 1){
|
|
retryingInSecondsBinding.Set("Retrying in " + secondsRemainingString + " seconds...")
|
|
} else {
|
|
retryingInSecondsBinding.Set("Retrying in " + secondsRemainingString + " second...")
|
|
}
|
|
|
|
time.Sleep(time.Second)
|
|
secondsRemaining -= 1
|
|
|
|
if (secondsRemaining <= 0){
|
|
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
retryFunction()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
retryingInLabel := widget.NewLabelWithData(retryingInSecondsBinding)
|
|
retryingInLabel.TextStyle = getFyneTextStyle_Bold()
|
|
retryingInLabelCentered := getWidgetCentered(retryingInLabel)
|
|
|
|
retryButton := getWidgetCentered(widget.NewButtonWithIcon("Retry", theme.ViewRefreshIcon(), retryFunction))
|
|
exitButton := getWidgetCentered(widget.NewButtonWithIcon("Exit", theme.CancelIcon(), exitPage))
|
|
|
|
manageConnectionDescription := getLabelCentered("Check if your internet connection is working below.")
|
|
|
|
manageConnectionButton := getWidgetCentered(widget.NewButtonWithIcon("Manage Connection", theme.DownloadIcon(), func(){
|
|
setManageNetworkConnectionPage(window, currentPage)
|
|
}))
|
|
|
|
page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4, widget.NewSeparator(), retryingInLabelCentered, widget.NewSeparator(), retryButton, exitButton, widget.NewSeparator(), manageConnectionDescription, manageConnectionButton)
|
|
|
|
setPageContent(page, window)
|
|
|
|
go startRetryCountdownFunction()
|
|
|
|
return
|
|
}
|
|
|
|
downloadProgressStatusBinding := binding.NewString()
|
|
downloadNumHostsContactedBinding := binding.NewString()
|
|
downloadProgressDetailsBinding := binding.NewString()
|
|
|
|
updateBindingsFunction := func(){
|
|
|
|
startTime := time.Now().Unix()
|
|
|
|
setDownloadProgressStatus := func(processComplete bool, newStatus string){
|
|
|
|
getProgressEllipsis := func()string{
|
|
if (processComplete == true){
|
|
return ""
|
|
}
|
|
|
|
currentTime := time.Now().Unix()
|
|
secondsElapsed := currentTime - startTime
|
|
if (secondsElapsed % 3 == 0){
|
|
return "."
|
|
}
|
|
if (secondsElapsed % 3 == 1){
|
|
return ".."
|
|
}
|
|
return "..."
|
|
}
|
|
progressEllipsis := getProgressEllipsis()
|
|
downloadProgressStatusBinding.Set(newStatus + progressEllipsis)
|
|
}
|
|
|
|
// Map Structure: Process identifier -> Progress details
|
|
processLatestProgressDetailsMap := make(map[[23]byte]string)
|
|
// Map Structure: Process identifier -> Progress details last update time
|
|
processLatestUpdatedTimesMap := make(map[[23]byte]int64)
|
|
|
|
for {
|
|
|
|
// We use the below function to combine the stats from all processes, if multiple processes exist.
|
|
//Outputs:
|
|
// -bool: Download is complete
|
|
// -bool: Download ran out of hosts
|
|
// -int: Number of successful downloads
|
|
// -int: Number of hosts missing content
|
|
// -string: Download progress details
|
|
// -error
|
|
getDownloadInfo := func()(bool, bool, int, int, string, error){
|
|
|
|
allProcessesAreComplete := false
|
|
|
|
// This will sum all the successful downloads for all processes
|
|
numberOfSuccessfulDownloads := 0
|
|
|
|
// This will sum all of the number of hosts missing content for all processes
|
|
numberOfHostsMissingContent := 0
|
|
|
|
for _, processIdentifier := range processIdentifiersList{
|
|
|
|
processFound, processIsComplete, processEncounteredError, processError, processNumberOfSuccessfulDownloads, processNumberOfHostsMissingContent, processProgressDetails := manualDownloads.GetProcessInfo(processIdentifier)
|
|
if (processFound == false){
|
|
// This should not happen
|
|
return false, false, 0, 0, "", errors.New("Download process not found.")
|
|
}
|
|
if (processIsComplete == true && processEncounteredError == true){
|
|
return true, false, 0, 0, "", processError
|
|
}
|
|
if (processIsComplete == true && processNumberOfSuccessfulDownloads == 0){
|
|
// Process failed and ran out of hosts
|
|
return true, true, 0, 0, "", nil
|
|
}
|
|
|
|
numberOfSuccessfulDownloads += processNumberOfSuccessfulDownloads
|
|
numberOfHostsMissingContent += processNumberOfHostsMissingContent
|
|
|
|
if (processIsComplete == false){
|
|
allProcessesAreComplete = false
|
|
}
|
|
|
|
latestDetails, exists := processLatestProgressDetailsMap[processIdentifier]
|
|
if (exists == true && latestDetails == processProgressDetails){
|
|
// This process has not had a new details update
|
|
// We can skip it
|
|
continue
|
|
}
|
|
|
|
// This process has new details
|
|
|
|
processLatestProgressDetailsMap[processIdentifier] = processProgressDetails
|
|
|
|
currentTime := time.Now().Unix()
|
|
|
|
processLatestUpdatedTimesMap[processIdentifier] = currentTime
|
|
}
|
|
|
|
// Now we find the newest status to show to the user
|
|
|
|
newestProgressDetails := ""
|
|
newestDetailsUpdatedTime := int64(0)
|
|
|
|
for index, processIdentifier := range processIdentifiersList{
|
|
|
|
latestDetailsUpdateTime, exists := processLatestUpdatedTimesMap[processIdentifier]
|
|
if (exists == false){
|
|
// This should not happen
|
|
// All processes should be added to this map during our first iteration through processes details
|
|
return false, false, 0, 0, "", errors.New("processLatestUpdatedTimesMap missing process latest updated time")
|
|
}
|
|
|
|
if (index == 0 || latestDetailsUpdateTime > newestDetailsUpdatedTime){
|
|
|
|
latestProcessDetails, exists := processLatestProgressDetailsMap[processIdentifier]
|
|
if (exists == false){
|
|
// This should not happen
|
|
// All processes should be added to this map during our first iteration through processes details
|
|
return false, false, 0, 0, "", errors.New("processLatestProgressDetailsMap missing process details")
|
|
}
|
|
|
|
newestProgressDetails = latestProcessDetails
|
|
}
|
|
}
|
|
|
|
return allProcessesAreComplete, false, numberOfSuccessfulDownloads, numberOfHostsMissingContent, newestProgressDetails, nil
|
|
}
|
|
|
|
downloadIsComplete, downloadRanOutOfHosts, numberOfSuccessfulDownloads, numberOfHostsMissingContent, downloadProgressDetails, err := getDownloadInfo()
|
|
if (err != nil){
|
|
setDownloadProgressStatus(true, "ERROR: " + err.Error())
|
|
downloadProgressDetailsBinding.Set("Report this error to the Seekia developers.")
|
|
return
|
|
}
|
|
|
|
numberOfHostsContacted := numberOfSuccessfulDownloads + numberOfHostsMissingContent
|
|
numberOfHostsContactedString := helpers.ConvertIntToString(numberOfHostsContacted)
|
|
|
|
if (downloadIsComplete == true){
|
|
|
|
// Download is complete.
|
|
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
return
|
|
}
|
|
|
|
expectedSuccessfulDownloads := len(processIdentifiersList) * expectedSuccessfulDownloadsPerProcess
|
|
|
|
if (numberOfSuccessfulDownloads >= expectedSuccessfulDownloads){
|
|
// We downloaded the content we wanted. Nothing left to do.
|
|
afterCompletionPage()
|
|
return
|
|
}
|
|
|
|
// Download did not get required content.
|
|
// We will show user option to retry.
|
|
|
|
retryButton := getWidgetCentered(widget.NewButtonWithIcon("Retry", theme.ViewRefreshIcon(), retryFunction))
|
|
|
|
exitButton := getWidgetCentered(widget.NewButtonWithIcon("Exit", theme.CancelIcon(), exitPage))
|
|
|
|
checkConnectionDescription := getLabelCentered("Check if your internet connection is working below.")
|
|
|
|
manageConnectionButton := getWidgetCentered(widget.NewButtonWithIcon("Manage Connection", theme.DownloadIcon(), func(){
|
|
setManageNetworkConnectionPage(window, currentPage)
|
|
}))
|
|
|
|
if (downloadRanOutOfHosts == true){
|
|
|
|
description2 := getLabelCentered("The download was unsuccessful.")
|
|
description3 := getLabelCentered("All the hosts we contacted failed to respond.")
|
|
description4 := getLabelCentered("You can exit or wait for more hosts to be found and retry.")
|
|
|
|
page := container.NewVBox(title, widget.NewSeparator(), description1, description2, description3, description4, retryButton, exitButton, widget.NewSeparator(), checkConnectionDescription, manageConnectionButton)
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
description2 := getLabelCentered("The download was unsuccessful.")
|
|
description3 := getLabelCentered("We contacted " + numberOfHostsContactedString + " hosts.")
|
|
description4 := getLabelCentered("The " + downloadType + " may not exist on the network.")
|
|
description5 := getLabelCentered("Retry the download?")
|
|
|
|
page := container.NewVBox(title, widget.NewSeparator(), description1, description2, description3, description4, description5, retryButton, exitButton, widget.NewSeparator(), checkConnectionDescription, manageConnectionButton)
|
|
|
|
setPageContent(page, window)
|
|
return
|
|
}
|
|
|
|
// Download is not complete
|
|
|
|
numberOfSuccessfulDownloadsString := helpers.ConvertIntToString(numberOfSuccessfulDownloads)
|
|
|
|
progressProgressStatusString := "Downloaded from " + numberOfSuccessfulDownloadsString + " hosts."
|
|
setDownloadProgressStatus(downloadIsComplete, progressProgressStatusString)
|
|
downloadProgressDetailsBinding.Set(downloadProgressDetails)
|
|
|
|
downloadNumHostsContactedBinding.Set("Contacted " + numberOfHostsContactedString + " hosts.")
|
|
|
|
pageHasChanged := checkIfPageHasChangedFunction()
|
|
if (pageHasChanged == true){
|
|
|
|
if (stopDownloadOnPageExit == true){
|
|
for _, processIdentifier := range processIdentifiersList{
|
|
manualDownloads.EndProcess(processIdentifier)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
time.Sleep(200 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
description2 := getBoldLabelCentered("Seekia is attempting to download the " + downloadType)
|
|
description3 := getLabelCentered("The status of the download is displayed below.")
|
|
|
|
downloadProgressStatusLabel := widget.NewLabelWithData(downloadProgressStatusBinding)
|
|
downloadProgressStatusLabel.TextStyle = getFyneTextStyle_Bold()
|
|
downloadProgressStatusLabelCentered := getWidgetCentered(downloadProgressStatusLabel)
|
|
|
|
downloadNumHostsContactedLabel := getWidgetCentered(widget.NewLabelWithData(downloadNumHostsContactedBinding))
|
|
downloadProgressDetailsLabel := getWidgetCentered(widget.NewLabelWithData(downloadProgressDetailsBinding))
|
|
|
|
exitPageButton := getWidgetCentered(widget.NewButtonWithIcon("Exit", theme.MediaSkipNextIcon(), func(){
|
|
|
|
for _, processIdentifier := range processIdentifiersList{
|
|
|
|
manualDownloads.EndProcess(processIdentifier)
|
|
}
|
|
|
|
appMemory.DeleteMemoryEntry("CurrentViewedPage")
|
|
exitPage()
|
|
}))
|
|
|
|
page := container.NewVBox(title, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), downloadProgressStatusLabelCentered, downloadNumHostsContactedLabel, downloadProgressDetailsLabel, widget.NewSeparator(), exitPageButton)
|
|
|
|
setPageContent(page, window)
|
|
|
|
go updateBindingsFunction()
|
|
}
|
|
|
|
|
|
|
|
|