hrev51996 adds 1 changeset to branch 'master'
old head: 466255e3248ba781d11dc3de024fc938a111e76d
new head: a9edb9bffa2100903fc7d6e2a42cedf1131f0172
overview:
https://git.haiku-os.org/haiku/log/?qt=range&q=a9edb9bffa21+%5E466255e3248b
----------------------------------------------------------------------------
a9edb9bffa21: HaikuDepot: Multiple improvements for user-ratings
* Display of the user-ratings listing improved
* When a user-rating is created / edited, the pkg is updated
* Creation date of the user-rating is unpacked shown
* Ability to create a user-rating with a comment, but no numerical rating
* Stars display show grey if no numerical rating present
* Improvements to error reporting when problem arise
* Parsing of the 'revision' field of the version working
* Removed debug logging for the text engine
* Other minor tweaks
Change-Id: I99f881ab1426641ef4177eec2d3bcacc7cb74e95
[ Andrew Lindesay <apl@xxxxxxxxxxxxxx> ]
----------------------------------------------------------------------------
Revision: hrev51996
Commit: a9edb9bffa2100903fc7d6e2a42cedf1131f0172
URL: https://git.haiku-os.org/haiku/commit/?id=a9edb9bffa21
Author: Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date: Tue Jun 5 18:26:08 2018 UTC
Committer: waddlesplash <waddlesplash@xxxxxxxxx>
Commit-Date: Sat Jun 9 17:07:03 2018 UTC
----------------------------------------------------------------------------
24 files changed, 921 insertions(+), 392 deletions(-)
data/artwork/icons/HaikuDepot_StarBlue | Bin 0 -> 1552 bytes
data/artwork/icons/HaikuDepot_StarGray | Bin 0 -> 1552 bytes
src/apps/haikudepot/HaikuDepot.rdef | 27 +-
src/apps/haikudepot/HaikuDepotConstants.h | 26 +-
src/apps/haikudepot/model/Model.cpp | 60 +++-
src/apps/haikudepot/model/PackageInfo.cpp | 18 +-
src/apps/haikudepot/model/PackageInfo.h | 9 +-
.../haikudepot/server/PkgDataUpdateProcess.cpp | 13 +-
src/apps/haikudepot/server/ServerHelper.cpp | 115 ++++++
src/apps/haikudepot/server/ServerHelper.h | 12 +-
src/apps/haikudepot/server/WebAppInterface.cpp | 273 ++++++++++----
src/apps/haikudepot/server/WebAppInterface.h | 19 +
.../haikudepot/textview/TextDocumentLayout.cpp | 6 +-
src/apps/haikudepot/textview/TextEditor.cpp | 6 -
src/apps/haikudepot/ui/App.cpp | 12 +
src/apps/haikudepot/ui/FeaturedPackagesView.cpp | 4 +-
src/apps/haikudepot/ui/MainWindow.cpp | 103 +++++-
src/apps/haikudepot/ui/MainWindow.h | 4 +
src/apps/haikudepot/ui/PackageInfoView.cpp | 182 ++++------
src/apps/haikudepot/ui/RatePackageWindow.cpp | 355 ++++++++++++-------
src/apps/haikudepot/ui/RatePackageWindow.h | 8 +
src/apps/haikudepot/ui/ScreenshotWindow.cpp | 5 +-
src/apps/haikudepot/ui_generic/RatingView.cpp | 49 ++-
src/apps/haikudepot/ui_generic/RatingView.h | 7 +-
----------------------------------------------------------------------------
diff --git a/data/artwork/icons/HaikuDepot_StarBlue
b/data/artwork/icons/HaikuDepot_StarBlue
new file mode 100644
index 0000000000..bcb5e41bcf
Binary files /dev/null and b/data/artwork/icons/HaikuDepot_StarBlue differ
diff --git a/data/artwork/icons/HaikuDepot_StarGray
b/data/artwork/icons/HaikuDepot_StarGray
new file mode 100644
index 0000000000..8ee6cb57cf
Binary files /dev/null and b/data/artwork/icons/HaikuDepot_StarGray differ
diff --git a/src/apps/haikudepot/HaikuDepot.rdef
b/src/apps/haikudepot/HaikuDepot.rdef
index e69fcb0688..35897fec87 100644
--- a/src/apps/haikudepot/HaikuDepot.rdef
+++ b/src/apps/haikudepot/HaikuDepot.rdef
@@ -65,32 +65,23 @@ resource vector_icon {
$"010C00"
};
-resource(501, "star") #'VICN' array {
- $"6E636966010300AAFF010A0AB43438BC6638BEF3B4C9C18338C9B238C311C165"
- $"C59EC928BEF3C457B84FC928BAD5C165010A00010000"
+resource(510, "starblue") #'VICN' array {
+ $"6E636966010300AAFF010A0A40223A38213835432C5A4049545A4B435F384638"
+ $"010A00010000"
};
-resource(502, "thumbs up") #'VICN' array {
- $"6E63696601020016020000003DC000BD80000000004B30004960000056FF4A01"
- $"060DF6FFFF0358404C4E4C524CC3AF4C3E5046503A50364EBC65C5F5344C3041"
- $"3046303F383C343C3C3C3F3A3F3A3F383C323CBB103C303E2C3C2C402C403040"
- $"2E40344638C16DBB23483C50404C405440010A00010000"
+resource(520, "stargray") #'VICN' array {
+ $"6E6369660105AB010A0A40223A38213835432C5A4049545A4B435F384638010A"
+ $"00010000"
};
-resource(503, "thumbs down") #'VICN' array {
- $"6E63696601020016020000003DC000BD80000000004B30004960000056FF4A01"
- $"060DF6FFFF035440344A344E34C217343A30423036303232BACDB98A30342C3F"
- $"2C3A2C413444304438443B463B463B48384E38C46F38503A5438543C543C503C"
- $"523C4C4248BFD5C45C44444C4048405040010A00010000"
-};
-
-resource(504, "installed") #'VICN' array {
+resource(530, "installed") #'VICN' array {
$"6E636966030369D90504011704011F0202043E24C4AC24B93B24243E24B93B24"
$"C4AC3E58B93B58C4AC58583E58C4AC58B93B0A06333A3C42493051363E4F2D41"
$"010A0002000100"
};
-resource(505, "arrow left") #'VICN' array {
+resource(540, "arrow left") #'VICN' array {
$"6E6369660304006603005900020006020000003C6000C000000000004C000048"
$"A0000080FF80FF00B300010A0722353622362C482C483E363E3648030A000100"
$"1240A32D00000000000040A32444CEA044D04B01178322040A0101001240A32D"
@@ -98,7 +89,7 @@ resource(505, "arrow left") #'VICN' array {
$"000040A32442FA1242FD72"
};
-resource(506, "arrow right") #'VICN' array {
+resource(550, "arrow right") #'VICN' array {
$"6E6369660304006603005900020006020000003C6000C000000000004C000048"
$"A0000080FF80FF00B300010A0748353448343E223E222C342C3422030A000100"
$"1240A32D00000000000040A32444D19644D04B01178322040A0101001240A32D"
diff --git a/src/apps/haikudepot/HaikuDepotConstants.h
b/src/apps/haikudepot/HaikuDepotConstants.h
index 7ca466b908..a3346ef56d 100644
--- a/src/apps/haikudepot/HaikuDepotConstants.h
+++ b/src/apps/haikudepot/HaikuDepotConstants.h
@@ -2,7 +2,8 @@
* Copyright 2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
-
+#ifndef HAIKU_DEPOT_CONSTANTS_H
+#define HAIKU_DEPOT_CONSTANTS_H
enum {
MSG_MAIN_WINDOW_CLOSED = 'mwcl',
@@ -12,11 +13,32 @@ enum {
MSG_ADD_VISIBLE_PACKAGES = 'avpk',
MSG_UPDATE_SELECTED_PACKAGE = 'uspk',
MSG_CLIENT_TOO_OLD = 'oldc',
+ MSG_NETWORK_TRANSPORT_ERROR = 'nett',
+ MSG_SERVER_ERROR = 'svre',
+ MSG_SERVER_DATA_CHANGED = 'svdc',
+ MSG_DID_ADD_USER_RATING = 'adur',
+ MSG_DID_UPDATE_USER_RATING = 'upur'
};
+#define RATING_MISSING -1.0f
+#define RATING_MIN 0.0f
+
+
#define HD_ERROR_BASE (B_ERRORS_END + 1)
#define HD_NETWORK_INACCESSIBLE (HD_ERROR_BASE + 1)
#define HD_CLIENT_TOO_OLD (HD_ERROR_BASE + 2)
#define HD_ERR_NOT_MODIFIED (HD_ERROR_BASE + 3)
-#define HD_ERR_NO_DATA (HD_ERROR_BASE + 4)
\ No newline at end of file
+#define HD_ERR_NO_DATA (HD_ERROR_BASE + 4)
+
+
+// These constants reference resources in 'HaikuDepot.ref'
+enum {
+ RSRC_STAR_BLUE = 510,
+ RSRC_STAR_GREY = 520,
+ RSRC_INSTALLED = 530,
+ RSRC_ARROW_LEFT = 540,
+ RSRC_ARROW_RIGHT = 550,
+};
+
+#endif // HAIKU_DEPOT_CONSTANTS_H
\ No newline at end of file
diff --git a/src/apps/haikudepot/model/Model.cpp
b/src/apps/haikudepot/model/Model.cpp
index eb8b4ecb5a..d5803c07cc 100644
--- a/src/apps/haikudepot/model/Model.cpp
+++ b/src/apps/haikudepot/model/Model.cpp
@@ -651,6 +651,11 @@ Model::SetShowDevelopPackages(bool show)
// #pragma mark - information retrieval
+/*! Initially only superficial data is loaded from the server into the data
+ model of the packages. When the package is viewed, additional data needs
+ to be populated including ratings. This method takes care of that.
+*/
+
void
Model::PopulatePackage(const PackageInfoRef& package, uint32 flags)
{
@@ -697,7 +702,7 @@ Model::PopulatePackage(const PackageInfoRef& package,
uint32 flags)
BAutolock locker(&fLock);
package->ClearUserRatings();
- int index = 0;
+ int32 index = 0;
while (true) {
BString name;
name << index++;
@@ -706,11 +711,19 @@ Model::PopulatePackage(const PackageInfoRef& package,
uint32 flags)
if (items.FindMessage(name, &item) !=
B_OK)
break;
+ BString code;
+ if (item.FindString("code", &code) !=
B_OK) {
+ printf("corrupt user rating at
index %" B_PRIi32 "\n",
+ index);
+ continue;
+ }
+
BString user;
BMessage userInfo;
if (item.FindMessage("user", &userInfo)
!= B_OK
||
userInfo.FindString("nickname", &user) != B_OK) {
- // Ignore, we need the user name
+ printf("ignored user rating
[%s] without a user "
+ "nickname\n",
code.String());
continue;
}
@@ -723,7 +736,8 @@ Model::PopulatePackage(const PackageInfoRef& package,
uint32 flags)
if (item.FindDouble("rating", &rating)
!= B_OK)
rating = -1;
if (comment.Length() == 0 && rating ==
-1) {
- // No useful information given.
+ printf("rating [%s] has no
comment or rating so will be"
+ "ignored\n",
code.String());
continue;
}
@@ -731,11 +745,13 @@ Model::PopulatePackage(const PackageInfoRef& package,
uint32 flags)
BString major = "?";
BString minor = "?";
BString micro = "";
+ double revision = -1;
BMessage version;
if (item.FindMessage("pkgVersion",
&version) == B_OK) {
version.FindString("major",
&major);
version.FindString("minor",
&minor);
version.FindString("micro",
µ);
+ version.FindDouble("revision",
&revision);
}
BString versionString = major;
versionString << ".";
@@ -744,15 +760,43 @@ Model::PopulatePackage(const PackageInfoRef& package,
uint32 flags)
versionString << ".";
versionString << micro;
}
+ if (revision > 0) {
+ versionString << "-";
+ versionString << (int) revision;
+ }
+
+ BDateTime createTimestamp;
+ double createTimestampMillisF;
+ if (item.FindDouble("createTimestamp",
+ &createTimestampMillisF) ==
B_OK) {
+ double createTimestampSecsF =
+ createTimestampMillisF
/ 1000.0;
+ time_t createTimestampSecs =
+ (time_t)
createTimestampSecsF;
+
createTimestamp.SetTime_t(createTimestampSecs);
+ }
+
// Add the rating to the PackageInfo
- package->AddUserRating(
- UserRating(UserInfo(user),
rating,
- comment, languageCode,
versionString, 0, 0)
- );
+ UserRating userRating =
UserRating(UserInfo(user), rating,
+ comment, languageCode,
versionString, 0, 0,
+ createTimestamp);
+ package->AddUserRating(userRating);
+
+ if (Logger::IsDebugEnabled()) {
+ printf("rating [%s] retrieved
from server\n",
+ code.String());
+ }
+ }
+
+ if (Logger::IsDebugEnabled()) {
+ printf("did retrieve %" B_PRIi32 " user
ratings for [%s]\n",
+ index - 1,
packageName.String());
}
} else {
_MaybeLogJsonRpcError(info, "retrieve user
ratings");
}
+ } else {
+ printf("unable to retrieve user ratings\n");
}
}
@@ -808,7 +852,7 @@ Model::_PopulatePackageChangelog(const PackageInfoRef&
package)
}
} else {
fprintf(stdout, "unable to obtain the changelog for the package"
- "[%s]\n", packageName.String());
+ " [%s]\n", packageName.String());
}
}
diff --git a/src/apps/haikudepot/model/PackageInfo.cpp
b/src/apps/haikudepot/model/PackageInfo.cpp
index c8d18ccb28..15e803885b 100644
--- a/src/apps/haikudepot/model/PackageInfo.cpp
+++ b/src/apps/haikudepot/model/PackageInfo.cpp
@@ -86,14 +86,16 @@ UserRating::UserRating()
fLanguage(),
fPackageVersion(),
fUpVotes(0),
- fDownVotes(0)
+ fDownVotes(0),
+ fCreateTimestamp()
{
}
UserRating::UserRating(const UserInfo& userInfo, float rating,
const BString& comment, const BString& language,
- const BString& packageVersion, int32 upVotes, int32 downVotes)
+ const BString& packageVersion, int32 upVotes, int32 downVotes,
+ const BDateTime& createTimestamp)
:
fUserInfo(userInfo),
fRating(rating),
@@ -101,8 +103,10 @@ UserRating::UserRating(const UserInfo& userInfo, float
rating,
fLanguage(language),
fPackageVersion(packageVersion),
fUpVotes(upVotes),
- fDownVotes(downVotes)
+ fDownVotes(downVotes),
+ fCreateTimestamp()
{
+ fCreateTimestamp.SetTime_t(createTimestamp.Time_t());
}
@@ -114,8 +118,10 @@ UserRating::UserRating(const UserRating& other)
fLanguage(other.fLanguage),
fPackageVersion(other.fPackageVersion),
fUpVotes(other.fUpVotes),
- fDownVotes(other.fDownVotes)
+ fDownVotes(other.fDownVotes),
+ fCreateTimestamp()
{
+ fCreateTimestamp.SetTime_t(other.CreateTimestamp().Time_t());
}
@@ -129,6 +135,7 @@ UserRating::operator=(const UserRating& other)
fPackageVersion = other.fPackageVersion;
fUpVotes = other.fUpVotes;
fDownVotes = other.fDownVotes;
+ fCreateTimestamp.SetTime_t(other.fCreateTimestamp.Time_t());
return *this;
}
@@ -142,7 +149,8 @@ UserRating::operator==(const UserRating& other) const
&& fLanguage == other.fLanguage
&& fPackageVersion == other.fPackageVersion
&& fUpVotes == other.fUpVotes
- && fDownVotes == other.fDownVotes;
+ && fDownVotes == other.fDownVotes
+ && fCreateTimestamp == other.fCreateTimestamp;
}
diff --git a/src/apps/haikudepot/model/PackageInfo.h
b/src/apps/haikudepot/model/PackageInfo.h
index 0cf570c5f4..1d83dc823d 100644
--- a/src/apps/haikudepot/model/PackageInfo.h
+++ b/src/apps/haikudepot/model/PackageInfo.h
@@ -1,6 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
- * Copyright 2016-2017, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PACKAGE_INFO_H
@@ -12,6 +12,7 @@
#include <Referenceable.h>
#include <package/PackageInfo.h>
+#include "DateTime.h"
#include "List.h"
#include "PackageInfoListener.h"
#include "SharedBitmap.h"
@@ -51,7 +52,8 @@ public:
const
BString& comment,
const
BString& language,
const
BString& packageVersion,
- int32
upVotes, int32 downVotes);
+ int32
upVotes, int32 downVotes,
+ const
BDateTime& createTimestamp);
UserRating(const UserRating& other);
UserRating& operator=(const
UserRating& other);
@@ -73,6 +75,8 @@ public:
{
return fUpVotes; }
int32 DownVotes() const
{
return fDownVotes; }
+ const BDateTime& CreateTimestamp() const
+ {
return fCreateTimestamp; }
private:
UserInfo fUserInfo;
float fRating;
@@ -81,6 +85,7 @@ private:
BString fPackageVersion;
int32 fUpVotes;
int32 fDownVotes;
+ BDateTime fCreateTimestamp;
};
diff --git a/src/apps/haikudepot/server/PkgDataUpdateProcess.cpp
b/src/apps/haikudepot/server/PkgDataUpdateProcess.cpp
index be4b1ec8e9..45361c7dc1 100644
--- a/src/apps/haikudepot/server/PkgDataUpdateProcess.cpp
+++ b/src/apps/haikudepot/server/PkgDataUpdateProcess.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2017, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2017-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -22,6 +22,7 @@
#include "DumpExportPkgJsonListener.h"
#include "DumpExportPkgScreenshot.h"
#include "DumpExportPkgVersion.h"
+#include "HaikuDepotConstants.h"
/*! This package listener (not at the JSON level) is feeding in the
@@ -145,11 +146,13 @@ PackageFillingPkgListener::ConsumePackage(const
PackageInfoRef& package,
}
}
- if (!pkg->DerivedRatingIsNull()) {
- RatingSummary summary;
+ RatingSummary summary;
+ summary.averageRating = RATING_MISSING;
+
+ if (!pkg->DerivedRatingIsNull())
summary.averageRating = pkg->DerivedRating();
- package->SetRatingSummary(summary);
- }
+
+ package->SetRatingSummary(summary);
if (!pkg->ProminenceOrderingIsNull())
package->SetProminence(pkg->ProminenceOrdering());
diff --git a/src/apps/haikudepot/server/ServerHelper.cpp
b/src/apps/haikudepot/server/ServerHelper.cpp
index d9033292b7..69e00a6102 100644
--- a/src/apps/haikudepot/server/ServerHelper.cpp
+++ b/src/apps/haikudepot/server/ServerHelper.cpp
@@ -16,6 +16,7 @@
#include "HaikuDepotConstants.h"
#include "ServerSettings.h"
+#include "WebAppInterface.h"
#undef B_TRANSLATION_CONTEXT
@@ -24,6 +25,120 @@
#define KEY_HEADER_MINIMUM_VERSION "X-Desktop-Application-Minimum-Version"
+/*! This method will cause an alert to be shown to the user regarding a
+ JSON-RPC error that has been sent from the application server. It will
+ send a message to the application looper which will then relay the message
+ to the looper and then onto the user to see.
+*/
+
+void
+ServerHelper::NotifyServerJsonRpcError(BMessage& error)
+{
+ BMessage message(MSG_SERVER_ERROR);
+ message.AddMessage("error", &error);
+ be_app->PostMessage(&message);
+}
+
+
+void
+ServerHelper::AlertServerJsonRpcError(BMessage* message)
+{
+ BMessage error;
+ int32 errorCode = 0;
+
+ if (message->FindMessage("error", &error) == B_OK)
+ errorCode = WebAppInterface::ErrorCodeFromResponse(error);
+
+ BString alertText;
+
+ switch (errorCode) {
+ case ERROR_CODE_VALIDATION:
+ // TODO; expand on that message.
+ alertText = B_TRANSLATE("A validation error has
occurred");
+ break;
+ case ERROR_CODE_OBJECTNOTFOUND:
+ alertText = B_TRANSLATE("A requested object or an
object involved"
+ " in the request was not found on the server.");
+ break;
+ case ERROR_CODE_CAPTCHABADRESPONSE:
+ alertText = B_TRANSLATE("The response to the captcha
was incorrect.");
+ break;
+ case ERROR_CODE_AUTHORIZATIONFAILURE:
+ case ERROR_CODE_AUTHORIZATIONRULECONFLICT:
+ alertText = B_TRANSLATE("Authorization or security
issue");
+ break;
+ default:
+ alertText.SetToFormat(
+ B_TRANSLATE("An unexpected error has been sent
from the"
+ " HaikuDepot server [%" B_PRIi32 "]"),
errorCode);
+ break;
+ }
+
+ BAlert* alert = new BAlert(
+ B_TRANSLATE("Server Error"),
+ alertText,
+ B_TRANSLATE("OK"));
+
+ alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
+ alert->Go();
+}
+
+
+void
+ServerHelper::NotifyTransportError(status_t error)
+{
+ switch (error) {
+ case HD_CLIENT_TOO_OLD:
+ // this is handled earlier on because it
requires some
+ // information from the HTTP request to create
a sensible
+ // error message.
+ break;
+
+ default:
+ {
+ BMessage message(MSG_NETWORK_TRANSPORT_ERROR);
+ message.AddInt64("errno", (int64) error);
+ be_app->PostMessage(&message);
+ break;
+ }
+ }
+}
+
+
+void
+ServerHelper::AlertTransportError(BMessage* message)
+{
+ status_t errno = B_OK;
+ int64 errnoInt64;
+ message->FindInt64("errno", &errnoInt64);
+ errno = (status_t) errnoInt64;
+
+ BString errorDescription("?");
+ BString alertText;
+
+ switch (errno) {
+ case HD_NETWORK_INACCESSIBLE:
+ errorDescription = B_TRANSLATE("Network Error");
+ break;
+ default:
+ errorDescription.SetTo(strerror(errno));
+ break;
+ }
+
+ alertText.SetToFormat(B_TRANSLATE("A network transport error has arisen"
+ " communicating with the HaikuDepot server system: %s"),
+ errorDescription.String());
+
+ BAlert* alert = new BAlert(
+ B_TRANSLATE("Network Transport Error"),
+ alertText,
+ B_TRANSLATE("OK"));
+
+ alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
+ alert->Go();
+}
+
+
void
ServerHelper::NotifyClientTooOld(const BHttpHeaders& responseHeaders)
{
diff --git a/src/apps/haikudepot/server/ServerHelper.h
b/src/apps/haikudepot/server/ServerHelper.h
index b526cf8845..01ad48b217 100644
--- a/src/apps/haikudepot/server/ServerHelper.h
+++ b/src/apps/haikudepot/server/ServerHelper.h
@@ -7,7 +7,9 @@
#define SERVER_HELPER_H
#include <HttpHeaders.h>
-#include <Message.h>
+
+
+class BMessage;
class ServerHelper {
@@ -19,6 +21,14 @@ public:
const BHttpHeaders& responseHeaders
);
static void
AlertClientTooOld(BMessage* message);
+
+ static void
NotifyTransportError(status_t error);
+ static void
AlertTransportError(BMessage* message);
+
+ static void
NotifyServerJsonRpcError(
+
BMessage& error);
+ static void
AlertServerJsonRpcError(
+
BMessage* message);
};
#endif // SERVER_HELPER_H
diff --git a/src/apps/haikudepot/server/WebAppInterface.cpp
b/src/apps/haikudepot/server/WebAppInterface.cpp
index 3d77e79517..e0b5956828 100644
--- a/src/apps/haikudepot/server/WebAppInterface.cpp
+++ b/src/apps/haikudepot/server/WebAppInterface.cpp
@@ -10,11 +10,13 @@
#include <AppFileInfo.h>
#include <Application.h>
+#include <AutoDeleter.h>
#include <Autolock.h>
#include <File.h>
#include <HttpHeaders.h>
#include <HttpRequest.h>
#include <Json.h>
+#include <JsonTextWriter.h>
#include <Message.h>
#include <Roster.h>
#include <Url.h>
@@ -363,56 +365,138 @@ WebAppInterface::RetrieveUserRating(const BString&
packageName,
const BString &repositoryCode, const BString& username,
BMessage& message)
{
- BString jsonString = JsonBuilder()
- .AddValue("jsonrpc", "2.0")
- .AddValue("id", ++fRequestIndex)
- .AddValue("method", "getUserRatingByUserAndPkgVersion")
- .AddArray("params")
- .AddObject()
- .AddValue("userNickname", username)
- .AddValue("pkgName", packageName)
- .AddValue("pkgVersionArchitectureCode",
architecture)
- .AddValue("pkgVersionMajor", version.Major(),
true)
- .AddValue("pkgVersionMinor", version.Minor(),
true)
- .AddValue("pkgVersionMicro", version.Micro(),
true)
- .AddValue("pkgVersionPreRelease",
version.PreRelease(), true)
- .AddValue("pkgVersionRevision",
(int)version.Revision())
- .AddValue("repositoryCode", repositoryCode)
- .EndObject()
- .EndArray()
- .End();
+ // BHttpRequest later takes ownership of this.
+ BMallocIO* requestEnvelopeData = new BMallocIO();
+ BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
- return _SendJsonRequest("userrating", jsonString, 0, message);
+ requestEnvelopeWriter.WriteObjectStart();
+ _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
+ "getUserRatingByUserAndPkgVersion");
+ requestEnvelopeWriter.WriteObjectName("params");
+ requestEnvelopeWriter.WriteArrayStart();
+
+ requestEnvelopeWriter.WriteObjectStart();
+
+ requestEnvelopeWriter.WriteObjectName("userNickname");
+ requestEnvelopeWriter.WriteString(username.String());
+ requestEnvelopeWriter.WriteObjectName("pkgName");
+ requestEnvelopeWriter.WriteString(packageName.String());
+ requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode");
+ requestEnvelopeWriter.WriteString(architecture.String());
+ requestEnvelopeWriter.WriteObjectName("repositoryCode");
+ requestEnvelopeWriter.WriteString(repositoryCode.String());
+
+ if (version.Major().Length() > 0) {
+ requestEnvelopeWriter.WriteObjectName("pkgVersionMajor");
+ requestEnvelopeWriter.WriteString(version.Major().String());
+ }
+
+ if (version.Minor().Length() > 0) {
+ requestEnvelopeWriter.WriteObjectName("pkgVersionMinor");
+ requestEnvelopeWriter.WriteString(version.Minor().String());
+ }
+
+ if (version.Micro().Length() > 0) {
+ requestEnvelopeWriter.WriteObjectName("pkgVersionMicro");
+ requestEnvelopeWriter.WriteString(version.Micro().String());
+ }
+
+ if (version.PreRelease().Length() > 0) {
+ requestEnvelopeWriter.WriteObjectName("pkgVersionPreRelease");
+
requestEnvelopeWriter.WriteString(version.PreRelease().String());
+ }
+
+ if (version.Revision() != 0) {
+ requestEnvelopeWriter.WriteObjectName("pkgVersionRevision");
+ requestEnvelopeWriter.WriteInteger(version.Revision());
+ }
+
+ requestEnvelopeWriter.WriteObjectEnd();
+ requestEnvelopeWriter.WriteArrayEnd();
+ requestEnvelopeWriter.WriteObjectEnd();
+
+ return _SendJsonRequest("userrating", requestEnvelopeData,
+ requestEnvelopeData->Position(), NEEDS_AUTHORIZATION, message);
}
status_t
WebAppInterface::CreateUserRating(const BString& packageName,
+ const BPackageVersion& version,
const BString& architecture, const BString& repositoryCode,
const BString& languageCode, const BString& comment,
const BString& stability, int rating, BMessage& message)
{
- BString jsonString = JsonBuilder()
- .AddValue("jsonrpc", "2.0")
- .AddValue("id", ++fRequestIndex)
- .AddValue("method", "createUserRating")
- .AddArray("params")
- .AddObject()
- .AddValue("pkgName", packageName)
- .AddValue("pkgVersionArchitectureCode",
architecture)
- .AddValue("pkgVersionType", "LATEST")
- .AddValue("userNickname", fUsername)
- .AddValue("rating", rating)
- .AddValue("userRatingStabilityCode", stability,
true)
- .AddValue("comment", comment)
- .AddValue("repositoryCode", repositoryCode)
- .AddValue("naturalLanguageCode", languageCode)
- .EndObject()
- .EndArray()
- .End();
+ // BHttpRequest later takes ownership of this.
+ BMallocIO* requestEnvelopeData = new BMallocIO();
+ BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
+
+ requestEnvelopeWriter.WriteObjectStart();
+ _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
+ "createUserRating");
+ requestEnvelopeWriter.WriteObjectName("params");
+ requestEnvelopeWriter.WriteArrayStart();
+
+ requestEnvelopeWriter.WriteObjectStart();
+ requestEnvelopeWriter.WriteObjectName("pkgName");
+ requestEnvelopeWriter.WriteString(packageName.String());
+ requestEnvelopeWriter.WriteObjectName("pkgVersionArchitectureCode");
+ requestEnvelopeWriter.WriteString(architecture.String());
+ requestEnvelopeWriter.WriteObjectName("repositoryCode");
+ requestEnvelopeWriter.WriteString(repositoryCode.String());
+ requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
+ requestEnvelopeWriter.WriteString(languageCode.String());
+ requestEnvelopeWriter.WriteObjectName("pkgVersionType");
+ requestEnvelopeWriter.WriteString("SPECIFIC");
+ requestEnvelopeWriter.WriteObjectName("userNickname");
+ requestEnvelopeWriter.WriteString(fUsername.String());
+
+ if (!version.Major().IsEmpty()) {
+ requestEnvelopeWriter.WriteObjectName("pkgVersionMajor");
+ requestEnvelopeWriter.WriteString(version.Major());
+ }
+
+ if (!version.Minor().IsEmpty()) {
+ requestEnvelopeWriter.WriteObjectName("pkgVersionMinor");
+ requestEnvelopeWriter.WriteString(version.Minor());
+ }
+
+ if (!version.Micro().IsEmpty()) {
+ requestEnvelopeWriter.WriteObjectName("pkgVersionMicro");
+ requestEnvelopeWriter.WriteString(version.Micro());
+ }
+
+ if (!version.PreRelease().IsEmpty()) {
+ requestEnvelopeWriter.WriteObjectName("pkgVersionPreRelease");
+ requestEnvelopeWriter.WriteString(version.PreRelease());
+ }
+
+ if (version.Revision() != 0) {
+ requestEnvelopeWriter.WriteObjectName("pkgVersionRevision");
+ requestEnvelopeWriter.WriteInteger(version.Revision());
+ }
+
+ if (rating > 0.0f) {
+ requestEnvelopeWriter.WriteObjectName("rating");
+ requestEnvelopeWriter.WriteInteger(rating);
+ }
+
+ if (stability.Length() > 0) {
+
requestEnvelopeWriter.WriteObjectName("userRatingStabilityCode");
+ requestEnvelopeWriter.WriteString(stability);
+ }
+
+ if (comment.Length() > 0) {
+ requestEnvelopeWriter.WriteObjectName("comment");
+ requestEnvelopeWriter.WriteString(comment.String());
+ }
- return _SendJsonRequest("userrating", jsonString, NEEDS_AUTHORIZATION,
- message);
+ requestEnvelopeWriter.WriteObjectEnd();
+ requestEnvelopeWriter.WriteArrayEnd();
+ requestEnvelopeWriter.WriteObjectEnd();
+
+ return _SendJsonRequest("userrating", requestEnvelopeData,
+ requestEnvelopeData->Position(), NEEDS_AUTHORIZATION, message);
}
@@ -421,31 +505,56 @@ WebAppInterface::UpdateUserRating(const BString& ratingID,
const BString& languageCode, const BString& comment,
const BString& stability, int rating, bool active, BMessage& message)
{
- BString jsonString = JsonBuilder()
- .AddValue("jsonrpc", "2.0")
- .AddValue("id", ++fRequestIndex)
- .AddValue("method", "updateUserRating")
- .AddArray("params")
- .AddObject()
- .AddValue("code", ratingID)
- .AddValue("rating", rating)
- .AddValue("userRatingStabilityCode", stability,
true)
- .AddValue("comment", comment)
- .AddValue("naturalLanguageCode", languageCode)
- .AddValue("active", active)
- .AddArray("filter")
- .AddItem("ACTIVE")
- .AddItem("NATURALLANGUAGE")
- .AddItem("USERRATINGSTABILITY")
- .AddItem("COMMENT")
- .AddItem("RATING")
- .EndArray()
- .EndObject()
- .EndArray()
- .End();
+ // BHttpRequest later takes ownership of this.
+ BMallocIO* requestEnvelopeData = new BMallocIO();
+ BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData);
+
+ requestEnvelopeWriter.WriteObjectStart();
+ _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter,
+ "updateUserRating");
+
+ requestEnvelopeWriter.WriteObjectName("params");
+ requestEnvelopeWriter.WriteArrayStart();
+
+ requestEnvelopeWriter.WriteObjectStart();
+
+ requestEnvelopeWriter.WriteObjectName("code");
+ requestEnvelopeWriter.WriteString(ratingID.String());
+ requestEnvelopeWriter.WriteObjectName("naturalLanguageCode");
+ requestEnvelopeWriter.WriteString(languageCode.String());
+ requestEnvelopeWriter.WriteObjectName("active");
+ requestEnvelopeWriter.WriteBoolean(active);
- return _SendJsonRequest("userrating", jsonString, NEEDS_AUTHORIZATION,
- message);
+ requestEnvelopeWriter.WriteObjectName("filter");
+ requestEnvelopeWriter.WriteArrayStart();
+ requestEnvelopeWriter.WriteString("ACTIVE");
+ requestEnvelopeWriter.WriteString("NATURALLANGUAGE");
+ requestEnvelopeWriter.WriteString("USERRATINGSTABILITY");
+ requestEnvelopeWriter.WriteString("COMMENT");
+ requestEnvelopeWriter.WriteString("RATING");
+ requestEnvelopeWriter.WriteArrayEnd();
+
+ if (rating >= 0) {
+ requestEnvelopeWriter.WriteObjectName("rating");
+ requestEnvelopeWriter.WriteInteger(rating);
+ }
+
+ if (stability.Length() > 0) {
+
requestEnvelopeWriter.WriteObjectName("userRatingStabilityCode");
+ requestEnvelopeWriter.WriteString(stability);
+ }
+
+ if (comment.Length() > 0) {
+ requestEnvelopeWriter.WriteObjectName("comment");
+ requestEnvelopeWriter.WriteString(comment);
+ }
+
+ requestEnvelopeWriter.WriteObjectEnd();
+ requestEnvelopeWriter.WriteArrayEnd();
+ requestEnvelopeWriter.WriteObjectEnd();
+
+ return _SendJsonRequest("userrating", requestEnvelopeData,
+ requestEnvelopeData->Position(), NEEDS_AUTHORIZATION, message);
}
@@ -555,9 +664,45 @@ WebAppInterface::AuthenticateUser(const BString& nickName,
}
+/*! JSON-RPC invocations return a response. The response may be either
+ a result or it may be an error depending on the response structure.
+ If it is an error then there may be additional detail that is the
+ error code and message. This method will extract the error code
+ from the response. This method will return 0 if the payload does
+ not look like an error.
+*/
+
+int32
+WebAppInterface::ErrorCodeFromResponse(BMessage& response)
+{
+ BMessage error;
+ double code;
+
+ if (response.FindMessage("error", &error) == B_OK
+ && error.FindDouble("code", &code) == B_OK) {
+ return (int32) code;
+ }
+
+ return 0;
+}
+
+
// #pragma mark - private
+void
+WebAppInterface::_WriteStandardJsonRpcEnvelopeValues(BJsonWriter& writer,
+ const char* methodName)
+{
+ writer.WriteObjectName("jsonrpc");
+ writer.WriteString("2.0");
+ writer.WriteObjectName("id");
+ writer.WriteInteger(++fRequestIndex);
+ writer.WriteObjectName("method");
+ writer.WriteString(methodName);
+}
+
+
status_t
WebAppInterface::_SendJsonRequest(const char* domain, BDataIO* requestData,
size_t requestDataSize, uint32 flags, BMessage& reply) const
@@ -658,10 +803,10 @@ WebAppInterface::_SendJsonRequest(const char* domain,
BString jsonString,
if (Logger::IsTraceEnabled())
printf("_SendJsonRequest(%s)\n", jsonString.String());
+ // gets 'adopted' by the subsequent http request.
BMemoryIO* data = new BMemoryIO(
jsonString.String(), jsonString.Length() - 1);
return _SendJsonRequest(domain, data, jsonString.Length() - 1, flags,
reply);
}
-
diff --git a/src/apps/haikudepot/server/WebAppInterface.h
b/src/apps/haikudepot/server/WebAppInterface.h
index 09690a78db..b6ce6f7219 100644
--- a/src/apps/haikudepot/server/WebAppInterface.h
+++ b/src/apps/haikudepot/server/WebAppInterface.h
@@ -8,6 +8,7 @@
#include <Application.h>
+#include <JsonWriter.h>
#include <String.h>
#include <package/PackageVersion.h>
@@ -21,6 +22,18 @@ using BPackageKit::BPackageVersion;
typedef List<BString, false> StringList;
+/*! These are error codes that are sent back to the client from the server */
+
+#define ERROR_CODE_NONE 0
+#define ERROR_CODE_VALIDATION -32800
+#define ERROR_CODE_OBJECTNOTFOUND -32801
+#define ERROR_CODE_CAPTCHABADRESPONSE -32802
+#define ERROR_CODE_AUTHORIZATIONFAILURE -32803
+#define ERROR_CODE_BADPKGICON -32804
+#define ERROR_CODE_LIMITEXCEEDED -32805
+#define ERROR_CODE_AUTHORIZATIONRULECONFLICT -32806
+
+
class WebAppInterface {
public:
WebAppInterface();
@@ -57,6 +70,7 @@ public:
status_t CreateUserRating(
const
BString& packageName,
+ const
BPackageVersion& version,
const
BString& architecture,
const
BString& repositoryCode,
const
BString& languageCode,
@@ -92,7 +106,12 @@ public:
const
BString& passwordClear,
BMessage& message);
+ static int32 ErrorCodeFromResponse(BMessage&
response);
+
private:
+ void
_WriteStandardJsonRpcEnvelopeValues(
+
BJsonWriter& writer,
+ const
char* methodName);
status_t _SendJsonRequest(const
char* domain,
BString
jsonString, uint32 flags,
BMessage& reply) const;
diff --git a/src/apps/haikudepot/textview/TextDocumentLayout.cpp
b/src/apps/haikudepot/textview/TextDocumentLayout.cpp
index 8afa1142a2..6d00d78f46 100644
--- a/src/apps/haikudepot/textview/TextDocumentLayout.cpp
+++ b/src/apps/haikudepot/textview/TextDocumentLayout.cpp
@@ -25,9 +25,9 @@ public:
virtual void TextChanged(const TextChangedEvent& event)
{
- printf("TextChanged(%" B_PRIi32 ", %" B_PRIi32 ")\n",
- event.FirstChangedParagraph(),
- event.ChangedParagraphCount());
+// printf("TextChanged(%" B_PRIi32 ", %" B_PRIi32 ")\n",
+// event.FirstChangedParagraph(),
+// event.ChangedParagraphCount());
// TODO: The event does not contain useful data. Make the event
// system work so only the affected paragraphs are updated.
// I think we need "first affected", "last affected" (both
relative
diff --git a/src/apps/haikudepot/textview/TextEditor.cpp
b/src/apps/haikudepot/textview/TextEditor.cpp
index bfd46ba850..eaf6b688d5 100644
--- a/src/apps/haikudepot/textview/TextEditor.cpp
+++ b/src/apps/haikudepot/textview/TextEditor.cpp
@@ -257,8 +257,6 @@ TextEditor::Insert(int32 offset, const BString& string)
if (ret == B_OK) {
_SetCaretOffset(offset + string.CountChars(), true, false,
true);
-
- fDocument->PrintToStream();
}
return ret;
@@ -275,8 +273,6 @@ TextEditor::Remove(int32 offset, int32 length)
if (ret == B_OK) {
_SetCaretOffset(offset, true, false, true);
-
- fDocument->PrintToStream();
}
return ret;
@@ -293,8 +289,6 @@ TextEditor::Replace(int32 offset, int32 length, const
BString& string)
if (ret == B_OK) {
_SetCaretOffset(offset + string.CountChars(), true, false,
true);
-
- fDocument->PrintToStream();
}
return ret;
diff --git a/src/apps/haikudepot/ui/App.cpp b/src/apps/haikudepot/ui/App.cpp
index af3543d509..a28aafaf47 100644
--- a/src/apps/haikudepot/ui/App.cpp
+++ b/src/apps/haikudepot/ui/App.cpp
@@ -108,6 +108,18 @@ App::MessageReceived(BMessage* message)
ServerHelper::AlertClientTooOld(message);
break;
+ case MSG_NETWORK_TRANSPORT_ERROR:
+ ServerHelper::AlertTransportError(message);
+ break;
+
+ case MSG_SERVER_ERROR:
+ ServerHelper::AlertServerJsonRpcError(message);
+ break;
+
+ case MSG_SERVER_DATA_CHANGED:
+ fMainWindow->PostMessage(message);
+ break;
+
default:
BApplication::MessageReceived(message);
break;
diff --git a/src/apps/haikudepot/ui/FeaturedPackagesView.cpp
b/src/apps/haikudepot/ui/FeaturedPackagesView.cpp
index fd6cc9d234..d1ca2e0ee4 100644
--- a/src/apps/haikudepot/ui/FeaturedPackagesView.cpp
+++ b/src/apps/haikudepot/ui/FeaturedPackagesView.cpp
@@ -17,6 +17,7 @@
#include <SpaceLayoutItem.h>
#include "BitmapView.h"
+#include "HaikuDepotConstants.h"
#include "MainWindow.h"
#include "MarkupTextView.h"
#include "MessagePackageListener.h"
@@ -30,7 +31,8 @@
static const rgb_color kLightBlack = (rgb_color){ 60, 60, 60, 255 };
-static BitmapRef sInstalledIcon(new(std::nothrow) SharedBitmap(504), true);
+static BitmapRef sInstalledIcon(new(std::nothrow)
+ SharedBitmap(RSRC_INSTALLED), true);
// #pragma mark - PackageView
diff --git a/src/apps/haikudepot/ui/MainWindow.cpp
b/src/apps/haikudepot/ui/MainWindow.cpp
index 0db941aa7d..d15b7f7d66 100644
--- a/src/apps/haikudepot/ui/MainWindow.cpp
+++ b/src/apps/haikudepot/ui/MainWindow.cpp
@@ -390,6 +390,26 @@ MainWindow::MessageReceived(BMessage* message)
_AdoptModel();
break;
+ // this may be triggered by, for example, a user rating
being added
+ // or having been altered.
+ case MSG_SERVER_DATA_CHANGED:
+ {
+ BString name;
+ if (message->FindString("name", &name) == B_OK) {
+ BAutolock locker(fModel.Lock());
+ if (fPackageInfoView->Package()->Name() ==
name) {
+ _PopulatePackageAsync(true);
+ } else {
+ if (Logger::IsDebugEnabled()) {
+ printf("pkg [%s] is updated on
the server, but is "
+ "not selected so will
not be updated.\n",
+ name.String());
+ }
+ }
+ }
+ break;
+ }
+
case MSG_PACKAGE_SELECTED:
{
BString name;
@@ -624,8 +644,8 @@ MainWindow::StoreSettings(BMessage& settings) const
void
MainWindow::PackageChanged(const PackageInfoEvent& event)
{
- uint32 whatchedChanges = PKG_CHANGED_STATE | PKG_CHANGED_PROMINENCE;
- if ((event.Changes() & whatchedChanges) != 0) {
+ uint32 watchedChanges = PKG_CHANGED_STATE | PKG_CHANGED_PROMINENCE;
+ if ((event.Changes() & watchedChanges) != 0) {
PackageInfoRef ref(event.Package());
BMessage message(MSG_PACKAGE_CHANGED);
message.AddPointer("package", ref.Get());
@@ -850,12 +870,7 @@ MainWindow::_AdoptPackage(const PackageInfoRef& package)
fPackageListView->SelectPackage(package);
}
- // Trigger asynchronous package population from the web-app
- {
- AutoLocker<BLocker> lock(&fPackageToPopulateLock);
- fPackageToPopulate = package;
- }
- release_sem_etc(fPackageToPopulateSem, 1, 0);
+ _PopulatePackageAsync(false);
}
@@ -1255,6 +1270,34 @@ MainWindow::_PackageActionWorker(void* arg)
}
+/*! This method will cause the package to have its data refreshed from
+ the server application. The refresh happens in the background; this method
+ is asynchronous.
+*/
+
+void
+MainWindow::_PopulatePackageAsync(bool forcePopulate)
+{
+ // Trigger asynchronous package population from the web-app
+ {
+ AutoLocker<BLocker> lock(&fPackageToPopulateLock);
+ fPackageToPopulate = fPackageInfoView->Package();
+ fForcePopulatePackage = forcePopulate;
+ }
+ release_sem_etc(fPackageToPopulateSem, 1, 0);
+
+ if (Logger::IsDebugEnabled()) {
+ printf("pkg [%s] will be updated from the server.\n",
+ fPackageToPopulate->Name().String());
+ }
+}
+
+
+/*! This method will run in the background. The thread will block until there
+ is a package to be updated. When the thread unblocks, it will update the
+ package with information from the server.
+*/
+
status_t
MainWindow::_PopulatePackageWorker(void* arg)
{
@@ -1262,15 +1305,27 @@ MainWindow::_PopulatePackageWorker(void* arg)
while (acquire_sem(window->fPackageToPopulateSem) == B_OK) {
PackageInfoRef package;
+ bool force;
{
AutoLocker<BLocker>
lock(&window->fPackageToPopulateLock);
package = window->fPackageToPopulate;
+ force = window->fForcePopulatePackage;
}
if (package.Get() != NULL) {
- window->fModel.PopulatePackage(package,
- Model::POPULATE_USER_RATINGS |
Model::POPULATE_SCREEN_SHOTS
- | Model::POPULATE_CHANGELOG);
+ uint32 populateFlags = Model::POPULATE_USER_RATINGS
+ | Model::POPULATE_SCREEN_SHOTS
+ | Model::POPULATE_CHANGELOG;
+
+ if (force)
+ populateFlags |= Model::POPULATE_FORCE;
+
+ window->fModel.PopulatePackage(package, populateFlags);
+
+ if (Logger::IsDebugEnabled()) {
+ printf("populating package [%s]\n",
+ package->Name().String());
+ }
}
}
@@ -1435,9 +1490,35 @@ MainWindow::_UpdateAvailableRepositories()
}
+bool
+MainWindow::_SelectedPackageHasWebAppRepositoryCode()
+{
+ const PackageInfoRef& package = fPackageInfoView->Package();
+ const DepotInfo* depot = fModel.DepotForName(package->DepotName());
+
+ BString repositoryCode;
+
+ if (depot != NULL)
+ repositoryCode = depot->WebAppRepositoryCode();
+
+ return !repositoryCode.IsEmpty();
+}
+
+
void
MainWindow::_RatePackage()
{
+ if (!_SelectedPackageHasWebAppRepositoryCode()) {
+ BAlert* alert = new(std::nothrow) BAlert(
+ B_TRANSLATE("rating_not_possible"),
+ B_TRANSLATE("Because there is no representation for
this package "
+ "on the HaikuDepot server system, it is not
possible to create "
+ "a new rating or edit an existing rating."),
+ B_TRANSLATE("OK"));
+ alert->Go();
+ return;
+ }
+
if (fModel.Username().IsEmpty()) {
BAlert* alert = new(std::nothrow) BAlert(
B_TRANSLATE("Not logged in"),
diff --git a/src/apps/haikudepot/ui/MainWindow.h
b/src/apps/haikudepot/ui/MainWindow.h
index 64b580f32d..8b89cd4776 100644
--- a/src/apps/haikudepot/ui/MainWindow.h
+++ b/src/apps/haikudepot/ui/MainWindow.h
@@ -57,6 +57,8 @@ private:
virtual Model* GetModel();
private:
+ bool
_SelectedPackageHasWebAppRepositoryCode();
+
void _BuildMenu(BMenuBar*
menuBar);
void
_BuildUserMenu(BMenuBar* menuBar);
@@ -73,6 +75,7 @@ private:
void
_RefreshRepositories(bool force);
void
_RefreshPackageList(bool force);
+ void
_PopulatePackageAsync(bool forcePopulate);
void
_StartRefreshWorker(bool force = false);
static status_t _RefreshModelThreadWorker(void*
arg);
static status_t _PackageActionWorker(void* arg);
@@ -127,6 +130,7 @@ private:
thread_id fPopulatePackageWorker;
PackageInfoRef fPackageToPopulate;
+ bool fForcePopulatePackage;
BLocker fPackageToPopulateLock;
sem_id fPackageToPopulateSem;
diff --git a/src/apps/haikudepot/ui/PackageInfoView.cpp
b/src/apps/haikudepot/ui/PackageInfoView.cpp
index 4b4e86af8e..59c54020ab 100644
--- a/src/apps/haikudepot/ui/PackageInfoView.cpp
+++ b/src/apps/haikudepot/ui/PackageInfoView.cpp
@@ -1,5 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
+ * Copyright 2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -15,6 +16,7 @@
#include <CardLayout.h>
#include <Catalog.h>
#include <ColumnListView.h>
+#include <DateFormat.h>
#include <Font.h>
#include <GridView.h>
#include <LayoutBuilder.h>
@@ -724,7 +726,6 @@ public:
fWebsiteLinkView->SetViewUIColor(ViewUIColor(), kContentTint);
BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0.0f)
-// .Add(BSpaceLayoutItem::CreateHorizontalStrut(32.0f))
.AddGroup(leftGroup, 1.0f)
.Add(fScreenshotView)
.AddGroup(B_HORIZONTAL)
@@ -867,98 +868,71 @@ private:
class RatingItemView : public BGroupView {
public:
- RatingItemView(const UserRating& rating, const BitmapRef& voteUpIcon,
- const BitmapRef& voteDownIcon)
+ RatingItemView(const UserRating& rating)
:
BGroupView(B_HORIZONTAL, 0.0f)
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint);
- fAvatarView = new BitmapView("avatar view");
- if (rating.User().Avatar().Get() != NULL) {
- fAvatarView->SetBitmap(rating.User().Avatar(),
- SharedBitmap::SIZE_16);
+ BLayoutBuilder::Group<BLayoutBuilder::Group<void*> >
verticalGroup =
+ BLayoutBuilder::Group<>(this)
+ .AddGroup(B_VERTICAL, 0.0f);
+
+ {
+ BStringView* userNicknameView = new
BStringView("user-nickname",
+ rating.User().NickName());
+ userNicknameView->SetFont(be_bold_font);
+ verticalGroup.Add(userNicknameView);
}
- fAvatarView->SetExplicitMinSize(BSize(16.0f, 16.0f));
-
- fNameView = new BStringView("user name",
rating.User().NickName());
-
- BFont nameFont(be_bold_font);
- nameFont.SetSize(std::max(9.0f, floorf(nameFont.Size() *
0.9f)));
- fNameView->SetFont(&nameFont);
- fNameView->SetExplicitMaxSize(
- BSize(nameFont.StringWidth("xxxxxxxxxxxxxxxxxxxxxx"),
B_SIZE_UNSET));
-
- fRatingView = new RatingView("package rating view");
- fRatingView->SetRating(rating.Rating());
-
- BString ratingLabel;
- if (rating.Rating() >= 0.0f)
- ratingLabel.SetToFormat("%.1f", rating.Rating());
- fRatingLabelView = new BStringView("rating label", ratingLabel);
-
- BString versionLabel(B_TRANSLATE("for %Version%"));
- versionLabel.ReplaceAll("%Version%", rating.PackageVersion());
- fPackageVersionView = new BStringView("package version",
- versionLabel);
- BFont versionFont(be_plain_font);
- versionFont.SetSize(std::max(9.0f, floorf(versionFont.Size() *
0.85f)));
- fPackageVersionView->SetFont(&versionFont);
-
- // TODO: User rating IDs to identify which rating to vote up or
down
-// BMessage* voteUpMessage = new BMessage(MSG_VOTE_UP);
-// voteUpMessage->AddInt32("rating id", -1);
-// BMessage* voteDownMessage = new BMessage(MSG_VOTE_DOWN);
-// voteDownMessage->AddInt32("rating id", -1);
-//
-// fVoteUpIconView = new BitmapButton("vote up icon",
voteUpMessage);
-// fUpVoteCountView = new BStringView("up vote count", "");
-// fVoteDownIconView = new BitmapButton("vote down icon",
voteDownMessage);
-// fDownVoteCountView = new BStringView("up vote count", "");
-//
-// fVoteUpIconView->SetBitmap(voteUpIcon, SharedBitmap::SIZE_16);
-// fVoteDownIconView->SetBitmap(voteDownIcon,
SharedBitmap::SIZE_16);
-//
-// fUpVoteCountView->SetFont(&versionFont);
-// fUpVoteCountView->SetHighColor(kLightBlack);
-// fDownVoteCountView->SetFont(&versionFont);
-// fDownVoteCountView->SetHighColor(kLightBlack);
-//
-// BString voteCountLabel;
-// voteCountLabel.SetToFormat("%" B_PRId32, rating.UpVotes());
-// fUpVoteCountView->SetText(voteCountLabel);
-// voteCountLabel.SetToFormat("%" B_PRId32, rating.DownVotes());
-// fDownVoteCountView->SetText(voteCountLabel);
-
- fTextView = new TextView("rating text");
- ParagraphStyle paragraphStyle(fTextView->ParagraphStyle());
- paragraphStyle.SetJustify(true);
- fTextView->SetParagraphStyle(paragraphStyle);
-
- fTextView->SetText(rating.Comment());
- BLayoutBuilder::Group<>(this)
- .Add(fAvatarView, 0.2f)
- .AddGroup(B_VERTICAL, 0.0f)
- .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
- .Add(fNameView)
- .Add(fRatingView)
- .Add(fRatingLabelView)
- .AddGlue(0.1f)
- .Add(fPackageVersionView)
- .AddGlue(5.0f)
-// .AddGroup(B_HORIZONTAL, 0.0f, 0.0f)
-// .Add(fVoteUpIconView)
-// .Add(fUpVoteCountView)
-//
.AddStrut(B_USE_HALF_ITEM_SPACING)
-// .Add(fVoteDownIconView)
-// .Add(fDownVoteCountView)
-// .End()
- .End()
- .Add(fTextView)
+ BLayoutBuilder::Group<BLayoutBuilder::Group<
+ BLayoutBuilder::Group<void *> > > ratingGroup =
+ verticalGroup.AddGroup(B_HORIZONTAL,
B_USE_DEFAULT_SPACING);
+
+ if (rating.Rating() >= 0) {
+ RatingView* ratingView = new RatingView("package rating
view");
+ ratingView->SetRating(rating.Rating());
+ ratingGroup.Add(ratingView);
+ }
+
+ {
+ BDateFormat dateFormat;
+ BString createTimestampPresentation;
+
+ dateFormat.Format(createTimestampPresentation,
+ rating.CreateTimestamp().Date(),
B_MEDIUM_DATE_FORMAT);
+
+ BString ratingContextDescription(
+ B_TRANSLATE("%hd.timestamp% (version
%hd.version%)"));
+ ratingContextDescription.ReplaceAll("%hd.timestamp%",
+ createTimestampPresentation);
+ ratingContextDescription.ReplaceAll("%hd.version%",
+ rating.PackageVersion());
+
+ BStringView* ratingContextView = new
BStringView("rating-context",
+ ratingContextDescription);
+ BFont versionFont(be_plain_font);
+ ratingContextView->SetFont(&versionFont);
+ ratingGroup.Add(ratingContextView);
+ }
+
+ ratingGroup.AddGlue();
+ ratingGroup.End();
+
+ if (rating.Comment() > 0) {
+ TextView* textView = new TextView("rating-text");
+ ParagraphStyle
paragraphStyle(textView->ParagraphStyle());
+ paragraphStyle.SetJustify(true);
+ textView->SetParagraphStyle(paragraphStyle);
+ textView->SetText(rating.Comment());
+ verticalGroup.AddStrut(8.0f);
+ verticalGroup.Add(textView);
+ verticalGroup.AddStrut(8.0f);
+ }
+
+ verticalGroup
.End()
- .SetInsets(B_USE_DEFAULT_SPACING)
- ;
+ .SetInsets(B_USE_DEFAULT_SPACING);
SetFlags(Flags() | B_WILL_DRAW);
}
@@ -976,19 +950,6 @@ public:
StrokeLine(Bounds().LeftBottom(), Bounds().RightBottom());
}
-private:
- BitmapView* fAvatarView;
- BStringView* fNameView;
- RatingView* fRatingView;
- BStringView* fRatingLabelView;
- BStringView* fPackageVersionView;
-
-// BitmapView* fVoteUpIconView;
-// BStringView* fUpVoteCountView;
-// BitmapView* fVoteDownIconView;
-// BStringView* fDownVoteCountView;
-
- TextView* fTextView;
};
@@ -1062,9 +1023,7 @@ class UserRatingsView : public BGroupView {
public:
UserRatingsView()
:
- BGroupView("package ratings view", B_HORIZONTAL),
- fThumbsUpIcon(BitmapRef(new SharedBitmap(502), true)),
- fThumbsDownIcon(BitmapRef(new SharedBitmap(503), true))
+ BGroupView("package ratings view", B_HORIZONTAL)
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint);
@@ -1077,6 +1036,8 @@ public:
BScrollView* scrollView = new RatingsScrollView(
"ratings scroll view", ratingsContainerView);
+ scrollView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
+ B_SIZE_UNLIMITED));
BLayoutBuilder::Group<>(this)
.AddGroup(B_VERTICAL)
@@ -1084,6 +1045,7 @@ public:
.AddGlue()
.SetInsets(0.0f, B_USE_DEFAULT_SPACING, 0.0f,
0.0f)
.End()
+ .AddStrut(64.0)
.Add(scrollView, 1.0f)
.SetInsets(B_USE_DEFAULT_SPACING, -1.0f, -1.0f, -1.0f)
;
@@ -1122,15 +1084,10 @@ public:
// TODO: Sort by age or usefullness rating
for (int i = count - 1; i >= 0; i--) {
const UserRating& rating = userRatings.ItemAtFast(i);
- // Prevent ratings from showing that have a comment
which
- // is in another language
- if (!rating.Comment().IsEmpty()
- && fPreferredLanguages.CountItems() > 0
- &&
!fPreferredLanguages.Contains(rating.Language())) {
- continue;
- }
- RatingItemView* view = new RatingItemView(rating,
fThumbsUpIcon,
- fThumbsDownIcon);
+ // was previously filtering comments just for
the current
+ // user's language, but as there are not so
many comments at
+ // the moment, just show all of them for now.
+ RatingItemView* view = new RatingItemView(rating);
fRatingContainerLayout->AddView(0, view);
}
}
@@ -1181,8 +1138,6 @@ private:
private:
BGroupLayout* fRatingContainerLayout;
RatingSummaryView* fRatingSummaryView;
- BitmapRef fThumbsUpIcon;
- BitmapRef fThumbsDownIcon;
StringList fPreferredLanguages;
};
@@ -1516,5 +1471,4 @@ PackageInfoView::Clear()
fPackageListener->SetPackage(PackageInfoRef(NULL));
fPackage.Unset();
-}
-
+}
\ No newline at end of file
diff --git a/src/apps/haikudepot/ui/RatePackageWindow.cpp
b/src/apps/haikudepot/ui/RatePackageWindow.cpp
index 4906ac62ed..585eaa3e91 100644
--- a/src/apps/haikudepot/ui/RatePackageWindow.cpp
+++ b/src/apps/haikudepot/ui/RatePackageWindow.cpp
@@ -1,6 +1,6 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@xxxxxx>.
- * Copyright 2016, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
+ * Copyright 2016-2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -21,8 +21,10 @@
#include <ScrollView.h>
#include <StringView.h>
+#include "HaikuDepotConstants.h"
#include "MarkupParser.h"
#include "RatingView.h"
+#include "ServerHelper.h"
#include "TextDocumentView.h"
#include "WebAppInterface.h"
@@ -32,11 +34,12 @@
enum {
- MSG_SEND = 'send',
- MSG_PACKAGE_RATED = 'rpkg',
- MSG_STABILITY_SELECTED = 'stbl',
- MSG_LANGUAGE_SELECTED = 'lngs',
- MSG_RATING_ACTIVE_CHANGED = 'rtac'
+ MSG_SEND = 'send',
+ MSG_PACKAGE_RATED = 'rpkg',
+ MSG_STABILITY_SELECTED = 'stbl',
+ MSG_LANGUAGE_SELECTED = 'lngs',
+ MSG_RATING_ACTIVE_CHANGED = 'rtac',
+ MSG_RATING_DETERMINATE_CHANGED = 'rdch'
};
//! Layouts the scrollbar so it looks nice with no border and the document
@@ -98,7 +101,8 @@ public:
SetRatingView()
:
RatingView("rate package view"),
- fPermanentRating(0.0f)
+ fPermanentRating(0.0f),
+ fRatingDeterminate(true)
{
SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
SetRating(fPermanentRating);
@@ -134,6 +138,24 @@ public:
SetRating(rating);
}
+/*! By setting this to false, this indicates that there is no rating for the
+ set; ie NULL. The indeterminate rating is indicated by a pale grey
+ colored star.
+*/
+
+ void SetRatingDeterminate(bool value) {
+ fRatingDeterminate = value;
+ Invalidate();
+ }
+
+protected:
+ virtual const BBitmap* StarBitmap()
+ {
+ if (fRatingDeterminate)
+ return fStarBlueBitmap.Bitmap(SharedBitmap::SIZE_16);
+ return fStarGrayBitmap.Bitmap(SharedBitmap::SIZE_16);
+ }
+
private:
float _RatingForMousePos(BPoint where)
{
@@ -141,6 +163,7 @@ private:
}
float fPermanentRating;
+ bool fRatingDeterminate;
};
@@ -189,6 +212,10 @@ RatePackageWindow::RatePackageWindow(BWindow* parent,
BRect frame,
B_TRANSLATE("Your rating:"));
fSetRatingView = new SetRatingView();
+ fSetRatingView->SetRatingDeterminate(false);
+ fRatingDeterminateCheckBox = new BCheckBox("has rating", NULL,
+ new BMessage(MSG_RATING_DETERMINATE_CHANGED));
+ fRatingDeterminateCheckBox->SetValue(B_CONTROL_OFF);
fTextView = new TextDocumentView();
ScrollView* textScrollView = new ScrollView(
@@ -258,7 +285,10 @@ RatePackageWindow::RatePackageWindow(BWindow* parent,
BRect frame,
BLayoutBuilder::Group<>(this, B_VERTICAL)
.AddGrid()
.Add(ratingLabel, 0, 0)
- .Add(fSetRatingView, 1, 0)
+ .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING, 1, 0)
+ .Add(fRatingDeterminateCheckBox)
+ .Add(fSetRatingView)
+ .End()
.AddMenuField(fStabilityField, 0, 1)
.AddMenuField(fCommentLanguageField, 0, 2)
.End()
@@ -284,12 +314,31 @@ RatePackageWindow::~RatePackageWindow()
}
+void
+RatePackageWindow::DispatchMessage(BMessage* message, BHandler *handler)
+{
+ if (message->what == B_KEY_DOWN) {
+ int8 key;
+ // if the user presses escape, close the window.
+ if ((message->FindInt8("byte", &key) == B_OK)
+ && key == B_ESCAPE) {
+ Quit();
+ return;
+ }
+ }
+
+ BWindow::DispatchMessage(message, handler);
+}
+
+
void
RatePackageWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_PACKAGE_RATED:
message->FindFloat("rating", &fRating);
+ fSetRatingView->SetRatingDeterminate(true);
+ fRatingDeterminateCheckBox->SetValue(B_CONTROL_ON);
break;
case MSG_STABILITY_SELECTED:
@@ -300,6 +349,11 @@ RatePackageWindow::MessageReceived(BMessage* message)
message->FindString("code", &fCommentLanguage);
break;
+ case MSG_RATING_DETERMINATE_CHANGED:
+ fSetRatingView->SetRatingDeterminate(
+ fRatingDeterminateCheckBox->Value() ==
B_CONTROL_ON);
+ break;
+
case MSG_RATING_ACTIVE_CHANGED:
{
int32 value;
@@ -308,6 +362,32 @@ RatePackageWindow::MessageReceived(BMessage* message)
break;
}
+ case MSG_DID_ADD_USER_RATING:
+ {
+ BAlert* alert = new(std::nothrow) BAlert(
+ B_TRANSLATE("user_rating"),
+ B_TRANSLATE("Your rating was uploaded
successfully. "
+ "You can update or remove it at any
time by visiting the "
+ "server application on the web."),
+ B_TRANSLATE("Close"), NULL, NULL,
+ B_WIDTH_AS_USUAL, B_WARNING_ALERT);
+ alert->Go();
+ _RefreshPackageData();
+ break;
+ }
+
+ case MSG_DID_UPDATE_USER_RATING:
+ {
+ BAlert* alert = new(std::nothrow) BAlert(
+ B_TRANSLATE("user_rating"),
+ B_TRANSLATE("Your rating was updated."),
+ B_TRANSLATE("Close"), NULL, NULL,
+ B_WIDTH_AS_USUAL, B_WARNING_ALERT);
+ alert->Go();
+ _RefreshPackageData();
+ break;
+ }
+
case MSG_SEND:
_SendRating();
break;
@@ -318,6 +398,20 @@ RatePackageWindow::MessageReceived(BMessage* message)
}
}
+/*! Refresh the data shown about the current page. This may be useful, for
+ example when somebody adds a rating and that changes the rating of the
+ package or they add a rating and want to see that immediately. The logic
+ should round-trip to the server so that actual data is shown.
+*/
+
+void
+RatePackageWindow::_RefreshPackageData()
+{
+ BMessage message(MSG_SERVER_DATA_CHANGED);
+ message.AddString("name", fPackage->Name());
+ be_app->PostMessage(&message);
+}
+
void
RatePackageWindow::SetPackage(const PackageInfoRef& package)
@@ -360,8 +454,6 @@ RatePackageWindow::_SetWorkerThread(thread_id thread)
bool enabled = thread < 0;
-// fTextEditor->SetEnabled(enabled);
-// fSetRatingView->SetEnabled(enabled);
fStabilityField->SetEnabled(enabled);
fCommentLanguageField->SetEnabled(enabled);
fSendButton->SetEnabled(enabled);
@@ -386,6 +478,68 @@ RatePackageWindow::_QueryRatingThreadEntry(void* data)
}
+/*! A server request has been made to the server and the server has responded
+ with some data. The data is known not to be an error and now the data can
+ be extracted into the user interface elements.
+*/
+
+void
+RatePackageWindow::_RelayServerDataToUI(BMessage& response)
+{
+ if (Lock()) {
+ response.FindString("code", &fRatingID);
+ response.FindBool("active", &fRatingActive);
+ BString comment;
+ if (response.FindString("comment", &comment) == B_OK) {
+ MarkupParser parser;
+ fRatingText = parser.CreateDocumentFromMarkup(comment);
+ fTextView->SetTextDocument(fRatingText);
+ }
+ if (response.FindString("userRatingStabilityCode",
+ &fStability) == B_OK) {
+ int32 index = 0;
+ for (int32 i = fStabilityCodes.CountItems() - 1; i >=
0; i--) {
+ const StabilityRating& stability
+ = fStabilityCodes.ItemAtFast(i);
+ if (stability.Name() == fStability) {
+ index = i;
+ break;
+ }
+ }
+ BMenuItem* item =
fStabilityField->Menu()->ItemAt(index);
+ if (item != NULL)
+ item->SetMarked(true);
+ }
+ if (response.FindString("naturalLanguageCode",
+ &fCommentLanguage) == B_OK) {
+ BMenuItem* item = fCommentLanguageField->Menu()->ItemAt(
+
fModel.SupportedLanguages().IndexOf(fCommentLanguage));
+ if (item != NULL)
+ item->SetMarked(true);
+ }
+ double rating;
+ if (response.FindDouble("rating", &rating) == B_OK) {
+ fRating = (float)rating;
+ fSetRatingView->SetPermanentRating(fRating);
+ fSetRatingView->SetRatingDeterminate(true);
+ fRatingDeterminateCheckBox->SetValue(B_CONTROL_ON);
+ } else {
+ fSetRatingView->SetRatingDeterminate(false);
+ fRatingDeterminateCheckBox->SetValue(B_CONTROL_OFF);
+ }
+
+ fRatingActiveCheckBox->SetValue(fRatingActive);
+ fRatingActiveCheckBox->Show();
+
+ fSendButton->SetLabel(B_TRANSLATE("Update"));
+
+ Unlock();
+ } else {
+ fprintf(stderr, "unable to acquire lock to update the ui\n");
+ }
+}
+
+
void
RatePackageWindow::_QueryRatingThread()
{
@@ -416,67 +570,50 @@ RatePackageWindow::_QueryRatingThread()
if (depot != NULL)
repositoryCode = depot->WebAppRepositoryCode();
- if (repositoryCode.Length() == 0) {
+ if (repositoryCode.IsEmpty()) {
printf("unable to obtain the repository code for depot; %s\n",
package->DepotName().String());
+ BMessenger(this).SendMessage(B_QUIT_REQUESTED);
} else {
status_t status = interface.RetrieveUserRating(
package->Name(), package->Version(),
package->Architecture(),
repositoryCode, username, info);
- // info.PrintToStream();
-
- BMessage result;
- if (status == B_OK && info.FindMessage("result", &result) ==
B_OK
- && Lock()) {
-
- result.FindString("code", &fRatingID);
- result.FindBool("active", &fRatingActive);
- BString comment;
- if (result.FindString("comment", &comment) == B_OK) {
- MarkupParser parser;
- fRatingText =
parser.CreateDocumentFromMarkup(comment);
- fTextView->SetTextDocument(fRatingText);
- }
- if (result.FindString("userRatingStabilityCode",
- &fStability) == B_OK) {
- int32 index = 0;
- for (int32 i = fStabilityCodes.CountItems() -
1; i >= 0; i--) {
- const StabilityRating& stability
- = fStabilityCodes.ItemAtFast(i);
- if (stability.Name() == fStability) {
- index = i;
- break;
+ if (status == B_OK) {
+ // could be an error or could be a valid
response envelope
+ // containing data.
+ switch (interface.ErrorCodeFromResponse(info)) {
+ case ERROR_CODE_NONE:
+ {
+ //info.PrintToStream();
+ BMessage result;
+ if (info.FindMessage("result", &result)
== B_OK) {
+ _RelayServerDataToUI(result);
+ } else {
+ fprintf(stderr, "bad response
envelope missing 'result'"
+ "entry\n");
+
ServerHelper::NotifyTransportError(B_BAD_VALUE);
+
BMessenger(this).SendMessage(B_QUIT_REQUESTED);
}
+ break;
}
- BMenuItem* item =
fStabilityField->Menu()->ItemAt(index);
- if (item != NULL)
- item->SetMarked(true);
- }
- if (result.FindString("naturalLanguageCode",
- &fCommentLanguage) == B_OK) {
- BMenuItem* item =
fCommentLanguageField->Menu()->ItemAt(
-
fModel.SupportedLanguages().IndexOf(fCommentLanguage));
- if (item != NULL)
- item->SetMarked(true);
+ case ERROR_CODE_OBJECTNOTFOUND:
+ // an expected response
+ fprintf(stderr, "there was no previous
rating for this"
+ " user on this version of this
package so a new rating"
+ " will be added.\n");
+ break;
+ default:
+
ServerHelper::NotifyServerJsonRpcError(info);
+
BMessenger(this).SendMessage(B_QUIT_REQUESTED);
+ break;
}
- double rating;
- if (result.FindDouble("rating", &rating) == B_OK) {
- fRating = (float)rating;
- fSetRatingView->SetPermanentRating(fRating);
- }
-
- fRatingActiveCheckBox->SetValue(fRatingActive);
- fRatingActiveCheckBox->Show();
-
- fSendButton->SetLabel(B_TRANSLATE("Update"));
-
- Unlock();
} else {
- fprintf(stderr, "rating query: Failed response: %s\n",
+ fprintf(stderr, "an error has arisen communicating with
the"
+ " server to obtain data for an existing rating
[%s]\n",
strerror(status));
- if (!info.IsEmpty())
- info.PrintToStream();
+ ServerHelper::NotifyTransportError(status);
+ BMessenger(this).SendMessage(B_QUIT_REQUESTED);
}
}
@@ -501,6 +638,7 @@ RatePackageWindow::_SendRatingThread()
return;
}
+ BMessenger messenger = BMessenger(this);
BString package = fPackage->Name();
BString architecture = fPackage->Architecture();
BString repositoryCode;
@@ -533,86 +671,41 @@ RatePackageWindow::_SendRatingThread()
status_t status;
BMessage info;
if (ratingID.Length() > 0) {
+ printf("will update the existing user rating [%s]\n",
+ ratingID.String());
status = interface.UpdateUserRating(ratingID,
- languageCode, comment, stability, rating, active, info);
+ languageCode, comment, stability, rating, active, info);
} else {
- status = interface.CreateUserRating(package, architecture,
- repositoryCode, languageCode, comment, stability,
rating, info);
+ printf("will create a new user rating for pkg [%s]\n",
+ package.String());
+ status = interface.CreateUserRating(package,
fPackage->Version(),
+ architecture, repositoryCode, languageCode, comment,
stability,
+ rating, info);
}
- BString error = B_TRANSLATE(
- "There was a puzzling response from the web service.");
-
- BMessage result;
if (status == B_OK) {
- if (info.FindMessage("result", &result) == B_OK) {
- error = "";
- } else if (info.FindMessage("error", &result) == B_OK) {
- result.PrintToStream();
- BString message;
- if (result.FindString("message", &message) == B_OK) {
- if (message == "objectnotfound") {
- error = B_TRANSLATE("The package was
not found by the "
- "web service. This probably
means that it comes "
- "from a depot which is not
tracked there. Rating "
- "such packages is unfortunately
not supported.");
- } else {
- error << B_TRANSLATE(" It responded
with: ");
- error << message;
- }
+ // could be an error or could be a valid response
envelope
+ // containing data.
+ switch (interface.ErrorCodeFromResponse(info)) {
+ case ERROR_CODE_NONE:
+ {
+ if (ratingID.Length() > 0)
+
messenger.SendMessage(MSG_DID_UPDATE_USER_RATING);
+ else
+
messenger.SendMessage(MSG_DID_ADD_USER_RATING);
+ break;
}
+ default:
+ ServerHelper::NotifyServerJsonRpcError(info);
+ break;
}
} else {
- error = B_TRANSLATE(
- "It was not possible to contact the web service.");
+ fprintf(stderr, "an error has arisen communicating with the"
+ " server to obtain data for an existing rating [%s]\n",
+ strerror(status));
+ ServerHelper::NotifyTransportError(status);
}
- if (!error.IsEmpty()) {
- BString failedTitle;
- if (ratingID.Length() > 0)
- failedTitle = B_TRANSLATE("Failed to update rating");
- else
- failedTitle = B_TRANSLATE("Failed to rate package");
-
- BAlert* alert = new(std::nothrow) BAlert(
- failedTitle,
- error,
- B_TRANSLATE("Close"), NULL, NULL,
- B_WIDTH_AS_USUAL, B_WARNING_ALERT);
-
- if (alert != NULL)
- alert->Go();
-
- fprintf(stderr,
- B_TRANSLATE("Failed to create or update rating: %s\n"),
- error.String());
- if (!info.IsEmpty())
- info.PrintToStream();
-
- _SetWorkerThread(-1);
- } else {
- _SetWorkerThread(-1);
-
- fModel.PopulatePackage(fPackage,
- Model::POPULATE_FORCE | Model::POPULATE_USER_RATINGS);
-
- BMessenger(this).SendMessage(B_QUIT_REQUESTED);
-
- BString message;
- if (ratingID.Length() > 0) {
- message = B_TRANSLATE("Your rating was updated
successfully.");
- } else {
- message = B_TRANSLATE("Your rating was uploaded
successfully. "
- "You can update or remove it at any time by
rating the "
- "package again.");
- }
-
- BAlert* alert = new(std::nothrow) BAlert(
- B_TRANSLATE("Success"),
- message,
- B_TRANSLATE("Close"));
-
- if (alert != NULL)
- alert->Go();
- }
+ messenger.SendMessage(B_QUIT_REQUESTED);
+ _SetWorkerThread(-1);
}
diff --git a/src/apps/haikudepot/ui/RatePackageWindow.h
b/src/apps/haikudepot/ui/RatePackageWindow.h
index 1835df97a4..b5c6889f79 100644
--- a/src/apps/haikudepot/ui/RatePackageWindow.h
+++ b/src/apps/haikudepot/ui/RatePackageWindow.h
@@ -1,5 +1,6 @@
/*
* Copyright 2014, Stephan Aßmus <superstippi@xxxxxx>.
+ * Copyright 2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef RATE_PACKAGE_WINDOW_H
@@ -26,11 +27,15 @@ public:
Model&
model);
virtual ~RatePackageWindow();
+ virtual void DispatchMessage(BMessage*
message,
+
BHandler *handler);
virtual void MessageReceived(BMessage*
message);
void SetPackage(const
PackageInfoRef& package);
private:
+ void
_RelayServerDataToUI(BMessage& result);
+
void _SendRating();
void
_SetWorkerThread(thread_id thread);
@@ -41,6 +46,8 @@ private:
static int32 _SendRatingThreadEntry(void*
data);
void _SendRatingThread();
+ void _RefreshPackageData();
+
private:
Model& fModel;
TextDocumentRef fRatingText;
@@ -54,6 +61,7 @@ private:
PackageInfoRef fPackage;
SetRatingView* fSetRatingView;
+ BCheckBox*
fRatingDeterminateCheckBox;
BMenuField* fStabilityField;
BMenuField* fCommentLanguageField;
TextDocumentView* fTextView;
diff --git a/src/apps/haikudepot/ui/ScreenshotWindow.cpp
b/src/apps/haikudepot/ui/ScreenshotWindow.cpp
index 48a341e485..3ef69a0f84 100644
--- a/src/apps/haikudepot/ui/ScreenshotWindow.cpp
+++ b/src/apps/haikudepot/ui/ScreenshotWindow.cpp
@@ -17,6 +17,7 @@
#include "BarberPole.h"
#include "BitmapView.h"
+#include "HaikuDepotConstants.h"
#include "WebAppInterface.h"
@@ -29,9 +30,9 @@ static const rgb_color kBackgroundColor = { 51, 102, 152, 255
};
// transparent regions
static BitmapRef sNextButtonIcon(
- new(std::nothrow) SharedBitmap(505), true);
+ new(std::nothrow) SharedBitmap(RSRC_ARROW_LEFT), true);
static BitmapRef sPreviousButtonIcon(
- new(std::nothrow) SharedBitmap(506), true);
+ new(std::nothrow) SharedBitmap(RSRC_ARROW_RIGHT), true);
ScreenshotWindow::ScreenshotWindow(BWindow* parent, BRect frame)
diff --git a/src/apps/haikudepot/ui_generic/RatingView.cpp
b/src/apps/haikudepot/ui_generic/RatingView.cpp
index bd733103c0..ca0c001b23 100644
--- a/src/apps/haikudepot/ui_generic/RatingView.cpp
+++ b/src/apps/haikudepot/ui_generic/RatingView.cpp
@@ -1,5 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
+ * Copyright 2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@@ -10,12 +11,15 @@
#include <LayoutUtils.h>
+#include "HaikuDepotConstants.h"
+
RatingView::RatingView(const char* name)
:
BView(name, B_WILL_DRAW),
- fStarBitmap(501),
- fRating(-1.0f)
+ fStarBlueBitmap(RSRC_STAR_BLUE),
+ fStarGrayBitmap(RSRC_STAR_GREY),
+ fRating(RATING_MISSING)
{
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
SetLowUIColor(ViewUIColor());
@@ -33,16 +37,26 @@ RatingView::AttachedToWindow()
AdoptParentColors();
}
+/*! This method will return a star image that can be used repeatedly in the
+ user interface in order to signify the rating given by a user. It could
+ be grey if no rating is assigned.
+*/
+
+const BBitmap*
+RatingView::StarBitmap()
+{
+ if (fRating < RATING_MIN)
+ return fStarGrayBitmap.Bitmap(SharedBitmap::SIZE_16);
+ return fStarBlueBitmap.Bitmap(SharedBitmap::SIZE_16);
+}
+
void
RatingView::Draw(BRect updateRect)
{
FillRect(updateRect, B_SOLID_LOW);
+ const BBitmap* star = StarBitmap();
- if (fRating < 0.0f)
- return;
-
- const BBitmap* star = fStarBitmap.Bitmap(SharedBitmap::SIZE_16);
if (star == NULL) {
fprintf(stderr, "No star icon found in application
resources.\n");
return;
@@ -56,21 +70,20 @@ RatingView::Draw(BRect updateRect)
x += 16 + 2;
}
- if (fRating >= 5.0f)
- return;
+ if (fRating >= RATING_MIN && fRating < 5.0f) {
+ SetDrawingMode(B_OP_OVER);
- SetDrawingMode(B_OP_OVER);
+ BRect rect(Bounds());
+ rect.right = x - 2;
+ rect.left = ceilf(rect.left + (fRating / 5.0f) * rect.Width());
- BRect rect(Bounds());
- rect.right = x - 2;
- rect.left = ceilf(rect.left + (fRating / 5.0f) * rect.Width());
+ rgb_color color = LowColor();
+ color.alpha = 190;
+ SetHighColor(color);
- rgb_color color = LowColor();
- color.alpha = 190;
- SetHighColor(color);
-
- SetDrawingMode(B_OP_ALPHA);
- FillRect(rect, B_SOLID_HIGH);
+ SetDrawingMode(B_OP_ALPHA);
+ FillRect(rect, B_SOLID_HIGH);
+ }
}
diff --git a/src/apps/haikudepot/ui_generic/RatingView.h
b/src/apps/haikudepot/ui_generic/RatingView.h
index 3f439ead1e..05abe99cda 100644
--- a/src/apps/haikudepot/ui_generic/RatingView.h
+++ b/src/apps/haikudepot/ui_generic/RatingView.h
@@ -1,5 +1,6 @@
/*
* Copyright 2013-2014, Stephan Aßmus <superstippi@xxxxxx>.
+ * Copyright 2018, Andrew Lindesay <apl@xxxxxxxxxxxxxx>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef RATING_VIEW_H
@@ -26,8 +27,12 @@ public:
void SetRating(float rating);
float Rating() const;
+protected:
+ virtual const BBitmap* StarBitmap();
+ SharedBitmap fStarBlueBitmap;
+ SharedBitmap fStarGrayBitmap;
+
private:
- SharedBitmap fStarBitmap;
float fRating;
};