Merge lp:~alecu/unity-scope-click/refunds-previews into lp:unity-scope-click
- refunds-previews
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | dobey |
Approved revision: | 328 |
Merged at revision: | 327 |
Proposed branch: | lp:~alecu/unity-scope-click/refunds-previews |
Merge into: | lp:unity-scope-click |
Prerequisite: | lp:~alecu/unity-scope-click/split-close-preview |
Diff against target: |
742 lines (+438/-23) 9 files modified
libclickscope/click/pay.cpp (+33/-3) libclickscope/click/pay.h (+4/-3) libclickscope/click/preview.cpp (+138/-4) libclickscope/click/preview.h (+35/-3) libclickscope/tests/test_preview.cpp (+199/-6) scope/clickapps/apps-scope.cpp (+6/-0) scope/clickstore/store-query.cpp (+13/-4) scope/clickstore/store-query.h (+1/-0) scope/clickstore/store-scope.cpp (+9/-0) |
To merge this branch: | bzr merge lp:~alecu/unity-scope-click/refunds-previews |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
dobey (community) | Approve | ||
Charles Kerr (community) | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email: mp+257444@code.launchpad.net |
Commit message
Show the "Refund" button and call the pay-service when clicked
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
- 324. By Alejandro J. Cura
-
Do not use the refundable time for equality comparison
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:324
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Charles Kerr (charlesk) wrote : | # |
I think the branch needs to be resynced with trunk before silo.
Individual comments inline. Overall, looks good. I added a few optional suggestions but didn't see any showstoppers.
Charles Kerr (charlesk) : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:324
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 325. By Alejandro J. Cura
-
Merged trunk in
- 326. By Alejandro J. Cura
-
Fixes suggested by charles' review
- 327. By Alejandro J. Cura
-
Fix broken tests
Alejandro J. Cura (alecu) wrote : | # |
Thanks for the thorough review!
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:327
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
dobey (dobey) wrote : | # |
Some commentary in line.
Charles Kerr (charlesk) wrote : | # |
Thanks for making the minor tweaks I suggested :)
Still LGTM overall, I agree with dobey's suggestion and it got me thinking about another issue, comments inline.
- 328. By Alejandro J. Cura
-
Don't call parse_timestamp twice in a row.
Alejandro J. Cura (alecu) wrote : | # |
Thanks for the reviews, fixed as suggested.
dobey (dobey) : | # |
- 329. By Alejandro J. Cura
-
Fixed the type of the timestamp stored in result variant
- 330. By Alejandro J. Cura
-
Call libpay.refund() from the qt thread
- 331. By Alejandro J. Cura
-
Add more logging before and after calling libpay
- 332. By Alejandro J. Cura
-
Initialize pay package before starting a refund
- 333. By Alejandro J. Cura
-
Now correctly setting up the pay package
Preview Diff
1 | === modified file 'libclickscope/click/pay.cpp' | |||
2 | --- libclickscope/click/pay.cpp 2015-04-10 13:13:46 +0000 | |||
3 | +++ libclickscope/click/pay.cpp 2015-05-29 21:20:25 +0000 | |||
4 | @@ -82,7 +82,16 @@ | |||
5 | 82 | namespace pay { | 82 | namespace pay { |
6 | 83 | 83 | ||
7 | 84 | bool operator==(const Purchase& lhs, const Purchase& rhs) { | 84 | bool operator==(const Purchase& lhs, const Purchase& rhs) { |
9 | 85 | return lhs.name == rhs.name && lhs.refundable_until == rhs.refundable_until; | 85 | return lhs.name == rhs.name; |
10 | 86 | } | ||
11 | 87 | |||
12 | 88 | Package& Package::instance() { | ||
13 | 89 | static Package the_instance; | ||
14 | 90 | return the_instance; | ||
15 | 91 | } | ||
16 | 92 | |||
17 | 93 | Package::Package() : impl(new Private()) | ||
18 | 94 | { | ||
19 | 86 | } | 95 | } |
20 | 87 | 96 | ||
21 | 88 | Package::Package(const QSharedPointer<click::web::Client>& client) : | 97 | Package::Package(const QSharedPointer<click::web::Client>& client) : |
22 | @@ -101,6 +110,16 @@ | |||
23 | 101 | } | 110 | } |
24 | 102 | } | 111 | } |
25 | 103 | 112 | ||
26 | 113 | bool Package::refund(const std::string& pkg_name) | ||
27 | 114 | { | ||
28 | 115 | if (!running) { | ||
29 | 116 | qDebug() << "pay service starting"; | ||
30 | 117 | setup_pay_service(); | ||
31 | 118 | } | ||
32 | 119 | qDebug() << "actually calling refund"; | ||
33 | 120 | return pay_package_item_start_refund(impl->pay_package, pkg_name.c_str()); | ||
34 | 121 | } | ||
35 | 122 | |||
36 | 104 | bool Package::verify(const std::string& pkg_name) | 123 | bool Package::verify(const std::string& pkg_name) |
37 | 105 | { | 124 | { |
38 | 106 | typedef std::pair<std::string, bool> _PurchasedTuple; | 125 | typedef std::pair<std::string, bool> _PurchasedTuple; |
39 | @@ -167,8 +186,12 @@ | |||
40 | 167 | const json::Value item = root[i]; | 186 | const json::Value item = root[i]; |
41 | 168 | if (item[JsonKeys::state].asString() == PURCHASE_STATE_COMPLETE) { | 187 | if (item[JsonKeys::state].asString() == PURCHASE_STATE_COMPLETE) { |
42 | 169 | auto package_name = item[JsonKeys::package_name].asString(); | 188 | auto package_name = item[JsonKeys::package_name].asString(); |
43 | 189 | qDebug() << "parsing:" << package_name.c_str(); | ||
44 | 170 | auto refundable_until_value = item[JsonKeys::refundable_until]; | 190 | auto refundable_until_value = item[JsonKeys::refundable_until]; |
46 | 171 | Purchase p(package_name, parse_timestamp(refundable_until_value)); | 191 | qDebug() << "refundable until:" << refundable_until_value.asString().c_str(); |
47 | 192 | auto refundable_parsed = parse_timestamp(refundable_until_value); | ||
48 | 193 | qDebug() << "parsed:" << refundable_parsed; | ||
49 | 194 | Purchase p(package_name, refundable_parsed); | ||
50 | 172 | purchases.insert(p); | 195 | purchases.insert(p); |
51 | 173 | } | 196 | } |
52 | 174 | } | 197 | } |
53 | @@ -195,10 +218,17 @@ | |||
54 | 195 | 218 | ||
55 | 196 | void Package::setup_pay_service() | 219 | void Package::setup_pay_service() |
56 | 197 | { | 220 | { |
58 | 198 | impl->pay_package = pay_package_new(Package::NAME); | 221 | qDebug() << "new package"; |
59 | 222 | PayPackage* newpkg = pay_package_new(Package::NAME); | ||
60 | 223 | qDebug() << "got package:" << newpkg; | ||
61 | 224 | qDebug() << "about to set it on impl:" << impl.isNull(); | ||
62 | 225 | fprintf(stderr, "and the package is at: %p\n", impl->pay_package); | ||
63 | 226 | impl->pay_package = newpkg; | ||
64 | 227 | qDebug() << "installing observer"; | ||
65 | 199 | pay_package_item_observer_install(impl->pay_package, | 228 | pay_package_item_observer_install(impl->pay_package, |
66 | 200 | pay_verification_observer, | 229 | pay_verification_observer, |
67 | 201 | this); | 230 | this); |
68 | 231 | qDebug() << "Flag we are running"; | ||
69 | 202 | running = true; | 232 | running = true; |
70 | 203 | } | 233 | } |
71 | 204 | 234 | ||
72 | 205 | 235 | ||
73 | === modified file 'libclickscope/click/pay.h' | |||
74 | --- libclickscope/click/pay.h 2015-04-10 13:13:46 +0000 | |||
75 | +++ libclickscope/click/pay.h 2015-05-29 21:20:25 +0000 | |||
76 | @@ -92,21 +92,22 @@ | |||
77 | 92 | public: | 92 | public: |
78 | 93 | constexpr static const char* NAME{"click-scope"}; | 93 | constexpr static const char* NAME{"click-scope"}; |
79 | 94 | 94 | ||
81 | 95 | Package() = default; | 95 | Package(); |
82 | 96 | Package(const QSharedPointer<click::web::Client>& client); | 96 | Package(const QSharedPointer<click::web::Client>& client); |
83 | 97 | virtual ~Package(); | 97 | virtual ~Package(); |
84 | 98 | 98 | ||
85 | 99 | virtual bool verify(const std::string& pkg_name); | 99 | virtual bool verify(const std::string& pkg_name); |
86 | 100 | virtual click::web::Cancellable get_purchases(std::function<void(const PurchaseSet& purchased_apps)> callback); | 100 | virtual click::web::Cancellable get_purchases(std::function<void(const PurchaseSet& purchased_apps)> callback); |
88 | 101 | 101 | virtual bool refund(const std::string& pkg_name); | |
89 | 102 | static std::string get_base_url(); | 102 | static std::string get_base_url(); |
90 | 103 | static Package& instance(); | ||
91 | 103 | 104 | ||
92 | 104 | protected: | 105 | protected: |
93 | 105 | virtual void setup_pay_service(); | 106 | virtual void setup_pay_service(); |
94 | 106 | virtual void pay_package_verify(const std::string& pkg_name); | 107 | virtual void pay_package_verify(const std::string& pkg_name); |
95 | 107 | 108 | ||
96 | 108 | struct Private; | 109 | struct Private; |
98 | 109 | std::shared_ptr<pay::Package::Private> impl; | 110 | QScopedPointer<pay::Package::Private> impl; |
99 | 110 | 111 | ||
100 | 111 | bool running = false; | 112 | bool running = false; |
101 | 112 | QSharedPointer<click::web::Client> client; | 113 | QSharedPointer<click::web::Client> client; |
102 | 113 | 114 | ||
103 | === modified file 'libclickscope/click/preview.cpp' | |||
104 | --- libclickscope/click/preview.cpp 2015-04-10 14:24:10 +0000 | |||
105 | +++ libclickscope/click/preview.cpp 2015-05-29 21:20:25 +0000 | |||
106 | @@ -36,6 +36,7 @@ | |||
107 | 36 | #include <click/dbus_constants.h> | 36 | #include <click/dbus_constants.h> |
108 | 37 | #include <click/departments-db.h> | 37 | #include <click/departments-db.h> |
109 | 38 | #include <click/utils.h> | 38 | #include <click/utils.h> |
110 | 39 | #include <click/pay.h> | ||
111 | 39 | 40 | ||
112 | 40 | #include <boost/algorithm/string/replace.hpp> | 41 | #include <boost/algorithm/string/replace.hpp> |
113 | 41 | 42 | ||
114 | @@ -148,10 +149,16 @@ | |||
115 | 148 | << " given with download_url" << QString::fromStdString(download_url); | 149 | << " given with download_url" << QString::fromStdString(download_url); |
116 | 149 | return new UninstalledPreview(result, client, depts, nam); | 150 | return new UninstalledPreview(result, client, depts, nam); |
117 | 150 | } | 151 | } |
118 | 152 | } else if (metadict.count(click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED) != 0) { | ||
119 | 153 | return new CancelPurchasePreview(result, false); | ||
120 | 154 | } else if (metadict.count(click::Preview::Actions::CANCEL_PURCHASE_INSTALLED) != 0) { | ||
121 | 155 | return new CancelPurchasePreview(result, true); | ||
122 | 151 | } else if (metadict.count(click::Preview::Actions::UNINSTALL_CLICK) != 0) { | 156 | } else if (metadict.count(click::Preview::Actions::UNINSTALL_CLICK) != 0) { |
123 | 152 | return new UninstallConfirmationPreview(result); | 157 | return new UninstallConfirmationPreview(result); |
124 | 153 | } else if (metadict.count(click::Preview::Actions::CONFIRM_UNINSTALL) != 0) { | 158 | } else if (metadict.count(click::Preview::Actions::CONFIRM_UNINSTALL) != 0) { |
125 | 154 | return new UninstallingPreview(result, client, nam); | 159 | return new UninstallingPreview(result, client, nam); |
126 | 160 | } else if (metadict.count(click::Preview::Actions::CONFIRM_CANCEL_PURCHASE) != 0) { | ||
127 | 161 | return new CancellingPurchasePreview(result, client, nam); | ||
128 | 155 | } else if (metadict.count(click::Preview::Actions::RATED) != 0) { | 162 | } else if (metadict.count(click::Preview::Actions::RATED) != 0) { |
129 | 156 | return new InstalledPreview(result, metadata, client, depts); | 163 | return new InstalledPreview(result, metadata, client, depts); |
130 | 157 | } else if (metadict.count(click::Preview::Actions::SHOW_UNINSTALLED) != 0) { | 164 | } else if (metadict.count(click::Preview::Actions::SHOW_UNINSTALLED) != 0) { |
131 | @@ -522,6 +529,16 @@ | |||
132 | 522 | return widgets; | 529 | return widgets; |
133 | 523 | } | 530 | } |
134 | 524 | 531 | ||
135 | 532 | bool PreviewStrategy::isRefundable() const | ||
136 | 533 | { | ||
137 | 534 | time_t refundable_until = 0; | ||
138 | 535 | if (result.contains("refundable_until")) { | ||
139 | 536 | refundable_until = result["refundable_until"].get_int64_t(); | ||
140 | 537 | } | ||
141 | 538 | time_t now = time(NULL); | ||
142 | 539 | // refund button is not shown if less than ten seconds left | ||
143 | 540 | return refundable_until >= (now + 10); | ||
144 | 541 | } | ||
145 | 525 | 542 | ||
146 | 526 | // class DownloadErrorPreview | 543 | // class DownloadErrorPreview |
147 | 527 | 544 | ||
148 | @@ -747,10 +764,17 @@ | |||
149 | 747 | } | 764 | } |
150 | 748 | if (manifest.removable) | 765 | if (manifest.removable) |
151 | 749 | { | 766 | { |
156 | 750 | builder.add_tuple({ | 767 | if (!isRefundable()) { |
157 | 751 | {"id", scopes::Variant(click::Preview::Actions::UNINSTALL_CLICK)}, | 768 | builder.add_tuple({ |
158 | 752 | {"label", scopes::Variant(_("Uninstall"))} | 769 | {"id", scopes::Variant(click::Preview::Actions::UNINSTALL_CLICK)}, |
159 | 753 | }); | 770 | {"label", scopes::Variant(_("Uninstall"))} |
160 | 771 | }); | ||
161 | 772 | } else { | ||
162 | 773 | builder.add_tuple({ | ||
163 | 774 | {"id", scopes::Variant(click::Preview::Actions::CANCEL_PURCHASE_INSTALLED)}, | ||
164 | 775 | {"label", scopes::Variant(_("Cancel Purchase"))} | ||
165 | 776 | }); | ||
166 | 777 | } | ||
167 | 754 | } | 778 | } |
168 | 755 | if (!uri.empty() || manifest.removable) { | 779 | if (!uri.empty() || manifest.removable) { |
169 | 756 | buttons.add_attribute_value("actions", builder.end()); | 780 | buttons.add_attribute_value("actions", builder.end()); |
170 | @@ -852,6 +876,69 @@ | |||
171 | 852 | return widgets; | 876 | return widgets; |
172 | 853 | } | 877 | } |
173 | 854 | 878 | ||
174 | 879 | // class CancelPurchasePreview | ||
175 | 880 | |||
176 | 881 | CancelPurchasePreview::CancelPurchasePreview(const unity::scopes::Result& result, bool installed) | ||
177 | 882 | : PreviewStrategy(result), installed(installed) | ||
178 | 883 | { | ||
179 | 884 | } | ||
180 | 885 | |||
181 | 886 | CancelPurchasePreview::~CancelPurchasePreview() | ||
182 | 887 | { | ||
183 | 888 | } | ||
184 | 889 | |||
185 | 890 | scopes::PreviewWidgetList CancelPurchasePreview::build_widgets() | ||
186 | 891 | { | ||
187 | 892 | scopes::PreviewWidgetList widgets; | ||
188 | 893 | |||
189 | 894 | scopes::PreviewWidget confirmation("confirmation", "text"); | ||
190 | 895 | |||
191 | 896 | std::string title = result["title"].get_string(); | ||
192 | 897 | // TRANSLATORS: Do NOT translate ${title} here. | ||
193 | 898 | std::string message = | ||
194 | 899 | _("Are you sure you want to cancel the purchase of '${title}'? The app will be uninstalled."); | ||
195 | 900 | |||
196 | 901 | boost::replace_first(message, "${title}", title); | ||
197 | 902 | confirmation.add_attribute_value("text", scopes::Variant(message)); | ||
198 | 903 | widgets.push_back(confirmation); | ||
199 | 904 | |||
200 | 905 | scopes::PreviewWidget buttons("buttons", "actions"); | ||
201 | 906 | scopes::VariantBuilder builder; | ||
202 | 907 | |||
203 | 908 | auto action_no = installed ? click::Preview::Actions::SHOW_INSTALLED | ||
204 | 909 | : click::Preview::Actions::SHOW_UNINSTALLED; | ||
205 | 910 | |||
206 | 911 | builder.add_tuple({ | ||
207 | 912 | {"id", scopes::Variant(action_no)}, | ||
208 | 913 | {"label", scopes::Variant(_("No"))} | ||
209 | 914 | }); | ||
210 | 915 | builder.add_tuple({ | ||
211 | 916 | {"id", scopes::Variant(click::Preview::Actions::CONFIRM_CANCEL_PURCHASE)}, | ||
212 | 917 | {"label", scopes::Variant(_("Yes, cancel purchase"))} | ||
213 | 918 | }); | ||
214 | 919 | |||
215 | 920 | buttons.add_attribute_value("actions", builder.end()); | ||
216 | 921 | widgets.push_back(buttons); | ||
217 | 922 | |||
218 | 923 | scopes::PreviewWidget policy("policy", "text"); | ||
219 | 924 | policy.add_attribute_value("title", scopes::Variant{_("Returns and cancellation policy")}); | ||
220 | 925 | policy.add_attribute_value("text", scopes::Variant{ | ||
221 | 926 | _("When purchasing an app in the Ubuntu Store, you can cancel the charge within 15 minutes " | ||
222 | 927 | "after installation. If the cancel period has passed, we recommend contacting the app " | ||
223 | 928 | "developer directly for a refund.\n" | ||
224 | 929 | "You can find the developer’s contact information listed on the app’s preview page in the " | ||
225 | 930 | "Ubuntu Store.\n" | ||
226 | 931 | "Keep in mind that you cannot cancel the purchasing process of an app more than once.")}); | ||
227 | 932 | widgets.push_back(policy); | ||
228 | 933 | |||
229 | 934 | return widgets; | ||
230 | 935 | } | ||
231 | 936 | |||
232 | 937 | void CancelPurchasePreview::run(unity::scopes::PreviewReplyProxy const& reply) | ||
233 | 938 | { | ||
234 | 939 | // NOTE: no need to populateDetails() here. | ||
235 | 940 | reply->push(build_widgets()); | ||
236 | 941 | } | ||
237 | 855 | 942 | ||
238 | 856 | // class UninstallConfirmationPreview | 943 | // class UninstallConfirmationPreview |
239 | 857 | 944 | ||
240 | @@ -975,6 +1062,13 @@ | |||
241 | 975 | {"download_url", scopes::Variant(details.download_url)}, | 1062 | {"download_url", scopes::Variant(details.download_url)}, |
242 | 976 | {"download_sha512", scopes::Variant(details.download_sha512)}, | 1063 | {"download_sha512", scopes::Variant(details.download_sha512)}, |
243 | 977 | }); | 1064 | }); |
244 | 1065 | if (isRefundable()) { | ||
245 | 1066 | builder.add_tuple( | ||
246 | 1067 | { | ||
247 | 1068 | {"id", scopes::Variant(click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED)}, | ||
248 | 1069 | {"label", scopes::Variant(_("Cancel Purchase"))}, | ||
249 | 1070 | }); | ||
250 | 1071 | } | ||
251 | 978 | buttons.add_attribute_value("actions", builder.end()); | 1072 | buttons.add_attribute_value("actions", builder.end()); |
252 | 979 | oa_client.register_account_login_item(buttons, | 1073 | oa_client.register_account_login_item(buttons, |
253 | 980 | scopes::OnlineAccountClient::PostLoginAction::ContinueActivation, | 1074 | scopes::OnlineAccountClient::PostLoginAction::ContinueActivation, |
254 | @@ -1028,4 +1122,44 @@ | |||
255 | 1028 | } | 1122 | } |
256 | 1029 | 1123 | ||
257 | 1030 | 1124 | ||
258 | 1125 | // class CancellingPurchasePreview : public UninstallingPreview | ||
259 | 1126 | |||
260 | 1127 | CancellingPurchasePreview::CancellingPurchasePreview(const unity::scopes::Result& result, | ||
261 | 1128 | const QSharedPointer<click::web::Client>& client, | ||
262 | 1129 | const QSharedPointer<click::network::AccessManager>& nam) | ||
263 | 1130 | : UninstallingPreview(result, client, nam) | ||
264 | 1131 | { | ||
265 | 1132 | } | ||
266 | 1133 | |||
267 | 1134 | CancellingPurchasePreview::~CancellingPurchasePreview() | ||
268 | 1135 | { | ||
269 | 1136 | } | ||
270 | 1137 | |||
271 | 1138 | void CancellingPurchasePreview::run(unity::scopes::PreviewReplyProxy const& reply) | ||
272 | 1139 | { | ||
273 | 1140 | qDebug() << "in CancellingPurchasePreview::run, calling cancel_purchase"; | ||
274 | 1141 | cancel_purchase(); | ||
275 | 1142 | qDebug() << "in CancellingPurchasePreview::run, calling UninstallingPreview::run()"; | ||
276 | 1143 | UninstallingPreview::run(reply); | ||
277 | 1144 | } | ||
278 | 1145 | |||
279 | 1146 | void CancellingPurchasePreview::cancel_purchase() | ||
280 | 1147 | { | ||
281 | 1148 | auto package_name = result["name"].get_string(); | ||
282 | 1149 | qDebug() << "Will cancel the purchase of:" << package_name.c_str(); | ||
283 | 1150 | |||
284 | 1151 | std::promise<bool> refund_promise; | ||
285 | 1152 | std::future<bool> refund_future = refund_promise.get_future(); | ||
286 | 1153 | |||
287 | 1154 | run_under_qt([&refund_promise, package_name]() { | ||
288 | 1155 | qDebug() << "Calling refund for:" << package_name.c_str(); | ||
289 | 1156 | auto ret = pay::Package::instance().refund(package_name); | ||
290 | 1157 | qDebug() << "Refund returned:" << ret; | ||
291 | 1158 | refund_promise.set_value(ret); | ||
292 | 1159 | }); | ||
293 | 1160 | bool finished = refund_future.get(); | ||
294 | 1161 | qDebug() << "Finished refund:" << finished; | ||
295 | 1162 | } | ||
296 | 1163 | |||
297 | 1164 | |||
298 | 1031 | } // namespace click | 1165 | } // namespace click |
299 | 1032 | 1166 | ||
300 | === modified file 'libclickscope/click/preview.h' | |||
301 | --- libclickscope/click/preview.h 2015-04-10 14:24:10 +0000 | |||
302 | +++ libclickscope/click/preview.h 2015-05-29 21:20:25 +0000 | |||
303 | @@ -98,8 +98,11 @@ | |||
304 | 98 | constexpr static const char* PIN_TO_LAUNCHER{"pin_to_launcher"}; | 98 | constexpr static const char* PIN_TO_LAUNCHER{"pin_to_launcher"}; |
305 | 99 | constexpr static const char* UNINSTALL_CLICK{"uninstall_click"}; | 99 | constexpr static const char* UNINSTALL_CLICK{"uninstall_click"}; |
306 | 100 | constexpr static const char* CONFIRM_UNINSTALL{"confirm_uninstall"}; | 100 | constexpr static const char* CONFIRM_UNINSTALL{"confirm_uninstall"}; |
307 | 101 | constexpr static const char* CANCEL_PURCHASE_UNINSTALLED{"cancel_purchase_uninstalled"}; | ||
308 | 102 | constexpr static const char* CANCEL_PURCHASE_INSTALLED{"cancel_purchase_installed"}; | ||
309 | 101 | constexpr static const char* SHOW_UNINSTALLED{"show_uninstalled"}; | 103 | constexpr static const char* SHOW_UNINSTALLED{"show_uninstalled"}; |
310 | 102 | constexpr static const char* SHOW_INSTALLED{"show_installed"}; | 104 | constexpr static const char* SHOW_INSTALLED{"show_installed"}; |
311 | 105 | constexpr static const char* CONFIRM_CANCEL_PURCHASE{"confirm_cancel_purchase"}; | ||
312 | 103 | constexpr static const char* OPEN_ACCOUNTS{"open_accounts"}; | 106 | constexpr static const char* OPEN_ACCOUNTS{"open_accounts"}; |
313 | 104 | constexpr static const char* RATED{"rated"}; | 107 | constexpr static const char* RATED{"rated"}; |
314 | 105 | }; | 108 | }; |
315 | @@ -151,6 +154,7 @@ | |||
316 | 151 | virtual scopes::PreviewWidget build_updates_table(const PackageDetails& details); | 154 | virtual scopes::PreviewWidget build_updates_table(const PackageDetails& details); |
317 | 152 | virtual std::string build_whats_new(const PackageDetails& details); | 155 | virtual std::string build_whats_new(const PackageDetails& details); |
318 | 153 | virtual void run_under_qt(const std::function<void ()> &task); | 156 | virtual void run_under_qt(const std::function<void ()> &task); |
319 | 157 | virtual bool isRefundable() const; | ||
320 | 154 | 158 | ||
321 | 155 | scopes::Result result; | 159 | scopes::Result result; |
322 | 156 | QSharedPointer<click::web::Client> client; | 160 | QSharedPointer<click::web::Client> client; |
323 | @@ -209,10 +213,9 @@ | |||
324 | 209 | 213 | ||
325 | 210 | protected: | 214 | protected: |
326 | 211 | void getApplicationUri(const Manifest& manifest, std::function<void(const std::string&)> callback); | 215 | void getApplicationUri(const Manifest& manifest, std::function<void(const std::string&)> callback); |
328 | 212 | 216 | scopes::PreviewWidgetList createButtons(const std::string& uri, | |
329 | 217 | const click::Manifest& manifest); | ||
330 | 213 | private: | 218 | private: |
331 | 214 | static scopes::PreviewWidgetList createButtons(const std::string& uri, | ||
332 | 215 | const click::Manifest& manifest); | ||
333 | 216 | scopes::ActionMetadata metadata; | 219 | scopes::ActionMetadata metadata; |
334 | 217 | }; | 220 | }; |
335 | 218 | 221 | ||
336 | @@ -236,6 +239,20 @@ | |||
337 | 236 | virtual scopes::PreviewWidgetList purchasingWidgets(const PackageDetails &); | 239 | virtual scopes::PreviewWidgetList purchasingWidgets(const PackageDetails &); |
338 | 237 | }; | 240 | }; |
339 | 238 | 241 | ||
340 | 242 | class CancelPurchasePreview : public PreviewStrategy | ||
341 | 243 | { | ||
342 | 244 | public: | ||
343 | 245 | CancelPurchasePreview(const unity::scopes::Result& result, bool installed); | ||
344 | 246 | |||
345 | 247 | virtual ~CancelPurchasePreview(); | ||
346 | 248 | |||
347 | 249 | void run(unity::scopes::PreviewReplyProxy const& reply) override; | ||
348 | 250 | |||
349 | 251 | protected: | ||
350 | 252 | scopes::PreviewWidgetList build_widgets(); | ||
351 | 253 | bool installed; | ||
352 | 254 | }; | ||
353 | 255 | |||
354 | 239 | class UninstallConfirmationPreview : public PreviewStrategy | 256 | class UninstallConfirmationPreview : public PreviewStrategy |
355 | 240 | { | 257 | { |
356 | 241 | public: | 258 | public: |
357 | @@ -284,6 +301,21 @@ | |||
358 | 284 | 301 | ||
359 | 285 | }; | 302 | }; |
360 | 286 | 303 | ||
361 | 304 | class CancellingPurchasePreview : public UninstallingPreview | ||
362 | 305 | { | ||
363 | 306 | public: | ||
364 | 307 | CancellingPurchasePreview(const unity::scopes::Result& result, | ||
365 | 308 | const QSharedPointer<click::web::Client>& client, | ||
366 | 309 | const QSharedPointer<click::network::AccessManager>& nam); | ||
367 | 310 | |||
368 | 311 | virtual ~CancellingPurchasePreview(); | ||
369 | 312 | |||
370 | 313 | void run(unity::scopes::PreviewReplyProxy const& reply) override; | ||
371 | 314 | |||
372 | 315 | protected: | ||
373 | 316 | void cancel_purchase(); | ||
374 | 317 | }; | ||
375 | 318 | |||
376 | 287 | } // namespace click | 319 | } // namespace click |
377 | 288 | 320 | ||
378 | 289 | #endif | 321 | #endif |
379 | 290 | 322 | ||
380 | === modified file 'libclickscope/tests/test_preview.cpp' | |||
381 | --- libclickscope/tests/test_preview.cpp 2015-03-26 18:49:42 +0000 | |||
382 | +++ libclickscope/tests/test_preview.cpp 2015-05-29 21:20:25 +0000 | |||
383 | @@ -27,6 +27,8 @@ | |||
384 | 27 | * files in the program, then also delete it here. | 27 | * files in the program, then also delete it here. |
385 | 28 | */ | 28 | */ |
386 | 29 | 29 | ||
387 | 30 | #include <time.h> | ||
388 | 31 | |||
389 | 30 | #include <unity/scopes/testing/MockPreviewReply.h> | 32 | #include <unity/scopes/testing/MockPreviewReply.h> |
390 | 31 | #include <unity/scopes/testing/Result.h> | 33 | #include <unity/scopes/testing/Result.h> |
391 | 32 | 34 | ||
392 | @@ -34,6 +36,7 @@ | |||
393 | 34 | #include <click/preview.h> | 36 | #include <click/preview.h> |
394 | 35 | #include <fake_json.h> | 37 | #include <fake_json.h> |
395 | 36 | #include <click/index.h> | 38 | #include <click/index.h> |
396 | 39 | #include <click/interface.h> | ||
397 | 37 | #include <click/reviews.h> | 40 | #include <click/reviews.h> |
398 | 38 | #include <boost/locale/time_zone.hpp> | 41 | #include <boost/locale/time_zone.hpp> |
399 | 39 | 42 | ||
400 | @@ -100,6 +103,7 @@ | |||
401 | 100 | using click::PreviewStrategy::build_updates_table; | 103 | using click::PreviewStrategy::build_updates_table; |
402 | 101 | using click::PreviewStrategy::build_whats_new; | 104 | using click::PreviewStrategy::build_whats_new; |
403 | 102 | using click::PreviewStrategy::populateDetails; | 105 | using click::PreviewStrategy::populateDetails; |
404 | 106 | using click::PreviewStrategy::isRefundable; | ||
405 | 103 | }; | 107 | }; |
406 | 104 | 108 | ||
407 | 105 | class PreviewsBaseTest : public Test | 109 | class PreviewsBaseTest : public Test |
408 | @@ -330,15 +334,15 @@ | |||
409 | 330 | } | 334 | } |
410 | 331 | }; | 335 | }; |
411 | 332 | 336 | ||
413 | 333 | class FakeUninstalledPreview : public click::UninstalledPreview { | 337 | class FakeBaseUninstalledPreview : public click::UninstalledPreview { |
414 | 334 | std::string object_path; | 338 | std::string object_path; |
415 | 335 | public: | 339 | public: |
416 | 336 | std::unique_ptr<FakeDownloader> fake_downloader; | 340 | std::unique_ptr<FakeDownloader> fake_downloader; |
422 | 337 | FakeUninstalledPreview(const std::string& object_path, | 341 | FakeBaseUninstalledPreview(const std::string& object_path, |
423 | 338 | const unity::scopes::Result& result, | 342 | const unity::scopes::Result& result, |
424 | 339 | const QSharedPointer<click::web::Client>& client, | 343 | const QSharedPointer<click::web::Client>& client, |
425 | 340 | const std::shared_ptr<click::DepartmentsDb>& depts, | 344 | const std::shared_ptr<click::DepartmentsDb>& depts, |
426 | 341 | const QSharedPointer<click::network::AccessManager>& nam) | 345 | const QSharedPointer<click::network::AccessManager>& nam) |
427 | 342 | : click::UninstalledPreview(result, client, depts, nam), object_path(object_path), | 346 | : click::UninstalledPreview(result, client, depts, nam), object_path(object_path), |
428 | 343 | fake_downloader(new FakeDownloader(object_path, nam)) | 347 | fake_downloader(new FakeDownloader(object_path, nam)) |
429 | 344 | { | 348 | { |
430 | @@ -356,10 +360,22 @@ | |||
431 | 356 | details_callback(details); | 360 | details_callback(details); |
432 | 357 | reviews_callback({}, click::Reviews::Error::NoError); | 361 | reviews_callback({}, click::Reviews::Error::NoError); |
433 | 358 | } | 362 | } |
434 | 363 | }; | ||
435 | 364 | |||
436 | 365 | class FakeUninstalledPreview : public FakeBaseUninstalledPreview { | ||
437 | 366 | public: | ||
438 | 359 | MOCK_METHOD1(uninstalledActionButtonWidgets, scopes::PreviewWidgetList (const click::PackageDetails &details)); | 367 | MOCK_METHOD1(uninstalledActionButtonWidgets, scopes::PreviewWidgetList (const click::PackageDetails &details)); |
439 | 360 | MOCK_METHOD1(progressBarWidget, scopes::PreviewWidgetList(const std::string& object_path)); | 368 | MOCK_METHOD1(progressBarWidget, scopes::PreviewWidgetList(const std::string& object_path)); |
440 | 369 | FakeUninstalledPreview(const std::string& object_path, | ||
441 | 370 | const unity::scopes::Result& result, | ||
442 | 371 | const QSharedPointer<click::web::Client>& client, | ||
443 | 372 | const std::shared_ptr<click::DepartmentsDb>& depts, | ||
444 | 373 | const QSharedPointer<click::network::AccessManager>& nam) | ||
445 | 374 | : FakeBaseUninstalledPreview(object_path, result, client, depts, nam) { | ||
446 | 375 | } | ||
447 | 361 | }; | 376 | }; |
448 | 362 | 377 | ||
449 | 378 | |||
450 | 363 | TEST_F(UninstalledPreviewTest, testDownloadInProgress) { | 379 | TEST_F(UninstalledPreviewTest, testDownloadInProgress) { |
451 | 364 | std::string fake_object_path = "/fake/object/path"; | 380 | std::string fake_object_path = "/fake/object/path"; |
452 | 365 | 381 | ||
453 | @@ -385,3 +401,180 @@ | |||
454 | 385 | preview.run(replyptr); | 401 | preview.run(replyptr); |
455 | 386 | preview.fake_downloader->activate_callback(); | 402 | preview.fake_downloader->activate_callback(); |
456 | 387 | } | 403 | } |
457 | 404 | |||
458 | 405 | class FakeUninstalledRefundablePreview : FakeBaseUninstalledPreview { | ||
459 | 406 | public: | ||
460 | 407 | FakeUninstalledRefundablePreview(const unity::scopes::Result& result, | ||
461 | 408 | const QSharedPointer<click::web::Client>& client, | ||
462 | 409 | const std::shared_ptr<click::DepartmentsDb>& depts, | ||
463 | 410 | const QSharedPointer<click::network::AccessManager>& nam) | ||
464 | 411 | : FakeBaseUninstalledPreview(std::string{""}, result, client, depts, nam){ | ||
465 | 412 | } | ||
466 | 413 | using click::UninstalledPreview::uninstalledActionButtonWidgets; | ||
467 | 414 | MOCK_CONST_METHOD0(isRefundable, bool()); | ||
468 | 415 | }; | ||
469 | 416 | |||
470 | 417 | unity::scopes::VariantArray get_actions_from_widgets(const unity::scopes::PreviewWidgetList& widgets, int widget_number) { | ||
471 | 418 | auto widget = *std::next(widgets.begin(), widget_number); | ||
472 | 419 | return widget.attribute_values()["actions"].get_array(); | ||
473 | 420 | } | ||
474 | 421 | |||
475 | 422 | std::string get_action_from_widgets(const unity::scopes::PreviewWidgetList& widgets, int widget_number, int action_number) { | ||
476 | 423 | auto actions = get_actions_from_widgets(widgets, widget_number); | ||
477 | 424 | auto selected_action = actions.at(action_number).get_dict(); | ||
478 | 425 | return selected_action["id"].get_string(); | ||
479 | 426 | } | ||
480 | 427 | |||
481 | 428 | TEST_F(UninstalledPreviewTest, testIsRefundableButtonShown) { | ||
482 | 429 | result["name"] = "fake_app_name"; | ||
483 | 430 | result["price"] = 2.99; | ||
484 | 431 | result["purchased"] = true; | ||
485 | 432 | FakeUninstalledRefundablePreview preview(result, client, depts, nam); | ||
486 | 433 | |||
487 | 434 | click::PackageDetails pkgdetails; | ||
488 | 435 | EXPECT_CALL(preview, isRefundable()).Times(1) | ||
489 | 436 | .WillOnce(Return(true)); | ||
490 | 437 | auto widgets = preview.uninstalledActionButtonWidgets(pkgdetails); | ||
491 | 438 | ASSERT_EQ(get_action_from_widgets(widgets, 0, 1), "cancel_purchase_uninstalled"); | ||
492 | 439 | } | ||
493 | 440 | |||
494 | 441 | TEST_F(UninstalledPreviewTest, testIsRefundableButtonNotShown) { | ||
495 | 442 | result["name"] = "fake_app_name"; | ||
496 | 443 | result["price"] = 2.99; | ||
497 | 444 | result["purchased"] = true; | ||
498 | 445 | FakeUninstalledRefundablePreview preview(result, client, depts, nam); | ||
499 | 446 | |||
500 | 447 | click::PackageDetails pkgdetails; | ||
501 | 448 | EXPECT_CALL(preview, isRefundable()).Times(1) | ||
502 | 449 | .WillOnce(Return(false)); | ||
503 | 450 | auto widgets = preview.uninstalledActionButtonWidgets(pkgdetails); | ||
504 | 451 | ASSERT_EQ(get_actions_from_widgets(widgets, 0).size(), 1); | ||
505 | 452 | } | ||
506 | 453 | |||
507 | 454 | class InstalledPreviewTest : public Test { | ||
508 | 455 | protected: | ||
509 | 456 | unity::scopes::testing::Result result; | ||
510 | 457 | unity::scopes::ActionMetadata metadata; | ||
511 | 458 | unity::scopes::VariantMap metadict; | ||
512 | 459 | QSharedPointer<click::web::Client> client; | ||
513 | 460 | QSharedPointer<click::network::AccessManager> nam; | ||
514 | 461 | std::shared_ptr<click::DepartmentsDb> depts; | ||
515 | 462 | |||
516 | 463 | public: | ||
517 | 464 | InstalledPreviewTest() : metadata("en_EN", "phone") { | ||
518 | 465 | } | ||
519 | 466 | }; | ||
520 | 467 | |||
521 | 468 | class FakeInstalledRefundablePreview : public click::InstalledPreview { | ||
522 | 469 | public: | ||
523 | 470 | FakeInstalledRefundablePreview(const unity::scopes::Result& result, | ||
524 | 471 | const unity::scopes::ActionMetadata& metadata, | ||
525 | 472 | const QSharedPointer<click::web::Client> client, | ||
526 | 473 | const std::shared_ptr<click::DepartmentsDb> depts) | ||
527 | 474 | : click::InstalledPreview(result, metadata, client, depts) { | ||
528 | 475 | |||
529 | 476 | } | ||
530 | 477 | using click::InstalledPreview::createButtons; | ||
531 | 478 | MOCK_CONST_METHOD0(isRefundable, bool()); | ||
532 | 479 | }; | ||
533 | 480 | |||
534 | 481 | TEST_F(InstalledPreviewTest, testIsRefundableButtonShown) { | ||
535 | 482 | FakeInstalledRefundablePreview preview(result, metadata, client, depts); | ||
536 | 483 | EXPECT_CALL(preview, isRefundable()).Times(1) | ||
537 | 484 | .WillOnce(Return(true)); | ||
538 | 485 | click::Manifest manifest; | ||
539 | 486 | manifest.removable = true; | ||
540 | 487 | auto widgets = preview.createButtons("fake uri", manifest); | ||
541 | 488 | ASSERT_EQ(get_actions_from_widgets(widgets, 0).size(), 2); | ||
542 | 489 | ASSERT_EQ(get_action_from_widgets(widgets, 0, 1), "cancel_purchase_installed"); | ||
543 | 490 | } | ||
544 | 491 | |||
545 | 492 | TEST_F(InstalledPreviewTest, testIsRefundableButtonNotShown) { | ||
546 | 493 | FakeInstalledRefundablePreview preview(result, metadata, client, depts); | ||
547 | 494 | EXPECT_CALL(preview, isRefundable()).Times(1) | ||
548 | 495 | .WillOnce(Return(false)); | ||
549 | 496 | click::Manifest manifest; | ||
550 | 497 | manifest.removable = true; | ||
551 | 498 | auto widgets = preview.createButtons("fake uri", manifest); | ||
552 | 499 | ASSERT_EQ(get_actions_from_widgets(widgets, 0).size(), 2); | ||
553 | 500 | ASSERT_EQ(get_action_from_widgets(widgets, 0, 1), "uninstall_click"); | ||
554 | 501 | } | ||
555 | 502 | |||
556 | 503 | |||
557 | 504 | class RefundableTest : public PreviewStrategyTest { | ||
558 | 505 | |||
559 | 506 | }; | ||
560 | 507 | |||
561 | 508 | TEST_F(RefundableTest, testIsNotRefundableWhenFieldMissing) { | ||
562 | 509 | FakeResult result{vm}; | ||
563 | 510 | FakePreview preview{result}; | ||
564 | 511 | ASSERT_FALSE(preview.isRefundable()); | ||
565 | 512 | } | ||
566 | 513 | |||
567 | 514 | TEST_F(RefundableTest, testIsNotRefundableWhenExpired) { | ||
568 | 515 | FakeResult result{vm}; | ||
569 | 516 | time_t now = time(NULL); | ||
570 | 517 | result["refundable_until"] = (int64_t) (now - 300); | ||
571 | 518 | FakePreview preview{result}; | ||
572 | 519 | ASSERT_FALSE(preview.isRefundable()); | ||
573 | 520 | } | ||
574 | 521 | |||
575 | 522 | TEST_F(RefundableTest, testIsRefundable) { | ||
576 | 523 | FakeResult result{vm}; | ||
577 | 524 | time_t now = time(NULL); | ||
578 | 525 | result["refundable_until"] = (int64_t) (now + 300); | ||
579 | 526 | FakePreview preview{result}; | ||
580 | 527 | ASSERT_TRUE(preview.isRefundable()); | ||
581 | 528 | } | ||
582 | 529 | |||
583 | 530 | TEST_F(RefundableTest, testIsNotRefundableWhenExpiringRealSoon) { | ||
584 | 531 | FakeResult result{vm}; | ||
585 | 532 | time_t now = time(NULL); | ||
586 | 533 | result["refundable_until"] = (int64_t) (now + 8); | ||
587 | 534 | FakePreview preview{result}; | ||
588 | 535 | ASSERT_FALSE(preview.isRefundable()); | ||
589 | 536 | } | ||
590 | 537 | |||
591 | 538 | |||
592 | 539 | class FakeCancelPurchasePreview : public click::CancelPurchasePreview { | ||
593 | 540 | public: | ||
594 | 541 | FakeCancelPurchasePreview(const unity::scopes::Result& result, bool installed) | ||
595 | 542 | : click::CancelPurchasePreview(result, installed) { | ||
596 | 543 | |||
597 | 544 | } | ||
598 | 545 | using click::CancelPurchasePreview::build_widgets; | ||
599 | 546 | }; | ||
600 | 547 | |||
601 | 548 | class CancelPurchasePreviewTest : public PreviewsBaseTest { | ||
602 | 549 | |||
603 | 550 | }; | ||
604 | 551 | |||
605 | 552 | TEST_F(CancelPurchasePreviewTest, testNoShowsInstalled) | ||
606 | 553 | { | ||
607 | 554 | FakeResult result{vm}; | ||
608 | 555 | result["title"] = "fake app"; | ||
609 | 556 | FakeCancelPurchasePreview preview(result, true); | ||
610 | 557 | auto widgets = preview.build_widgets(); | ||
611 | 558 | auto action = get_action_from_widgets(widgets, 1, 0); | ||
612 | 559 | ASSERT_EQ(action, "show_installed"); | ||
613 | 560 | } | ||
614 | 561 | |||
615 | 562 | TEST_F(CancelPurchasePreviewTest, testNoShowsUninstalled) | ||
616 | 563 | { | ||
617 | 564 | FakeResult result{vm}; | ||
618 | 565 | result["title"] = "fake app"; | ||
619 | 566 | FakeCancelPurchasePreview preview(result, false); | ||
620 | 567 | auto widgets = preview.build_widgets(); | ||
621 | 568 | auto action = get_action_from_widgets(widgets, 1, 0); | ||
622 | 569 | ASSERT_EQ(action, "show_uninstalled"); | ||
623 | 570 | } | ||
624 | 571 | |||
625 | 572 | TEST_F(CancelPurchasePreviewTest, testYesCancelsPurchase) | ||
626 | 573 | { | ||
627 | 574 | FakeResult result{vm}; | ||
628 | 575 | result["title"] = "fake app"; | ||
629 | 576 | FakeCancelPurchasePreview preview(result, false); | ||
630 | 577 | auto widgets = preview.build_widgets(); | ||
631 | 578 | auto action = get_action_from_widgets(widgets, 1, 1); | ||
632 | 579 | ASSERT_EQ(action, "confirm_cancel_purchase"); | ||
633 | 580 | } | ||
634 | 388 | 581 | ||
635 | === modified file 'scope/clickapps/apps-scope.cpp' | |||
636 | --- scope/clickapps/apps-scope.cpp 2015-04-10 14:24:10 +0000 | |||
637 | +++ scope/clickapps/apps-scope.cpp 2015-05-29 21:20:25 +0000 | |||
638 | @@ -118,6 +118,12 @@ | |||
639 | 118 | if (action_id == click::Preview::Actions::UNINSTALL_CLICK) { | 118 | if (action_id == click::Preview::Actions::UNINSTALL_CLICK) { |
640 | 119 | activation->setHint(click::Preview::Actions::UNINSTALL_CLICK, unity::scopes::Variant(true)); | 119 | activation->setHint(click::Preview::Actions::UNINSTALL_CLICK, unity::scopes::Variant(true)); |
641 | 120 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); | 120 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); |
642 | 121 | } else if (action_id == click::Preview::Actions::CANCEL_PURCHASE_INSTALLED) { | ||
643 | 122 | activation->setHint(click::Preview::Actions::CANCEL_PURCHASE_INSTALLED, unity::scopes::Variant(true)); | ||
644 | 123 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); | ||
645 | 124 | } else if (action_id == click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED) { | ||
646 | 125 | activation->setHint(click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED, unity::scopes::Variant(true)); | ||
647 | 126 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); | ||
648 | 121 | } else if (action_id == click::Preview::Actions::SHOW_INSTALLED) { | 127 | } else if (action_id == click::Preview::Actions::SHOW_INSTALLED) { |
649 | 122 | activation->setHint(click::Preview::Actions::SHOW_INSTALLED, unity::scopes::Variant(true)); | 128 | activation->setHint(click::Preview::Actions::SHOW_INSTALLED, unity::scopes::Variant(true)); |
650 | 123 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); | 129 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); |
651 | 124 | 130 | ||
652 | === modified file 'scope/clickstore/store-query.cpp' | |||
653 | --- scope/clickstore/store-query.cpp 2015-03-18 14:00:53 +0000 | |||
654 | +++ scope/clickstore/store-query.cpp 2015-05-29 21:20:25 +0000 | |||
655 | @@ -293,7 +293,8 @@ | |||
656 | 293 | ss << "☆ " << pkg.rating; | 293 | ss << "☆ " << pkg.rating; |
657 | 294 | std::string rating{ss.str()}; | 294 | std::string rating{ss.str()}; |
658 | 295 | 295 | ||
660 | 296 | bool purchased = false; | 296 | bool was_purchased = false; |
661 | 297 | time_t refundable_until = 0; | ||
662 | 297 | double cur_price{0.00}; | 298 | double cur_price{0.00}; |
663 | 298 | auto suggested = impl->index.get_suggested_currency(); | 299 | auto suggested = impl->index.get_suggested_currency(); |
664 | 299 | std::string currency = Configuration::get_currency(suggested); | 300 | std::string currency = Configuration::get_currency(suggested); |
665 | @@ -307,20 +308,27 @@ | |||
666 | 307 | res["price"] = scopes::Variant(cur_price); | 308 | res["price"] = scopes::Variant(cur_price); |
667 | 308 | res[click::Query::ResultKeys::VERSION] = pkg.version; | 309 | res[click::Query::ResultKeys::VERSION] = pkg.version; |
668 | 309 | 310 | ||
669 | 311 | |||
670 | 312 | qDebug() << "App:" << pkg.name.c_str() << ", price:" << cur_price; | ||
671 | 310 | if (cur_price > 0.00f) { | 313 | if (cur_price > 0.00f) { |
672 | 311 | if (!Configuration::get_purchases_enabled()) { | 314 | if (!Configuration::get_purchases_enabled()) { |
673 | 312 | // Don't show priced apps if flag not set | 315 | // Don't show priced apps if flag not set |
674 | 313 | return; | 316 | return; |
675 | 314 | } | 317 | } |
676 | 315 | // Check if the priced app was already purchased. | 318 | // Check if the priced app was already purchased. |
678 | 316 | purchased = purchased_apps.count({pkg.name}) != 0; | 319 | auto purchased = purchased_apps.find({pkg.name}); |
679 | 320 | was_purchased = purchased != purchased_apps.end(); | ||
680 | 321 | if (was_purchased) { | ||
681 | 322 | refundable_until = purchased->refundable_until; | ||
682 | 323 | } | ||
683 | 324 | qDebug() << "was purchased?" << was_purchased << ", refundable_until:" << refundable_until; | ||
684 | 317 | } | 325 | } |
685 | 318 | if (installed != installedPackages.end()) { | 326 | if (installed != installedPackages.end()) { |
686 | 319 | res[click::Query::ResultKeys::INSTALLED] = true; | 327 | res[click::Query::ResultKeys::INSTALLED] = true; |
688 | 320 | res[click::Query::ResultKeys::PURCHASED] = purchased; | 328 | res[click::Query::ResultKeys::PURCHASED] = was_purchased; |
689 | 321 | price = _("✔ INSTALLED"); | 329 | price = _("✔ INSTALLED"); |
690 | 322 | res[click::Query::ResultKeys::VERSION] = installed->version; | 330 | res[click::Query::ResultKeys::VERSION] = installed->version; |
692 | 323 | } else if (purchased) { | 331 | } else if (was_purchased) { |
693 | 324 | res[click::Query::ResultKeys::PURCHASED] = true; | 332 | res[click::Query::ResultKeys::PURCHASED] = true; |
694 | 325 | res[click::Query::ResultKeys::INSTALLED] = false; | 333 | res[click::Query::ResultKeys::INSTALLED] = false; |
695 | 326 | price = _("✔ PURCHASED"); | 334 | price = _("✔ PURCHASED"); |
696 | @@ -336,6 +344,7 @@ | |||
697 | 336 | } | 344 | } |
698 | 337 | } | 345 | } |
699 | 338 | 346 | ||
700 | 347 | res[click::Query::ResultKeys::REFUNDABLE_UNTIL] = unity::scopes::Variant((int64_t)refundable_until); | ||
701 | 339 | res["price_area"] = price; | 348 | res["price_area"] = price; |
702 | 340 | res["rating"] = rating; | 349 | res["rating"] = rating; |
703 | 341 | 350 | ||
704 | 342 | 351 | ||
705 | === modified file 'scope/clickstore/store-query.h' | |||
706 | --- scope/clickstore/store-query.h 2015-04-10 13:13:46 +0000 | |||
707 | +++ scope/clickstore/store-query.h 2015-05-29 21:20:25 +0000 | |||
708 | @@ -64,6 +64,7 @@ | |||
709 | 64 | constexpr static const char* MAIN_SCREENSHOT{"main_screenshot"}; | 64 | constexpr static const char* MAIN_SCREENSHOT{"main_screenshot"}; |
710 | 65 | constexpr static const char* INSTALLED{"installed"}; | 65 | constexpr static const char* INSTALLED{"installed"}; |
711 | 66 | constexpr static const char* PURCHASED{"purchased"}; | 66 | constexpr static const char* PURCHASED{"purchased"}; |
712 | 67 | constexpr static const char* REFUNDABLE_UNTIL{"refundable_until"}; | ||
713 | 67 | constexpr static const char* VERSION{"version"}; | 68 | constexpr static const char* VERSION{"version"}; |
714 | 68 | }; | 69 | }; |
715 | 69 | 70 | ||
716 | 70 | 71 | ||
717 | === modified file 'scope/clickstore/store-scope.cpp' | |||
718 | --- scope/clickstore/store-scope.cpp 2015-04-10 14:24:10 +0000 | |||
719 | +++ scope/clickstore/store-scope.cpp 2015-05-29 21:20:25 +0000 | |||
720 | @@ -145,6 +145,12 @@ | |||
721 | 145 | } else if (action_id == click::Preview::Actions::DOWNLOAD_COMPLETED) { | 145 | } else if (action_id == click::Preview::Actions::DOWNLOAD_COMPLETED) { |
722 | 146 | activation->setHint(click::Preview::Actions::DOWNLOAD_COMPLETED, unity::scopes::Variant(true)); | 146 | activation->setHint(click::Preview::Actions::DOWNLOAD_COMPLETED, unity::scopes::Variant(true)); |
723 | 147 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); | 147 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); |
724 | 148 | } else if (action_id == click::Preview::Actions::CANCEL_PURCHASE_INSTALLED) { | ||
725 | 149 | activation->setHint(click::Preview::Actions::CANCEL_PURCHASE_INSTALLED, unity::scopes::Variant(true)); | ||
726 | 150 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); | ||
727 | 151 | } else if (action_id == click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED) { | ||
728 | 152 | activation->setHint(click::Preview::Actions::CANCEL_PURCHASE_UNINSTALLED, unity::scopes::Variant(true)); | ||
729 | 153 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); | ||
730 | 148 | } else if (action_id == click::Preview::Actions::UNINSTALL_CLICK) { | 154 | } else if (action_id == click::Preview::Actions::UNINSTALL_CLICK) { |
731 | 149 | activation->setHint(click::Preview::Actions::UNINSTALL_CLICK, unity::scopes::Variant(true)); | 155 | activation->setHint(click::Preview::Actions::UNINSTALL_CLICK, unity::scopes::Variant(true)); |
732 | 150 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); | 156 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); |
733 | @@ -157,6 +163,9 @@ | |||
734 | 157 | } else if (action_id == click::Preview::Actions::CONFIRM_UNINSTALL) { | 163 | } else if (action_id == click::Preview::Actions::CONFIRM_UNINSTALL) { |
735 | 158 | activation->setHint(click::Preview::Actions::CONFIRM_UNINSTALL, unity::scopes::Variant(true)); | 164 | activation->setHint(click::Preview::Actions::CONFIRM_UNINSTALL, unity::scopes::Variant(true)); |
736 | 159 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); | 165 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); |
737 | 166 | } else if (action_id == click::Preview::Actions::CONFIRM_CANCEL_PURCHASE) { | ||
738 | 167 | activation->setHint(click::Preview::Actions::CONFIRM_CANCEL_PURCHASE, unity::scopes::Variant(true)); | ||
739 | 168 | activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview); | ||
740 | 160 | } else if (action_id == click::Preview::Actions::RATED) { | 169 | } else if (action_id == click::Preview::Actions::RATED) { |
741 | 161 | scopes::VariantMap rating_info = metadata.scope_data().get_dict(); | 170 | scopes::VariantMap rating_info = metadata.scope_data().get_dict(); |
742 | 162 | // Cast to int because widget gives us double, which is wrong. | 171 | // Cast to int because widget gives us double, which is wrong. |
FAILED: Continuous integration, rev:323 jenkins. qa.ubuntu. com/job/ unity-scope- click-ci/ 583/ jenkins. qa.ubuntu. com/job/ unity-scope- click-vivid- amd64-ci/ 39/console jenkins. qa.ubuntu. com/job/ unity-scope- click-vivid- armhf-ci/ 40/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- scope-click- ci/583/ rebuild
http://