⛏️ index : haiku.git

author Andrew Lindesay <apl@lindesay.co.nz> 2025-10-26 0:24:40.0 +13:00:00
committer Andrew Lindesay <apl@lindesay.co.nz> 2025-11-07 9:40:43.0 +00:00:00
commit
a05bcfe24e6e1c8ea6b3d8600ae1ab8dc4bde32c [patch]
tree
1dd41d58e691e9336b8cdb545740ca372aa328e4
parent
9003873f8b07b64778f8135c7a77ddf2441301af
download
a05bcfe24e6e1c8ea6b3d8600ae1ab8dc4bde32c.tar.gz

HaikuDepot: Fixes for pkg view and network

This change addresses a number of issues around the synchronization
of data from the server when the user is viewing a package.
Handling of issues when the server is not network accessible is
improved. The user is also given the option is disable network
communications as problems are reported to them.

Resolves #19802

Change-Id: Iaa8214ba279a6bbf0f196643468d0f86a23ead1c
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9714
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>

Diff

 src/apps/haikudepot/packagemodel/PackageLocalInfo.cpp                  |  2 +-
 src/apps/haikudepot/packagemodel/PackageLocalizedText.cpp              |  4 ----
 src/apps/haikudepot/server/CacheScreenshotProcess.cpp                  | 11 +++++++++--
 src/apps/haikudepot/server/IncrementViewCounterProcess.cpp             |  2 +-
 src/apps/haikudepot/server/PopulatePkgChangelogFromServerProcess.cpp   | 11 +++++++++--
 src/apps/haikudepot/server/PopulatePkgUserRatingsFromServerProcess.cpp |  5 +++++
 src/apps/haikudepot/server/ServerHelper.cpp                            | 28 +++++++++++++++++++++++-----
 src/apps/haikudepot/ui/MainWindow.cpp                                  | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 src/apps/haikudepot/ui/MainWindow.h                                    |  9 +++------
 src/apps/haikudepot/ui/PackageInfoView.cpp                             | 14 +++++++++-----
 10 files changed, 114 insertions(+), 53 deletions(-)

diff --git a/src/apps/haikudepot/packagemodel/PackageLocalInfo.cpp b/src/apps/haikudepot/packagemodel/PackageLocalInfo.cpp
index 291be2c..3588532 100644
--- a/src/apps/haikudepot/packagemodel/PackageLocalInfo.cpp
+++ b/src/apps/haikudepot/packagemodel/PackageLocalInfo.cpp
@@ -333,7 +333,7 @@
PackageLocalInfoBuilder&
PackageLocalInfoBuilder::WithViewed()
{
	if (!fSource.IsSet() || fSource->Viewed()) {
	if (!fSource.IsSet() || !fSource->Viewed()) {
		_InitFromSource();
		fViewed = true;
	}
diff --git a/src/apps/haikudepot/packagemodel/PackageLocalizedText.cpp b/src/apps/haikudepot/packagemodel/PackageLocalizedText.cpp
index 66c8d4d..ae8dd0d 100644
--- a/src/apps/haikudepot/packagemodel/PackageLocalizedText.cpp
+++ b/src/apps/haikudepot/packagemodel/PackageLocalizedText.cpp
@@ -82,9 +82,6 @@
PackageLocalizedText::SetHasChangelog(bool value)
{
	fHasChangelog = value;

	if (!value)
		SetChangelog("");
}


@@ -92,7 +89,6 @@
PackageLocalizedText::SetChangelog(const BString& value)
{
	fChangelog = value;
	fHasChangelog = !value.IsEmpty();
}


diff --git a/src/apps/haikudepot/server/CacheScreenshotProcess.cpp b/src/apps/haikudepot/server/CacheScreenshotProcess.cpp
index dc363d2..a5c18b3 100644
--- a/src/apps/haikudepot/server/CacheScreenshotProcess.cpp
+++ b/src/apps/haikudepot/server/CacheScreenshotProcess.cpp
@@ -1,12 +1,14 @@
/*
 * Copyright 2023, Andrew Lindesay <apl@lindesay.co.nz>.
 * Copyright 2023-2025, Andrew Lindesay <apl@lindesay.co.nz>.
 * All rights reserved. Distributed under the terms of the MIT License.
 */
#include "CacheScreenshotProcess.h"

#include <Catalog.h>

#include "Logger.h"
#include "Model.h"
#include "ServerHelper.h"


#undef B_TRANSLATION_CONTEXT
@@ -44,5 +46,10 @@
status_t
CacheScreenshotProcess::RunInternal()
{
	if (!ServerHelper::IsNetworkAvailable()) {
		HDINFO("no network so will not cache screenshot");
		return B_OK;
	}

	return fModel->GetPackageScreenshotRepository()->CacheScreenshot(fScreenshotCoordinate);
}
}
diff --git a/src/apps/haikudepot/server/IncrementViewCounterProcess.cpp b/src/apps/haikudepot/server/IncrementViewCounterProcess.cpp
index 2b6cef8..155a342 100644
--- a/src/apps/haikudepot/server/IncrementViewCounterProcess.cpp
+++ b/src/apps/haikudepot/server/IncrementViewCounterProcess.cpp
@@ -75,7 +75,7 @@
	int32 attempts = ATTEMPTS;
	status_t result = B_OK;

	while (attempts > 0 && !WasStopped()) {
	while (attempts > 0 && !WasStopped() && ServerHelper::IsNetworkAvailable()) {
		BMessage resultEnvelope;
		WebAppInterfaceRef webApp = fModel->WebApp();
		result = webApp->IncrementViewCounter(fPackage, depot, resultEnvelope);
diff --git a/src/apps/haikudepot/server/PopulatePkgChangelogFromServerProcess.cpp b/src/apps/haikudepot/server/PopulatePkgChangelogFromServerProcess.cpp
index 3cb0479..82c5d13 100644
--- a/src/apps/haikudepot/server/PopulatePkgChangelogFromServerProcess.cpp
+++ b/src/apps/haikudepot/server/PopulatePkgChangelogFromServerProcess.cpp
@@ -51,6 +51,11 @@
status_t
PopulatePkgChangelogFromServerProcess::RunInternal()
{
	if (!ServerHelper::IsNetworkAvailable()) {
		HDINFO("no network so will not populate changelog");
		return B_OK;
	}

	// TODO; use API spec to code generation techniques instead of this manually written client.

	BMessage responsePayload;
@@ -66,8 +71,10 @@

		if (result == B_OK) {
			if (resultMessage.FindString("content", &content) == B_OK) {
				result = _UpdateChangelog(content.Trim());
				HDDEBUG("changelog populated for [%s]", fPackageName.String());
				content.Trim();
				result = _UpdateChangelog(content);
				HDDEBUG("changelog populated for [%s] (length %" B_PRIi32 ")",
					fPackageName.String(), content.Length());
			} else {
				HDDEBUG("no changelog present for [%s]", fPackageName.String());
			}
diff --git a/src/apps/haikudepot/server/PopulatePkgUserRatingsFromServerProcess.cpp b/src/apps/haikudepot/server/PopulatePkgUserRatingsFromServerProcess.cpp
index 25e110a..b528cf1 100644
--- a/src/apps/haikudepot/server/PopulatePkgUserRatingsFromServerProcess.cpp
+++ b/src/apps/haikudepot/server/PopulatePkgUserRatingsFromServerProcess.cpp
@@ -53,6 +53,11 @@
status_t
PopulatePkgUserRatingsFromServerProcess::RunInternal()
{
	if (!ServerHelper::IsNetworkAvailable()) {
		HDINFO("no network so will not populate user ratings");
		return B_OK;
	}

	// TODO; use API spec to code generation techniques instead of this manually written client.

	status_t status = B_OK;
diff --git a/src/apps/haikudepot/server/ServerHelper.cpp b/src/apps/haikudepot/server/ServerHelper.cpp
index 203d833..59c1472 100644
--- a/src/apps/haikudepot/server/ServerHelper.cpp
+++ b/src/apps/haikudepot/server/ServerHelper.cpp
@@ -1,5 +1,5 @@
/*
 * Copyright 2017-2021, Andrew Lindesay <apl@lindesay.co.nz>.
 * Copyright 2017-2025, Andrew Lindesay <apl@lindesay.co.nz>.
 * All rights reserved. Distributed under the terms of the MIT License.
 */

@@ -116,6 +116,12 @@
/*static*/ void
ServerHelper::AlertTransportError(BMessage* message)
{
	if (ServerSettings::ForceNoNetwork()) {
		HDERROR("Even though the user has opted to force no networking, a network transport error"
				" is being alerted.");
		return;
	}

	status_t error = B_OK;
	int64 errnoInt64;
	message->FindInt64("errno", &errnoInt64);
@@ -133,17 +139,23 @@
			break;
	}

	alertText.SetToFormat(B_TRANSLATE("A network transport error has arisen"
		" communicating with the server system: %s"),
	alertText.SetToFormat(B_TRANSLATE("A network transport error has arisen communicating with the"
									  " server system: %s"),
		errorDescription.String());

	BAlert* alert = new BAlert(
		B_TRANSLATE("Network transport error"),
		alertText,
		B_TRANSLATE("OK"));
	BAlert* alert = new BAlert(B_TRANSLATE("Network transport error"), alertText,
		B_TRANSLATE("Stop network use"), B_TRANSLATE("OK"));

	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
	alert->Go();

	switch (alert->Go()) {
		case 0:
			HDINFO("user has opted to disable use of networking");
			ServerSettings::SetForceNoNetwork(true);
			break;
		default:
			break;
	}
}


diff --git a/src/apps/haikudepot/ui/MainWindow.cpp b/src/apps/haikudepot/ui/MainWindow.cpp
index 8d62412..45e7f44 100644
--- a/src/apps/haikudepot/ui/MainWindow.cpp
+++ b/src/apps/haikudepot/ui/MainWindow.cpp
@@ -52,6 +52,7 @@
#include "RatePackageWindow.h"
#include "RatingUtils.h"
#include "ScreenshotWindow.h"
#include "ServerHelper.h"
#include "SettingsWindow.h"
#include "ShuttingDownWindow.h"
#include "ToLatestUserUsageConditionsWindow.h"
@@ -1125,8 +1126,10 @@
	if (message->FindString("name", &name) == B_OK) {
		const PackageInfoRef& viewedPackage = fPackageInfoView->Package();
		if (viewedPackage.IsSet()) {
			if (viewedPackage->Name() == name)
				_IncrementViewCounter(viewedPackage);
			const BString& viewedPackageName = viewedPackage->Name();

			if (viewedPackageName == name)
				_IncrementViewCounter(viewedPackageName);
			else
				HDINFO("incr. view counter; name mismatch");
		} else {
@@ -1142,23 +1145,38 @@


void
MainWindow::_IncrementViewCounter(const PackageInfoRef package)
MainWindow::_IncrementViewCounter(const BString& packageName)
{
	bool shouldIncrementViewCounter = false;
	const char* packageNameStr = packageName.String();
	const PackageInfoRef package = fModel.PackageForName(packageName);

	bool canShareAnonymousUsageData = fModel.CanShareAnonymousUsageData();
	if (canShareAnonymousUsageData && !PackageUtils::Viewed(package)) {
		fModel.AddPackage(PackageInfoBuilder(package)
				.WithLocalInfo(
					PackageLocalInfoBuilder(package->LocalInfo()).WithViewed().BuildRef())
				.BuildRef());
		shouldIncrementViewCounter = true;
	if (!package.IsSet()) {
		HDERROR("the package [%s] it not available -- won't increment the view counter",
			packageNameStr);
		return;
	}

	if (shouldIncrementViewCounter) {
		ProcessCoordinator* incrementViewCoordinator
			= ProcessCoordinatorFactory::CreateIncrementViewCounter(&fModel, package);
		_AddProcessCoordinator(incrementViewCoordinator);
	bool canShareAnonymousUsageData = fModel.CanShareAnonymousUsageData();

	// The logic here checks to see that the user has approved that their viewing of the package
	// can be anonymously recorded as a metric. We also check that within this session, the
	// package has not already been incremented as we count a viewing of a package within a
	// session of HaikuDepot desktop application as unique.

	if (canShareAnonymousUsageData && !PackageUtils::Viewed(package)) {
		if (ServerHelper::IsNetworkAvailable()) {
			PackageLocalInfoRef localInfoRef
				= PackageLocalInfoBuilder(package->LocalInfo()).WithViewed().BuildRef();
			fModel.AddPackage(PackageInfoBuilder(package).WithLocalInfo(localInfoRef).BuildRef());

			ProcessCoordinator* incrementViewCoordinator
				= ProcessCoordinatorFactory::CreateIncrementViewCounter(&fModel, package);
			_AddProcessCoordinator(incrementViewCoordinator);

			HDINFO("pkg [%s] will increment counter", packageNameStr);
		} else {
			HDINFO("pkg [%s] won't increment counter; network unavailable", packageNameStr);
		}
	}
}

@@ -1364,21 +1382,36 @@
	const char* packageNameStr = package->Name().String();

	PackageLocalizedTextRef localized = package->LocalizedText();
	bool networkAvailable = ServerHelper::IsNetworkAvailable();

	if (localized.IsSet()) {
		if (localized->HasChangelog() && (forcePopulate || localized->Changelog().IsEmpty())) {
			_AddProcessCoordinator(ProcessCoordinatorFactory::PopulatePkgChangelogCoordinator(
				&fModel, package->Name()));
			HDINFO("pkg [%s] will have changelog updated from server.", packageNameStr);
		} else {
			HDDEBUG("pkg [%s] not have changelog updated from server.", packageNameStr);
		if (forcePopulate || localized->Changelog().IsEmpty()) {
			if (localized->HasChangelog()) {
				if (networkAvailable) {
					_AddProcessCoordinator(
						ProcessCoordinatorFactory::PopulatePkgChangelogCoordinator(&fModel,
							package->Name()));
					HDINFO("pkg [%s] will have changelog updated from server.", packageNameStr);
				} else {
					HDINFO(
						"pkg [%s] will not have changelog updated from server; network unavailable",
						packageNameStr);
				}
			} else {
				HDINFO("pkg [%s] does not have a changelog -- won't try fetch it.", packageNameStr);
			}
		}
	}

	if (forcePopulate || RatingUtils::ShouldTryPopulateUserRatings(package->UserRatingInfo())) {
		_AddProcessCoordinator(
			ProcessCoordinatorFactory::PopulatePkgUserRatingsCoordinator(&fModel, package->Name()));
		HDINFO("pkg [%s] will have user ratings updated from server.", packageNameStr);
		if (networkAvailable) {
			_AddProcessCoordinator(ProcessCoordinatorFactory::PopulatePkgUserRatingsCoordinator(
				&fModel, package->Name()));
			HDINFO("pkg [%s] will have user ratings updated from server.", packageNameStr);
		} else {
			HDINFO("pkg [%s] won't have user ratings updated from server; network unavailable",
				packageNameStr);
		}
	} else {
		HDDEBUG("pkg [%s] not have user ratings updated from server.", packageNameStr);
	}
diff --git a/src/apps/haikudepot/ui/MainWindow.h b/src/apps/haikudepot/ui/MainWindow.h
index 97912f2..43c3af8 100644
--- a/src/apps/haikudepot/ui/MainWindow.h
+++ b/src/apps/haikudepot/ui/MainWindow.h
@@ -100,12 +100,9 @@
			void				_AdoptPackage(const PackageInfoRef& package);
			void				_ClearPackage();

			void				_SetupDelayedIncrementViewCounter(
									const PackageInfoRef package);
			void				_HandleIncrementViewCounter(
									const BMessage* message);
			void				_IncrementViewCounter(
									const PackageInfoRef package);
			void				_SetupDelayedIncrementViewCounter(const PackageInfoRef package);
			void				_HandleIncrementViewCounter(const BMessage* message);
			void				_IncrementViewCounter(const BString& packageName);

			void				_PopulatePackageAsync(bool forcePopulate);
			void				_StartBulkLoad(bool force = false);
diff --git a/src/apps/haikudepot/ui/PackageInfoView.cpp b/src/apps/haikudepot/ui/PackageInfoView.cpp
index 31dadb1..9c46b46 100644
--- a/src/apps/haikudepot/ui/PackageInfoView.cpp
+++ b/src/apps/haikudepot/ui/PackageInfoView.cpp
@@ -48,6 +48,7 @@
#include "ProcessCoordinatorFactory.h"
#include "RatingView.h"
#include "ScrollableGroupView.h"
#include "ServerHelper.h"
#include "SharedIcons.h"
#include "TextView.h"

@@ -1473,28 +1474,31 @@
PackageInfoView::_SetPackageScreenshotThumb(const PackageInfoRef& package)
{
	ScreenshotCoordinate desiredCoordinate = _ScreenshotThumbCoordinate(package);
	const char* packageNameCStr = package->Name().String();
	bool hasCachedBitmap = false;

	if (desiredCoordinate.IsValid()) {
		bool present = false;

		if (fModel->GetPackageScreenshotRepository()->HasCachedScreenshot(desiredCoordinate,
				&present)
			!= B_OK) {
			HDERROR("unable to ascertain if screenshot is present for pkg [%s]",
				package->Name().String());
			HDERROR("unable to ascertain if screenshot is present for pkg [%s]", packageNameCStr);
		} else if (present) {
			HDDEBUG("screenshot is already cached for [%s] -- will load it",
			HDDEBUG("screenshot is already cached for [%s] -- will load the cached data",
				package->Name().String());
			_HandleScreenshotCached(package, desiredCoordinate);
			hasCachedBitmap = true;
		} else if (!ServerHelper::IsNetworkAvailable()) {
			HDINFO("screenshot won't be cached [%s] -- network unavailable", packageNameCStr);
		} else {
			HDDEBUG("screenshot is not cached [%s] -- will cache it", package->Name().String());
			HDDEBUG("screenshot is not cached [%s] -- will cache it", packageNameCStr);
			ProcessCoordinator* processCoordinator
				= ProcessCoordinatorFactory::CacheScreenshotCoordinator(fModel, desiredCoordinate);
			fProcessCoordinatorConsumer->Consume(processCoordinator);
		}
	} else {
		HDDEBUG("no screenshot for pkg [%s]", package->Name().String());
		HDDEBUG("no screenshot for pkg [%s]", packageNameCStr);
	}

	if (!hasCachedBitmap)