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
This commit is contained in:
committed by
waddlesplash
parent
466255e324
commit
a9edb9bffa
Binary file not shown.
Binary file not shown.
@@ -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"
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
* Copyright 2018, Andrew Lindesay <[email protected]>.
|
||||
* 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)
|
||||
#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
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
|
||||
* Copyright 2016-2017, Andrew Lindesay <[email protected]>.
|
||||
* Copyright 2016-2018, Andrew Lindesay <[email protected]>.
|
||||
* 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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017, Andrew Lindesay <[email protected]>.
|
||||
* Copyright 2017-2018, Andrew Lindesay <[email protected]>.
|
||||
* 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());
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
return _SendJsonRequest("userrating", jsonString, NEEDS_AUTHORIZATION,
|
||||
message);
|
||||
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());
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return _SendJsonRequest("userrating", jsonString, NEEDS_AUTHORIZATION,
|
||||
message);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013-2014, Stephan Aßmus <[email protected]>.
|
||||
* Copyright 2018, Andrew Lindesay <[email protected]>.
|
||||
* 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());
|
||||
BLayoutBuilder::Group<BLayoutBuilder::Group<
|
||||
BLayoutBuilder::Group<void *> > > ratingGroup =
|
||||
verticalGroup.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING);
|
||||
|
||||
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));
|
||||
if (rating.Rating() >= 0) {
|
||||
RatingView* ratingView = new RatingView("package rating view");
|
||||
ratingView->SetRating(rating.Rating());
|
||||
ratingGroup.Add(ratingView);
|
||||
}
|
||||
|
||||
fRatingView = new RatingView("package rating view");
|
||||
fRatingView->SetRating(rating.Rating());
|
||||
{
|
||||
BDateFormat dateFormat;
|
||||
BString createTimestampPresentation;
|
||||
|
||||
BString ratingLabel;
|
||||
if (rating.Rating() >= 0.0f)
|
||||
ratingLabel.SetToFormat("%.1f", rating.Rating());
|
||||
fRatingLabelView = new BStringView("rating label", ratingLabel);
|
||||
dateFormat.Format(createTimestampPresentation,
|
||||
rating.CreateTimestamp().Date(), B_MEDIUM_DATE_FORMAT);
|
||||
|
||||
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);
|
||||
BString ratingContextDescription(
|
||||
B_TRANSLATE("%hd.timestamp% (version %hd.version%)"));
|
||||
ratingContextDescription.ReplaceAll("%hd.timestamp%",
|
||||
createTimestampPresentation);
|
||||
ratingContextDescription.ReplaceAll("%hd.version%",
|
||||
rating.PackageVersion());
|
||||
|
||||
// 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);
|
||||
BStringView* ratingContextView = new BStringView("rating-context",
|
||||
ratingContextDescription);
|
||||
BFont versionFont(be_plain_font);
|
||||
ratingContextView->SetFont(&versionFont);
|
||||
ratingGroup.Add(ratingContextView);
|
||||
}
|
||||
|
||||
fTextView = new TextView("rating text");
|
||||
ParagraphStyle paragraphStyle(fTextView->ParagraphStyle());
|
||||
paragraphStyle.SetJustify(true);
|
||||
fTextView->SetParagraphStyle(paragraphStyle);
|
||||
ratingGroup.AddGlue();
|
||||
ratingGroup.End();
|
||||
|
||||
fTextView->SetText(rating.Comment());
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2016, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* Copyright 2016-2018, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* 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);
|
||||
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;
|
||||
}
|
||||
if (result.FindString("naturalLanguageCode",
|
||||
&fCommentLanguage) == B_OK) {
|
||||
BMenuItem* item = fCommentLanguageField->Menu()->ItemAt(
|
||||
fModel.SupportedLanguages().IndexOf(fCommentLanguage));
|
||||
if (item != NULL)
|
||||
item->SetMarked(true);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2018, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* 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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2018, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
|
||||
* Copyright 2018, Andrew Lindesay <apl@lindesay.co.nz>.
|
||||
* 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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user