From 01339a54db48e4aaa7624b2d2159a8cbd90bf197 Mon Sep 17 00:00:00 2001 From: Andrew Lindesay Date: Tue, 9 Jul 2019 12:23:14 +0100 Subject: [PATCH] HaikuDepot: Display User Usage Conditions This change will allow the user to view the user usage conditions from the HDS system in a HaikuDepot window. The display of the text is not currently well formatted in that the Markdown is not yet properly parsed, but the display of the data is working. Relates to 15209 Change-Id: Ia6ad4ef995f5fe3c29c40221964e44d4554a033d Reviewed-on: https://review.haiku-os.org/c/haiku/+/1750 Reviewed-by: waddlesplash --- src/apps/haikudepot/HaikuDepotConstants.h | 3 + src/apps/haikudepot/Jamfile | 2 + src/apps/haikudepot/model/LocalIconStore.h | 2 - .../haikudepot/model/UserUsageConditions.cpp | 100 ++++++ .../haikudepot/model/UserUsageConditions.h | 37 +++ .../haikudepot/server/WebAppInterface.cpp | 161 +++++++-- src/apps/haikudepot/server/WebAppInterface.h | 12 + src/apps/haikudepot/ui/MainWindow.cpp | 52 ++- src/apps/haikudepot/ui/MainWindow.h | 2 + src/apps/haikudepot/ui/RatePackageWindow.cpp | 2 +- .../ui/UserUsageConditionsWindow.cpp | 314 ++++++++++++++++++ .../haikudepot/ui/UserUsageConditionsWindow.h | 66 ++++ 12 files changed, 705 insertions(+), 48 deletions(-) create mode 100644 src/apps/haikudepot/model/UserUsageConditions.cpp create mode 100644 src/apps/haikudepot/model/UserUsageConditions.h create mode 100644 src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp create mode 100644 src/apps/haikudepot/ui/UserUsageConditionsWindow.h diff --git a/src/apps/haikudepot/HaikuDepotConstants.h b/src/apps/haikudepot/HaikuDepotConstants.h index 9671451c7e..97014a1d20 100644 --- a/src/apps/haikudepot/HaikuDepotConstants.h +++ b/src/apps/haikudepot/HaikuDepotConstants.h @@ -27,6 +27,9 @@ enum { #define RATING_MIN 0.0f +#define RGB_COLOR_WHITE (rgb_color) { 255, 255, 255, 255 } + + #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) diff --git a/src/apps/haikudepot/Jamfile b/src/apps/haikudepot/Jamfile index dbc2ef984f..f6912f45a9 100644 --- a/src/apps/haikudepot/Jamfile +++ b/src/apps/haikudepot/Jamfile @@ -143,6 +143,8 @@ local applicationSources = ScrollableGroupView.cpp SharedBitmap.cpp UserLoginWindow.cpp + UserUsageConditions.cpp + UserUsageConditionsWindow.cpp WorkStatusView.cpp # network + server / local processes diff --git a/src/apps/haikudepot/model/LocalIconStore.h b/src/apps/haikudepot/model/LocalIconStore.h index 5745d21137..008b1fcf95 100644 --- a/src/apps/haikudepot/model/LocalIconStore.h +++ b/src/apps/haikudepot/model/LocalIconStore.h @@ -2,8 +2,6 @@ * Copyright 2017, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ - - #ifndef LOCAL_ICON_STORE_H #define LOCAL_ICON_STORE_H diff --git a/src/apps/haikudepot/model/UserUsageConditions.cpp b/src/apps/haikudepot/model/UserUsageConditions.cpp new file mode 100644 index 0000000000..24fc6fdf54 --- /dev/null +++ b/src/apps/haikudepot/model/UserUsageConditions.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 2019, Andrew Lindesay . + * + * All rights reserved. Distributed under the terms of the MIT License. + */ +#include "UserUsageConditions.h" + + +// These are keys that are used to store this object's data into a BMessage +// instance. + +#define KEY_COPY_MARKDOWN "copyMarkdown" +#define KEY_CODE "code" +#define KEY_MINIMUM_AGE "minimumAge" + + +UserUsageConditions::UserUsageConditions(BMessage* from) + : + fCode(""), + fCopyMarkdown(""), + fMinimumAge(0) +{ + int16 minimumAge; + + if (from->FindInt16(KEY_MINIMUM_AGE, &minimumAge) != B_OK) + printf("expected key [%s] in the message data\n", KEY_MINIMUM_AGE); + fMinimumAge = (uint8) minimumAge; + + if (from->FindString(KEY_CODE, &fCode) != B_OK) + printf("expected key [%s] in the message data\n", KEY_CODE); + + if (from->FindString(KEY_COPY_MARKDOWN, &fCopyMarkdown) != B_OK) + printf("expected key [%s] in the message data\n", KEY_COPY_MARKDOWN); +} + + +UserUsageConditions::UserUsageConditions() + : + fCode(""), + fCopyMarkdown(""), + fMinimumAge(0) +{ +} + + +UserUsageConditions::~UserUsageConditions() +{ +} + + +const BString& +UserUsageConditions::Code() const +{ + return fCode; +} + + +const uint8 +UserUsageConditions::MinimumAge() const +{ + return fMinimumAge; +} + + +const BString& +UserUsageConditions::CopyMarkdown() const +{ + return fCopyMarkdown; +} + + +void +UserUsageConditions::SetCode(const BString& code) +{ + fCode = code; +} + + +void +UserUsageConditions::SetMinimumAge(uint8 age) +{ + fMinimumAge = age; +} + + +void +UserUsageConditions::SetCopyMarkdown(const BString& copyMarkdown) +{ + fCopyMarkdown = copyMarkdown; +} + + +status_t +UserUsageConditions::Archive(BMessage* into, bool deep) const +{ + into->AddInt16(KEY_MINIMUM_AGE, (int16) fMinimumAge); + into->AddString(KEY_CODE, fCode); + into->AddString(KEY_COPY_MARKDOWN, fCopyMarkdown); + return B_OK; +} \ No newline at end of file diff --git a/src/apps/haikudepot/model/UserUsageConditions.h b/src/apps/haikudepot/model/UserUsageConditions.h new file mode 100644 index 0000000000..5dffca35e4 --- /dev/null +++ b/src/apps/haikudepot/model/UserUsageConditions.h @@ -0,0 +1,37 @@ +/* + * Copyright 2019, Andrew Lindesay . + * + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef USER_USAGE_CONDITIONS_H +#define USER_USAGE_CONDITIONS_H + +#include + +#include +#include + + +class UserUsageConditions : public BArchivable { +public: + UserUsageConditions(BMessage* from); + UserUsageConditions(); + virtual ~UserUsageConditions(); + + const BString& Code() const; + const uint8 MinimumAge() const; + const BString& CopyMarkdown() const; + + void SetCode(const BString& code); + void SetMinimumAge(uint8 age); + void SetCopyMarkdown(const BString& copyMarkdown); + + status_t Archive(BMessage* into, bool deep = true) const; +private: + BString fCode; + BString fCopyMarkdown; + uint8 fMinimumAge; +}; + + +#endif // USER_USAGE_CONDITIONS_H diff --git a/src/apps/haikudepot/server/WebAppInterface.cpp b/src/apps/haikudepot/server/WebAppInterface.cpp index 30231264a3..3928b50d45 100644 --- a/src/apps/haikudepot/server/WebAppInterface.cpp +++ b/src/apps/haikudepot/server/WebAppInterface.cpp @@ -411,6 +411,102 @@ WebAppInterface::RetrieveUserRating(const BString& packageName, } +/*! \brief Returns data relating to the user usage conditions + + \param code defines the version of the data to return or if empty then the + latest is returned. + + This method will go to the server and get details relating to the user usage + conditions. It does this in two API calls; first gets the details (the + minimum age) and in the second call, the text of the conditions is returned. +*/ + +status_t +WebAppInterface::RetrieveUserUsageConditions(const BString& code, + UserUsageConditions& conditions) +{ + BMessage responseEnvelopeMessage; + status_t result = _RetrieveUserUsageConditionsMeta(code, + responseEnvelopeMessage); + + if (result != B_OK) + return result; + + BMessage resultMessage; + if (responseEnvelopeMessage.FindMessage("result", &resultMessage) != B_OK) { + fprintf(stderr, "bad response envelope missing 'result' entry\n"); + return B_BAD_DATA; + } + + BString metaDataCode; + double metaDataMinimumAge; + BString copyMarkdown; + + if ( (resultMessage.FindString("code", &metaDataCode) != B_OK) + || (resultMessage.FindDouble( + "minimumAge", &metaDataMinimumAge) != B_OK) ) { + printf("unexpected response from server with missing user usage " + "conditions data\n"); + return B_BAD_DATA; + } + + BMallocIO* copyMarkdownData = new BMallocIO(); + result = _RetrieveUserUsageConditionsCopy(metaDataCode, copyMarkdownData); + + if (result != B_OK) + return result; + + conditions.SetCode(metaDataCode); + conditions.SetMinimumAge(metaDataMinimumAge); + conditions.SetCopyMarkdown( + BString(static_cast(copyMarkdownData->Buffer()), + copyMarkdownData->BufferLength())); + + return B_OK; +} + + +status_t +WebAppInterface::_RetrieveUserUsageConditionsMeta(const BString& code, + BMessage& message) +{ + BMallocIO* requestEnvelopeData = new BMallocIO(); + BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); + + requestEnvelopeWriter.WriteObjectStart(); + _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter, + "getUserUsageConditions"); + requestEnvelopeWriter.WriteObjectName("params"); + requestEnvelopeWriter.WriteArrayStart(); + + requestEnvelopeWriter.WriteObjectStart(); + + if (!code.IsEmpty()) { + requestEnvelopeWriter.WriteObjectName("code"); + requestEnvelopeWriter.WriteString(code.String()); + } + + requestEnvelopeWriter.WriteObjectEnd(); + requestEnvelopeWriter.WriteArrayEnd(); + requestEnvelopeWriter.WriteObjectEnd(); + + // now fetch this information into an object. + + return _SendJsonRequest("user", requestEnvelopeData, + _LengthAndSeekToZero(requestEnvelopeData), 0, message); +} + + +status_t +WebAppInterface::_RetrieveUserUsageConditionsCopy(const BString& code, + BDataIO* stream) +{ + return _SendRawGetRequest( + BString("/__user/usageconditions/") << code << "/document.md", + stream); +} + + status_t WebAppInterface::CreateUserRating(const BString& packageName, const BPackageVersion& version, @@ -555,36 +651,9 @@ status_t WebAppInterface::RetrieveScreenshot(const BString& code, int32 width, int32 height, BDataIO* stream) { - BUrl url = ServerSettings::CreateFullUrl( + return _SendRawGetRequest( BString("/__pkgscreenshot/") << code << ".png" << "?tw=" - << width << "&th=" << height); - - bool isSecure = url.Protocol() == "https"; - - ProtocolListener listener(Logger::IsTraceEnabled()); - listener.SetDownloadIO(stream); - - BHttpHeaders headers; - ServerSettings::AugmentHeaders(headers); - - BHttpRequest request(url, isSecure, "HTTP", &listener); - request.SetMethod(B_HTTP_GET); - request.SetHeaders(headers); - - thread_id thread = request.Run(); - wait_for_thread(thread, NULL); - - const BHttpResult& result = dynamic_cast( - request.Result()); - - int32 statusCode = result.StatusCode(); - - if (statusCode == 200) - return B_OK; - - fprintf(stderr, "failed to get screenshot from '%s': %" B_PRIi32 "\n", - url.UrlString().String(), statusCode); - return B_ERROR; + << width << "&th=" << height, stream); } @@ -830,6 +899,40 @@ WebAppInterface::_SendJsonRequest(const char* domain, const BString& jsonString, } +status_t +WebAppInterface::_SendRawGetRequest(const BString urlPathComponents, + BDataIO* stream) +{ + BUrl url = ServerSettings::CreateFullUrl(urlPathComponents); + bool isSecure = url.Protocol() == "https"; + + ProtocolListener listener(Logger::IsTraceEnabled()); + listener.SetDownloadIO(stream); + + BHttpHeaders headers; + ServerSettings::AugmentHeaders(headers); + + BHttpRequest request(url, isSecure, "HTTP", &listener); + request.SetMethod(B_HTTP_GET); + request.SetHeaders(headers); + + thread_id thread = request.Run(); + wait_for_thread(thread, NULL); + + const BHttpResult& result = dynamic_cast( + request.Result()); + + int32 statusCode = result.StatusCode(); + + if (statusCode == 200) + return B_OK; + + fprintf(stderr, "failed to get data from '%s': %" B_PRIi32 "\n", + url.UrlString().String(), statusCode); + return B_ERROR; +} + + void WebAppInterface::_LogPayload(BPositionIO* requestData, size_t size) { diff --git a/src/apps/haikudepot/server/WebAppInterface.h b/src/apps/haikudepot/server/WebAppInterface.h index 4b99fa231f..4eb8ac29d4 100644 --- a/src/apps/haikudepot/server/WebAppInterface.h +++ b/src/apps/haikudepot/server/WebAppInterface.h @@ -13,6 +13,7 @@ #include #include "List.h" +#include "UserUsageConditions.h" class BDataIO; @@ -88,6 +89,10 @@ public: int rating, bool active, BMessage& message); + status_t RetrieveUserUsageConditions( + const BString& code, + UserUsageConditions& conditions); + status_t RetrieveScreenshot( const BString& code, int32 width, int32 height, @@ -110,6 +115,13 @@ public: static int32 ErrorCodeFromResponse(BMessage& response); private: + status_t _SendRawGetRequest( + const BString urlPathComponents, + BDataIO* stream); + status_t _RetrieveUserUsageConditionsMeta( + const BString& code, BMessage& message); + status_t _RetrieveUserUsageConditionsCopy( + const BString& code, BDataIO* stream); void _WriteStandardJsonRpcEnvelopeValues( BJsonWriter& writer, const char* methodName); diff --git a/src/apps/haikudepot/ui/MainWindow.cpp b/src/apps/haikudepot/ui/MainWindow.cpp index e0a32ec567..270623045f 100644 --- a/src/apps/haikudepot/ui/MainWindow.cpp +++ b/src/apps/haikudepot/ui/MainWindow.cpp @@ -48,6 +48,7 @@ #include "support.h" #include "ScreenshotWindow.h" #include "UserLoginWindow.h" +#include "UserUsageConditionsWindow.h" #include "WorkStatusView.h" @@ -56,23 +57,24 @@ enum { - MSG_BULK_LOAD_DONE = 'mmwd', - MSG_REFRESH_REPOS = 'mrrp', - MSG_MANAGE_REPOS = 'mmrp', - MSG_SOFTWARE_UPDATER = 'mswu', - MSG_LOG_IN = 'lgin', - MSG_LOG_OUT = 'lgot', - MSG_AUTHORIZATION_CHANGED = 'athc', - MSG_CATEGORIES_LIST_CHANGED = 'clic', - MSG_PACKAGE_CHANGED = 'pchd', - MSG_WORK_STATUS_CHANGE = 'wsch', - MSG_WORK_STATUS_CLEAR = 'wscl', + MSG_BULK_LOAD_DONE = 'mmwd', + MSG_REFRESH_REPOS = 'mrrp', + MSG_MANAGE_REPOS = 'mmrp', + MSG_SOFTWARE_UPDATER = 'mswu', + MSG_LOG_IN = 'lgin', + MSG_LOG_OUT = 'lgot', + MSG_AUTHORIZATION_CHANGED = 'athc', + MSG_CATEGORIES_LIST_CHANGED = 'clic', + MSG_PACKAGE_CHANGED = 'pchd', + MSG_WORK_STATUS_CHANGE = 'wsch', + MSG_WORK_STATUS_CLEAR = 'wscl', + MSG_VIEW_LATEST_USER_USAGE_CONDITIONS = 'vluc', - MSG_SHOW_FEATURED_PACKAGES = 'sofp', - MSG_SHOW_AVAILABLE_PACKAGES = 'savl', - MSG_SHOW_INSTALLED_PACKAGES = 'sins', - MSG_SHOW_SOURCE_PACKAGES = 'ssrc', - MSG_SHOW_DEVELOP_PACKAGES = 'sdvl' + MSG_SHOW_FEATURED_PACKAGES = 'sofp', + MSG_SHOW_AVAILABLE_PACKAGES = 'savl', + MSG_SHOW_INSTALLED_PACKAGES = 'sins', + MSG_SHOW_SOURCE_PACKAGES = 'ssrc', + MSG_SHOW_DEVELOP_PACKAGES = 'sdvl' }; @@ -334,6 +336,10 @@ MainWindow::MessageReceived(BMessage* message) fModel.SetUsername(""); break; + case MSG_VIEW_LATEST_USER_USAGE_CONDITIONS: + _ViewLatestUserUsageConditions(); + break; + case MSG_AUTHORIZATION_CHANGED: _UpdateAuthorization(); break; @@ -734,6 +740,11 @@ MainWindow::_BuildUserMenu(BMenuBar* menuBar) new BMessage(MSG_LOG_OUT)); fUserMenu->AddItem(fLogOutItem); + fLogOutItem = new BMenuItem(B_TRANSLATE("View latest user usage conditions" + B_UTF8_ELLIPSIS), + new BMessage(MSG_VIEW_LATEST_USER_USAGE_CONDITIONS)); + fUserMenu->AddItem(fLogOutItem); + menuBar->AddItem(fUserMenu); } @@ -1332,3 +1343,12 @@ MainWindow::_ShowScreenshot() fScreenshotWindow->Unlock(); } + + +void +MainWindow::_ViewLatestUserUsageConditions() +{ + UserUsageConditionsWindow* window = new UserUsageConditionsWindow( + this, BRect(0, 0, 500, 400), fModel, LATEST); + window->Show(); +} \ No newline at end of file diff --git a/src/apps/haikudepot/ui/MainWindow.h b/src/apps/haikudepot/ui/MainWindow.h index 9210824bcf..841b2800ff 100644 --- a/src/apps/haikudepot/ui/MainWindow.h +++ b/src/apps/haikudepot/ui/MainWindow.h @@ -101,6 +101,8 @@ private: void _RatePackage(); void _ShowScreenshot(); + void _ViewLatestUserUsageConditions(); + private: FilterView* fFilterView; TabView* fListTabs; diff --git a/src/apps/haikudepot/ui/RatePackageWindow.cpp b/src/apps/haikudepot/ui/RatePackageWindow.cpp index e4a7c4aba4..1743043d29 100644 --- a/src/apps/haikudepot/ui/RatePackageWindow.cpp +++ b/src/apps/haikudepot/ui/RatePackageWindow.cpp @@ -465,7 +465,7 @@ RatePackageWindow::_SetWorkerThread(thread_id thread) } -int32 +/*static*/ int32 RatePackageWindow::_QueryRatingThreadEntry(void* data) { RatePackageWindow* window = reinterpret_cast(data); diff --git a/src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp b/src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp new file mode 100644 index 0000000000..829e08d2a2 --- /dev/null +++ b/src/apps/haikudepot/ui/UserUsageConditionsWindow.cpp @@ -0,0 +1,314 @@ +/* + * Copyright 2019, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "UserUsageConditionsWindow.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "AppUtils.h" +#include "BarberPole.h" +#include "HaikuDepotConstants.h" +#include "Logger.h" +#include "MarkupTextView.h" +#include "Model.h" +#include "UserUsageConditions.h" +#include "WebAppInterface.h" + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "UserUsageConditions" + +#define PLACEHOLDER_TEXT "..." + +#define INTRODUCTION_TEXT_LATEST "HaikuDepot communicates with a " \ + "sever component called HaikuDepotServer. These are the latest user " \ + "usage conditions for use of the HaikuDepotServer service." + +#define INTRODUCTION_TEXT_USER "HaikuDepot communicates with a " \ + "sever component called HaikuDepotServer. These are the user usage " \ + "conditions that the user has agreed to in relation to the use of the " \ + "HaikuDepotServer service." + +/*! This is the anticipated number of lines of test that appear in the + introduction. +*/ + +#define LINES_INTRODUCTION_TEXT 2 + + +enum { + MSG_USER_USAGE_CONDITIONS_DATA = 'uucd', + MSG_USER_USAGE_CONDITIONS_ERROR = 'uuce' +}; + + +UserUsageConditionsWindow::UserUsageConditionsWindow(BWindow* parent, + BRect frame, Model& model, UserUsageConditionsSelectionMode mode) + : + BWindow(frame, B_TRANSLATE("User Usage Conditions"), + B_FLOATING_WINDOW_LOOK, B_FLOATING_SUBSET_WINDOW_FEEL, + B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS + | B_NOT_RESIZABLE | B_NOT_ZOOMABLE), + fMode(mode), + fModel(model), + fWorkerThread(-1) +{ + AddToSubset(parent); + + if (mode != LATEST) + debugger("only the LATEST user usage conditions are handled for now"); + + fWorkerIndicator = new BarberPole("fetch data worker indicator"); + BSize workerIndicatorSize; + workerIndicatorSize.SetHeight(20); + fWorkerIndicator->SetExplicitMinSize(workerIndicatorSize); + + fCopyView = new MarkupTextView("copy view"); + fCopyView->SetViewUIColor(B_NO_COLOR); + fCopyView->SetLowColor(RGB_COLOR_WHITE); + fCopyView->SetInsets(8.0f); + + BScrollView* scrollView = new BScrollView("copy scroll view", fCopyView, + 0, false, true, B_PLAIN_BORDER); + + BTextView* introductionTextView = new BTextView("introduction text view"); + introductionTextView->AdoptSystemColors(); + introductionTextView->MakeEditable(false); + introductionTextView->MakeSelectable(false); + introductionTextView->SetText(B_TRANSLATE(_IntroductionTextForMode(mode))); + + fAgeNoteStringView = new BStringView("age note string view", + PLACEHOLDER_TEXT); + fAgeNoteStringView->AdoptSystemColors(); + fAgeNoteStringView->SetExplicitMaxSize( + BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + + BFont versionFont(be_plain_font); + versionFont.SetSize(9.0); + + fVersionStringView = new BStringView("version string view", + PLACEHOLDER_TEXT); + fVersionStringView->AdoptSystemColors(); + fVersionStringView->SetFont(&versionFont); + fVersionStringView->SetAlignment(B_ALIGN_RIGHT); + fVersionStringView->SetHighUIColor(B_PANEL_TEXT_COLOR, B_DARKEN_3_TINT); + fVersionStringView->SetExplicitMaxSize( + BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + + BSize introductionSize; + introductionSize.SetHeight( + _ExpectedIntroductionTextHeight(introductionTextView)); + introductionTextView->SetExplicitPreferredSize(introductionSize); + + BButton* okButton = new BButton("ok", B_TRANSLATE("OK"), + new BMessage(B_QUIT_REQUESTED)); + + BLayoutBuilder::Group<>(this, B_VERTICAL) + .SetInsets(B_USE_WINDOW_INSETS) + .Add(introductionTextView, 1) + .AddGlue() + .Add(fVersionStringView, 1) + .Add(scrollView, 96) + .Add(fAgeNoteStringView, 1) + .AddGroup(B_HORIZONTAL, 1) + .AddGlue() + .Add(okButton) + .End() + .Add(fWorkerIndicator, 1) + .End(); + + CenterIn(parent->Frame()); + + _FetchData(); + // start a new thread to pull down the user usage conditions data. +} + + +UserUsageConditionsWindow::~UserUsageConditionsWindow() +{ +} + + +void +UserUsageConditionsWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case MSG_USER_USAGE_CONDITIONS_DATA: + { + UserUsageConditions data(message); + _DisplayData(data); + fWorkerIndicator->Stop(); + break; + } + default: + BWindow::MessageReceived(message); + break; + } +} + + +bool +UserUsageConditionsWindow::QuitRequested() +{ + // for now we just don't allow the quit when the background thread + // is processing. In the future it would be good if the HTTP + // requests were re-organized such that cancellations were easier to + // implement. + + if (fWorkerThread == -1) + return true; + if (Logger::IsInfoEnabled()) { + fprintf(stderr, "unable to quit when the user usage " + "conditions window is still fetching data\n"); + } + return false; +} + + +/*! This method is called on the main thread in order to initiate the background + processing to obtain the user usage conditions data. It will take + responsibility for coordinating the creation of the thread and starting the + thread etc... +*/ + +void +UserUsageConditionsWindow::_FetchData() +{ + if (-1 != fWorkerThread) + debugger("illegal state - attempt to fetch, but fetch in progress"); + thread_id thread = spawn_thread(&_FetchDataThreadEntry, + "Fetch user usage conditions data", B_NORMAL_PRIORITY, this); + if (thread >= 0) { + fWorkerIndicator->Start(); + _SetWorkerThread(thread); + resume_thread(fWorkerThread); + } else { + debugger("unable to start a thread to fetch the user usage " + "conditions."); + } +} + + +/*! This method is called from the thread in order to start the thread; it is + the entry-point for the background processing to obtain the user usage + conditions. +*/ + +/*static*/ int32 +UserUsageConditionsWindow::_FetchDataThreadEntry(void* data) +{ + UserUsageConditionsWindow* win + = reinterpret_cast(data); + win->_FetchDataPerform(); + return 0; +} + + +/*! This method will perform the task of obtaining data about the user usage + conditions. +*/ + +void +UserUsageConditionsWindow::_FetchDataPerform() +{ + UserUsageConditions conditions; + WebAppInterface interface = fModel.GetWebAppInterface(); + + if (interface.RetrieveUserUsageConditions(NULL, conditions) == B_OK) { + BMessage dataMessage(MSG_USER_USAGE_CONDITIONS_DATA); + conditions.Archive(&dataMessage, true); + BMessenger(this).SendMessage(&dataMessage); + } else { + AppUtils::NotifySimpleError( + B_TRANSLATE("User Usage Conditions Download Problem"), + B_TRANSLATE("An error has arisen downloading the user usage " + "conditions. Check the log for details and try again.")); + BMessenger(this).SendMessage(B_QUIT_REQUESTED); + } + + _SetWorkerThread(-1); +} + + +void +UserUsageConditionsWindow::_SetWorkerThread(thread_id thread) +{ + if (!Lock()) { + if (Logger::IsInfoEnabled()) + fprintf(stderr, "failed to lock window\n"); + } else { + fWorkerThread = thread; + Unlock(); + } +} + + +void +UserUsageConditionsWindow::_DisplayData(const UserUsageConditions& data) +{ + fCopyView->SetText(data.CopyMarkdown()); + fAgeNoteStringView->SetText(_MinimumAgeText(data.MinimumAge())); + fVersionStringView->SetText(_VersionText(data.Code())); +} + + +/*static*/ const BString +UserUsageConditionsWindow::_VersionText(const BString& code) +{ + BString versionText( + B_TRANSLATE("Version %Code%")); + versionText.ReplaceAll("%Code%", code); + return versionText; +} + + +/*static*/ const BString +UserUsageConditionsWindow::_MinimumAgeText(uint8 minimumAge) +{ + BString minimumAgeString; + minimumAgeString.SetToFormat("%" B_PRId8, minimumAge); + BString ageNoteText( + B_TRANSLATE("Users are required to be %AgeYears% years of age or " + "older.")); + ageNoteText.ReplaceAll("%AgeYears%", minimumAgeString); + return ageNoteText; +} + + +/*static*/ const BString +UserUsageConditionsWindow::_IntroductionTextForMode( + UserUsageConditionsSelectionMode mode) +{ + switch (mode) { + case LATEST: + return INTRODUCTION_TEXT_LATEST; + case USER: + return INTRODUCTION_TEXT_USER; + default: + return "???"; + } +} + + +/*static*/ float +UserUsageConditionsWindow::_ExpectedIntroductionTextHeight( + BTextView* introductionTextView) +{ + float insetTop; + float insetBottom; + introductionTextView->GetInsets(NULL, &insetTop, NULL, &insetBottom); + + BSize introductionSize; + font_height fh; + be_plain_font->GetHeight(&fh); + return ((fh.ascent + fh.descent + fh.leading) * LINES_INTRODUCTION_TEXT) + + insetTop + insetBottom; +} diff --git a/src/apps/haikudepot/ui/UserUsageConditionsWindow.h b/src/apps/haikudepot/ui/UserUsageConditionsWindow.h new file mode 100644 index 0000000000..07c4fdcaff --- /dev/null +++ b/src/apps/haikudepot/ui/UserUsageConditionsWindow.h @@ -0,0 +1,66 @@ +/* + * Copyright 2019, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef USER_USAGE_CONDITIONS_WINDOW_H +#define USER_USAGE_CONDITIONS_WINDOW_H + +#include +#include +#include + +#include "PackageInfo.h" +#include "UserUsageConditions.h" + + +class BarberPole; +class BTextView; +class BStringView; +class MarkupTextView; +class Model; + + +enum UserUsageConditionsSelectionMode { + LATEST = 1, + USER = 2 +}; + + +class UserUsageConditionsWindow : public BWindow { +public: + UserUsageConditionsWindow(BWindow* parent, + BRect frame, Model& model, + UserUsageConditionsSelectionMode mode); + virtual ~UserUsageConditionsWindow(); + + virtual void MessageReceived(BMessage* message); + virtual bool QuitRequested(); + +private: + static const BString _VersionText(const BString& code); + static const BString _MinimumAgeText(uint8 minimumAge); + static const BString _IntroductionTextForMode( + UserUsageConditionsSelectionMode mode); + static float _ExpectedIntroductionTextHeight( + BTextView* introductionTextView); + + void _DisplayData(const UserUsageConditions& data); + + void _FetchData(); + void _SetWorkerThread(thread_id thread); + static int32 _FetchDataThreadEntry(void* data); + void _FetchDataPerform(); + +private: + UserUsageConditionsSelectionMode + fMode; + MarkupTextView* fCopyView; + Model& fModel; + BStringView* fAgeNoteStringView; + BStringView* fVersionStringView; + BarberPole* fWorkerIndicator; + thread_id fWorkerThread; +}; + + +#endif // USER_USAGE_CONDITIONS_WINDOW_H