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
35namespace cloud::audiocom
36{
37namespace
38{
40{
41 const wxFileName avatarFileName(FileNames::ConfigDir(), "avatar");
42 return avatarFileName.GetFullPath();
43}
44
45StringSetting userName { L"/cloud/audiocom/userName", "" };
46StringSetting displayName { L"/cloud/audiocom/displayName", "" };
47StringSetting avatarEtag { L"/cloud/audiocom/avatarEtag", "" };
48
51 [](const auto& state)
52 {
53 if (state.authorised)
54 GetUserService().UpdateUserData();
55 else
57 });
58
59} // namespace
60
62{
63 auto& oauthService = GetOAuthService();
64
65 if (!oauthService.HasAccessToken())
66 return;
67
68 using namespace audacity::network_manager;
69
70 Request request(GetServiceConfig().GetAPIUrl("/me"));
71
72 request.setHeader(
74 std::string(oauthService.GetAccessToken()));
75
76 request.setHeader(
78
79 auto response = NetworkManager::GetInstance().doGet(request);
80
81 response->setRequestFinishedCallback(
82 [response, this](auto)
83 {
84 const auto httpCode = response->getHTTPCode();
85
86 if (httpCode != 200)
87 return;
88
89 const auto body = response->readAll<std::string>();
90
91 using namespace rapidjson;
92
93 Document document;
94 document.Parse(body.data(), body.size());
95
96 if (!document.IsObject())
97 return;
98
99 const auto username = document["username"].GetString();
100 const auto avatar = document["avatar"].GetString();
101 const auto profileName = document["profile"]["name"].GetString();
102
104 [this, username = std::string(username),
105 profileName = std::string(profileName),
106 avatar = std::string(avatar)]()
107 {
110
111 gPrefs->Flush();
112
113 DownloadAvatar(avatar);
114
115 Publish({});
116 });
117 });
118}
119
121{
123 [this]()
124 {
125 // No valid data was present, do not spam Publish()
126 if (GetUserSlug().empty())
127 return;
128
129 userName.Write({});
130 displayName.Write({});
131 avatarEtag.Write({});
132
133 gPrefs->Flush();
134
135 Publish({});
136 });
137}
138
140{
141 static UserService userService;
142 return userService;
143}
144
145void UserService::DownloadAvatar(std::string_view url)
146{
147 const auto avatarPath = MakeAvatarPath();
148 const auto avatarTempPath = avatarPath + ".tmp";
149
150 if (url.empty())
151 {
152 if (wxFileExists(avatarPath))
153 wxRemoveFile(avatarPath);
154
155 return;
156 }
157
158 std::shared_ptr<wxFile> avatarFile = std::make_shared<wxFile>();
159
160 if (!avatarFile->Create(avatarTempPath, true))
161 return;
162
163 using namespace audacity::network_manager;
164
165 auto request = Request(std::string(url));
166
167 const auto etag = audacity::ToUTF8(avatarEtag.Read());
168
169 // If ETag is present - use it to prevent re-downloading the same file
170 if (!etag.empty() && wxFileExists(avatarPath))
171 request.setHeader(common_headers::IfNoneMatch, etag);
172
173 auto response = NetworkManager::GetInstance().doGet(request);
174
175 response->setOnDataReceivedCallback(
176 [response, avatarFile](auto)
177 {
178 std::vector<char> buffer(response->getBytesAvailable());
179
180 size_t bytes = response->readData(buffer.data(), buffer.size());
181
182 avatarFile->Write(buffer.data(), buffer.size());
183 });
184
185 response->setRequestFinishedCallback(
186 [response, avatarFile, avatarPath, avatarTempPath, this](auto)
187 {
188 avatarFile->Close();
189
190 const auto httpCode = response->getHTTPCode();
191
192 if (httpCode != 200)
193 {
194 // For any response except 200 just remove the temp file
195 wxRemoveFile(avatarTempPath);
196 return;
197 }
198
199 const auto etag = response->getHeader("ETag");
200 const auto oldPath = avatarPath + ".old";
201
202 if (wxFileExists(avatarPath))
203 if (!wxRenameFile(avatarPath, oldPath))
204 return;
205
206 if (!wxRenameFile(avatarTempPath, avatarPath))
207 {
208 // Try at least to get it back...
209 wxRenameFile(oldPath, avatarPath);
210 return;
211 }
212
213 if (wxFileExists(oldPath))
214 wxRemoveFile(oldPath);
215
217 [this, etag]()
218 {
219 avatarEtag.Write(etag);
220 gPrefs->Flush();
221
222 Publish({});
223 });
224 });
225}
226
228{
229 return displayName.Read();
230}
231
233{
234 return userName.Read();
235}
236
238{
239 auto path = MakeAvatarPath();
240
241 if (!wxFileExists(path))
242 return {};
243
244 return path;
245}
246
247} // namespace 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:257
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:205
Specialization of Setting for strings.
Definition: Prefs.h:368
virtual bool Flush() noexcept=0
Request & setHeader(const std::string &name, std::string value)
Definition: Request.cpp:46
Service for providing information about the user profile.
Definition: UserService.h:28
void DownloadAvatar(std::string_view url)
wxString GetDisplayName() const
Get the user name to display in the dialog.
wxString GetAvatarPath() const
Gets a path to the avatar.
void UpdateUserData()
Request the service to update the data.
Definition: UserService.cpp:61
wxString GetUserSlug() const
"Slug" used to construct shareable URLs
void ClearUserData()
Reset the user profile data.
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:208
FILES_API FilePath ConfigDir()
Audacity user config directory.
FrameStatistics & GetInstance() noexcept
std::string ToUTF8(const std::wstring &wstr)
wxString ToWXString(const std::string &str)
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.
OAuthService & GetOAuthService()
Returns the instance of the OAuthService.
UserService & GetUserService()