Audacity 3.2.0
CloudProjectOpenUtils.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 CloudProjectOpenUtils.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
12
13#include <wx/log.h>
14
16#include "BasicUI.h"
17#include "CloudSyncService.h"
18#include "CodeConversions.h"
19#include "ExportUtils.h"
20#include "Project.h"
21#include "ProjectFileIO.h"
22#include "ProjectManager.h"
23#include "ProjectWindow.h"
24#include "UriParser.h"
25
30
32
34{
35namespace
36{
37
39{
41 XO("Open trace audio.com"), XO("Synchronizing project"),
43}
44
46{
47 return [&dialog](double progress)
48 {
49 return dialog.Poll(static_cast<unsigned>(progress * 10000), 10000ULL) ==
51 };
52}
53
54template<typename T>
55T GetResult(std::future<T>& future)
56{
57 while (future.wait_for(std::chrono::milliseconds(50)) !=
58 std::future_status::ready)
60
61 return future.get();
62}
63
65{
66 if (
69 return false;
70
72 {
73 wxLogError(
74 "Opening of the cloud project was canceled", result.Result.Content);
75 return true;
76 }
77
79 {
80 UnsyncedProjectDialog { nullptr, false }.ShowDialog();
81 return true;
82 }
83
85
86 wxLogError("Failed to open cloud project: %s", result.Result.Content);
87
88 return true;
89}
90
92{
93 None,
94 Remote,
95 Local,
96 Stop
97};
98
101{
103 return ConflictResolution::None;
104
107 };
108 const auto resolution = dialog.ShowDialog();
109
111 return ConflictResolution::Stop;
112
114 return ConflictResolution::Local;
115
117 return ConflictResolution::Remote;
118
119 assert(false);
120 return ConflictResolution::Stop;
121}
122
124{
125 wxLogMessage(
126 "Transfer stats: %f Kb transferred, %f secs",
127 stats.BytesTransferred / 1024.0,
128 std::chrono::duration<float>(stats.TransferDuration).count());
129}
130
131} // namespace
132
134{
135 return AllProjects {}.empty() ? nullptr : AllProjects {}.rbegin()->get();
136}
137
138AudacityProject* GetOpenedProject(std::string_view projectId)
139{
140 const auto begin = AllProjects {}.begin(), end = AllProjects {}.end();
141 auto iter = std::find_if(
142 begin, end,
143 [projectId](const AllProjects::value_type& ptr) {
144 return projectId ==
146 });
147
148 if (iter == end)
149 return nullptr;
150
151 return iter->get();
152}
153
155 AudacityProject* potentialTarget, std::string_view projectId,
156 std::string_view snapshotId, CloudSyncService::SyncMode mode)
157{
159
160 auto authResult =
162
163 if (authResult.Result != AuthResult::Status::Authorised)
164 {
165 LinkFailedDialog dialog { potentialTarget != nullptr ?
166 &ProjectWindow::Get(*potentialTarget) :
167 nullptr,
169 dialog.ShowModal();
170 return nullptr;
171 }
172
173 auto openedProject = GetOpenedProject(projectId);
174
175 if (openedProject != nullptr && mode != CloudSyncService::SyncMode::ForceNew)
176 {
178 {
179 ProjectWindow::Get(*openedProject).Close(true);
180 }
181 else
182 {
183 auto snapshotIdFuture =
184 CloudSyncService::Get().GetHeadSnapshotID(std::string(projectId));
185
186 auto snapshotIdResult = GetResult(snapshotIdFuture);
187
188 if (std::holds_alternative<std::string>(snapshotIdResult))
189 {
190 const auto headSnapshotId =
191 *std::get_if<std::string>(&snapshotIdResult);
192
193 if (
194 headSnapshotId !=
196 {
197 auto dialog = std::make_unique<ProjectVersionConflictDialog>(
199
200 const auto result = dialog->ShowDialog();
201 dialog.reset();
202
203 if (
205 {
206 auto newProject = ProjectManager::New();
208 newProject, projectId, headSnapshotId,
210 }
211 }
212 }
213
214 ProjectWindow::Get(*openedProject).Raise();
215 return openedProject;
216 }
217 }
218
219 auto progressDialog = MakeProgress();
220
221 auto future = CloudSyncService::Get().OpenFromCloud(
222 std::string(projectId), std::string(snapshotId), mode,
223 MakePoller(*progressDialog));
224
225 auto result = GetResult(future);
226 LogTransferStats(result.Stats);
227
228 progressDialog.reset();
229
230 const auto conflictResolution =
231 GetConfilctResolution(potentialTarget, result);
232
233 if (conflictResolution == ConflictResolution::Stop)
234 return nullptr;
235
236 if (conflictResolution == ConflictResolution::Remote)
237 {
239 potentialTarget, projectId, snapshotId,
241 }
242
243 if (HandleFailure(result))
244 return nullptr;
245
247 GetPotentialTarget(), audacity::ToWXString(result.ProjectPath), true,
248 false);
249
250 if (project != nullptr && mode == CloudSyncService::SyncMode::ForceNew)
252
253 return project;
254}
255
257 AudacityProject* potentialTarget, std::string_view projectId,
258 std::string_view snapshotId, bool forceNew)
259{
261 potentialTarget, projectId, snapshotId,
264}
265
267 AudacityProject& project, std::string_view path, AudiocomTrace trace,
268 bool force)
269{
271
272 if (!CloudSyncService::Get().IsCloudProject(std::string(path)))
273 return true;
274
275 auto authResult = PerformBlockingAuth(&project, trace);
276
277 if (authResult.Result != AuthResult::Status::Authorised)
278 {
279 LinkFailedDialog dialog { &ProjectWindow::Get(project), trace };
280 dialog.ShowModal();
281 return false;
282 }
283
284 auto progressDialog = MakeProgress();
285
286 auto future = CloudSyncService::Get().SyncProject(
287 project, std::string(path), force, MakePoller(*progressDialog));
288
289 auto result = GetResult(future);
290 LogTransferStats(result.Stats);
291
292 progressDialog.reset();
293
294 const auto conflictResolution = GetConfilctResolution(&project, result);
295
296 if (conflictResolution == ConflictResolution::Stop)
297 return false;
298
299 if (conflictResolution == ConflictResolution::Remote)
300 return SyncCloudProject(project, path, trace, true);
301
302 if (HandleFailure(result))
303 return false;
304
305 return true;
306}
307
308bool HandleProjectLink(std::string_view uri)
309{
311
312 const auto parsedUri = ParseUri(uri);
313
314 if (parsedUri.Scheme != "audacity" || parsedUri.Host != "open")
315 return false;
316
317 const auto queryParameters = ParseUriQuery(parsedUri.Query);
318
319 if (queryParameters.empty())
320 return false;
321
322 const auto projectId = queryParameters.find("projectId");
323
324 if (projectId == queryParameters.end())
325 return false;
326
327 const auto snapshotId = queryParameters.find("snapshotId");
328
329 const auto forceNew = queryParameters.find("new") != queryParameters.end();
330
332 GetPotentialTarget(), projectId->second,
333 snapshotId != queryParameters.end() ? snapshotId->second :
334 std::string_view {},
335 forceNew);
336
337 return true;
338}
339
341{
342 auto& projectCloudExtension = ProjectCloudExtension::Get(project);
343
344 if (!projectCloudExtension.IsCloudProject())
345 return;
346
348 [&project,
349 projectId = std::string(projectCloudExtension.GetCloudProjectId())]
350 {
351 auto newProject = ProjectManager::New();
352 ProjectWindow::Get(project).Close(true);
353 OpenProjectFromCloud(
354 newProject, projectId, {},
356 });
357}
358} // namespace audacity::cloud::audiocom::sync
Toolkit-neutral facade for basic user interface services.
#define ASSERT_MAIN_THREAD()
Definition: BasicUI.h:414
Declare functions to perform UTF-8 to std::wstring conversions.
AudiocomTrace
Definition: ExportUtils.h:27
XO("Cut/Copy/Paste")
const auto project
QueryFields ParseUriQuery(std::string_view query, std::string_view delimiter) noexcept
Parses URI query and returns QueryFields structure with parsed fields.
Definition: UriParser.cpp:59
UriFields ParseUri(std::string_view uri) noexcept
Definition: UriParser.cpp:9
const_iterator end() const
Definition: Project.cpp:27
Container::value_type value_type
Definition: Project.h:57
const_iterator begin() const
Definition: Project.cpp:22
const_reverse_iterator rbegin() const
Definition: Project.cpp:32
bool empty() const
Definition: Project.h:47
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Abstraction of a progress dialog with well defined time-to-completion estimate.
Definition: BasicUI.h:164
virtual ProgressResult Poll(unsigned long long numerator, unsigned long long denominator, const TranslatableString &message={})=0
Update the bar and poll for clicks. Call only on the main thread.
static ProjectFileIO & Get(AudacityProject &project)
static AudacityProject * New()
static AudacityProject * OpenProject(AudacityProject *pGivenProject, const FilePath &fileNameArg, bool addtohistory, bool reuseNonemptyProject)
Open a file into an AudacityProject, returning the project, or nullptr for failure.
static ProjectWindow & Get(AudacityProject &project)
GetHeadSnapshotIDFuture GetHeadSnapshotID(std::string_view projectId)
SyncFuture SyncProject(AudacityProject &project, const std::string &path, bool forceSync, sync::ProgressCallback callback)
SyncFuture OpenFromCloud(std::string projectId, std::string snapshotId, SyncMode mode, sync::ProgressCallback callback)
Open the project from the cloud. This operation is asynchronous.
DialogButtonIdentifier ShowDialog(std::function< DialogButtonIdentifier()> poller={})
static ProjectCloudExtension & Get(AudacityProject &project)
static void OnOpen(const CloudSyncError &error)
@ ProgressShowCancel
Definition: BasicUI.h:142
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:214
void Yield()
Dispatch waiting events, including actions enqueued by CallAfter.
Definition: BasicUI.cpp:225
std::unique_ptr< ProgressDialog > MakeProgress(const TranslatableString &title, const TranslatableString &message, unsigned flags=(ProgressShowStop|ProgressShowCancel), const TranslatableString &remainingLabelText={})
Create and display a progress dialog.
Definition: BasicUI.h:302
ConflictResolution GetConfilctResolution(AudacityProject *project, const ProjectSyncResult &result)
void ReopenProject(AudacityProject &project)
bool HandleProjectLink(std::string_view uri)
bool SyncCloudProject(AudacityProject &project, std::string_view path, AudiocomTrace trace, bool force)
AudacityProject * OpenProjectFromCloud(AudacityProject *potentialTarget, std::string_view projectId, std::string_view snapshotId, CloudSyncService::SyncMode mode)
AudacityProject * GetOpenedProject(std::string_view projectId)
AuthResult PerformBlockingAuth(AudacityProject *project, AudiocomTrace trace, const TranslatableString &alternativeActionLabel)
wxString ToWXString(const std::string &str)
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
const char * begin(const char *str) noexcept
Definition: StringUtils.h:101