Audacity 3.2.0
UserService.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 UserService.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
11
12#include "UserService.h"
13
14#include <memory>
15#include <vector>
16
17#include <wx/file.h>
18
19#include <rapidjson/document.h>
20
21#include "ServiceConfig.h"
22#include "OAuthService.h"
23
24#include "BasicUI.h"
25#include "FileNames.h"
26#include "Observer.h"
27#include "Prefs.h"
28
29#include "IResponse.h"
30#include "NetworkManager.h"
31#include "Request.h"
32
33#include "CodeConversions.h"
34
36{
37namespace
38{
40{
41 const wxFileName avatarFileName(FileNames::ConfigDir(), "avatar");
42 return avatarFileName.GetFullPath();
43}
44
45StringSetting userId { L"/cloud/audiocom/userId", "" };
46StringSetting userName { L"/cloud/audiocom/userName", "" };
47StringSetting displayName { L"/cloud/audiocom/displayName", "" };
48StringSetting avatarEtag { L"/cloud/audiocom/avatarEtag", "" };
49
52 [](const auto& state)
53 {
54 if (state.authorised)
55 GetUserService().UpdateUserData();
56 else
58 });
59
60} // namespace
61
63{
64 auto& oauthService = GetOAuthService();
65
66 if (!oauthService.HasAccessToken())
67 return;
68
69 using namespace audacity::network_manager;
70
71 Request request(GetServiceConfig().GetAPIUrl("/me"));
72
73 request.setHeader(
75 std::string(oauthService.GetAccessToken()));
76
77 request.setHeader(
79
80 auto response = NetworkManager::GetInstance().doGet(request);
81
82 response->setRequestFinishedCallback(
83 [response, this](auto)
84 {
85 const auto httpCode = response->getHTTPCode();
86
87 if (httpCode != 200)
88 return;
89
90 const auto body = response->readAll<std::string>();
91
92 using namespace rapidjson;
93
94 Document document;
95 document.Parse(body.data(), body.size());
96
97 if (!document.IsObject())
98 return;
99
100 const auto id = document["id"].GetString();
101 const auto username = document["username"].GetString();
102 const auto avatar = document["avatar"].GetString();
103 const auto profileName = document["profile"]["name"].GetString();
104
106 [this,
107 id = std::string(id),
108 username = std::string(username),
109 profileName = std::string(profileName),
110 avatar = std::string(avatar)]()
111 {
115
116 gPrefs->Flush();
117
118 DownloadAvatar(avatar);
119
120 Publish({});
121 });
122 });
123}
124
126{
128 [this]()
129 {
130 // No valid data was present, do not spam Publish()
131 if (GetUserSlug().empty())
132 return;
133
134 userId.Write({});
135 userName.Write({});
136 displayName.Write({});
137 avatarEtag.Write({});
138
139 gPrefs->Flush();
140
141 Publish({});
142 });
143}
144
146{
147 static UserService userService;
148 return userService;
149}
150
151void UserService::DownloadAvatar(std::string_view url)
152{
153 const auto avatarPath = MakeAvatarPath();
154 const auto avatarTempPath = avatarPath + ".tmp";
155
156 if (url.empty())
157 {
158 if (wxFileExists(avatarPath))
159 wxRemoveFile(avatarPath);
160
161 return;
162 }
163
164 std::shared_ptr<wxFile> avatarFile = std::make_shared<wxFile>();
165
166 if (!avatarFile->Create(avatarTempPath, true))
167 return;
168
169 using namespace audacity::network_manager;
170
171 auto request = Request(std::string(url));
172
173 const auto etag = audacity::ToUTF8(avatarEtag.Read());
174
175 // If ETag is present - use it to prevent re-downloading the same file
176 if (!etag.empty() && wxFileExists(avatarPath))
177 request.setHeader(common_headers::IfNoneMatch, etag);
178
179 auto response = NetworkManager::GetInstance().doGet(request);
180
181 response->setOnDataReceivedCallback(
182 [response, avatarFile](auto)
183 {
184 std::vector<char> buffer(response->getBytesAvailable());
185
186 size_t bytes = response->readData(buffer.data(), buffer.size());
187
188 avatarFile->Write(buffer.data(), buffer.size());
189 });
190
191 response->setRequestFinishedCallback(
192 [response, avatarFile, avatarPath, avatarTempPath, this](auto)
193 {
194 avatarFile->Close();
195
196 const auto httpCode = response->getHTTPCode();
197
198 if (httpCode != 200)
199 {
200 // For any response except 200 just remove the temp file
201 wxRemoveFile(avatarTempPath);
202 return;
203 }
204
205 const auto etag = response->getHeader("ETag");
206 const auto oldPath = avatarPath + ".old";
207
208 if (wxFileExists(avatarPath))
209 if (!wxRenameFile(avatarPath, oldPath))
210 return;
211
212 if (!wxRenameFile(avatarTempPath, avatarPath))
213 {
214 // Try at least to get it back...
215 wxRenameFile(oldPath, avatarPath);
216 return;
217 }
218
219 if (wxFileExists(oldPath))
220 wxRemoveFile(oldPath);
221
223 [this, etag]()
224 {
225 avatarEtag.Write(etag);
226 gPrefs->Flush();
227
228 Publish({});
229 });
230 });
231}
232
234{
235 return userId.Read();
236}
237
239{
240 return displayName.Read();
241}
242
244{
245 return userName.Read();
246}
247
249{
250 auto path = MakeAvatarPath();
251
252 if (!wxFileExists(path))
253 return {};
254
255 return path;
256}
257
258} // namespace audacity::cloud::audiocom
Toolkit-neutral facade for basic user interface services.
Declare functions to perform UTF-8 to std::wstring conversions.
Declare an interface for HTTP response.
Declare a class for performing HTTP requests.
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
Declare a class for constructing HTTP requests.
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
CallbackReturn Publish(const UserDataChanged &message)
Send a message to connected callbacks.
Definition: Observer.h:207
A move-only handle representing a connection to a Publisher.
Definition: Observer.h:70
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:259
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
Specialization of Setting for strings.
Definition: Prefs.h:370
virtual bool Flush() noexcept=0
Service for providing information about the user profile.
Definition: UserService.h:28
void DownloadAvatar(std::string_view url)
wxString GetUserSlug() const
"Slug" used to construct shareable URLs
void ClearUserData()
Reset the user profile data.
wxString GetAvatarPath() const
Gets a path to the avatar.
wxString GetUserId() const
Gets user id.
wxString GetDisplayName() const
Get the user name to display in the dialog.
void UpdateUserData()
Request the service to update the data.
Definition: UserService.cpp:62
ResponsePtr doGet(const Request &request)
Request & setHeader(const std::string &name, std::string value)
Definition: Request.cpp:46
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:214
FILES_API FilePath ConfigDir()
Audacity user config directory.
UserService & GetUserService()
OAuthService & GetOAuthService()
Returns the instance of the OAuthService.
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.
std::string ToUTF8(const std::wstring &wstr)
wxString ToWXString(const std::string &str)