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 "Project.h"
20#include "ProjectFileIO.h"
21#include "ProjectManager.h"
22#include "ProjectWindow.h"
23#include "UriParser.h"
24
29
31
33{
34namespace
35{
36
38{
40 XO("Open from audio.com"), XO("Synchronizing project"),
42}
43
45{
46 return [&dialog](double progress)
47 {
48 return dialog.Poll(static_cast<unsigned>(progress * 10000), 10000ULL) ==
50 };
51}
52
53template<typename T>
54T GetResult(std::future<T>& future)
55{
56 while (future.wait_for(std::chrono::milliseconds(50)) !=
57 std::future_status::ready)
59
60 return future.get();
61}
62
64{
65 if (
68 return false;
69
71 {
72 wxLogError(
73 "Opening of the cloud project was canceled", result.Result.Content);
74 return true;
75 }
76
78 {
79 UnsyncedProjectDialog { nullptr, false }.ShowDialog();
80 return true;
81 }
82
84
85 wxLogError("Failed to open cloud project: %s", result.Result.Content);
86
87 return true;
88}
89
91{
92 None,
93 Remote,
94 Local,
95 Stop
96};
97
100{
102 return ConflictResolution::None;
103
106 };
107 const auto resolution = dialog.ShowDialog();
108
110 return ConflictResolution::Stop;
111
113 return ConflictResolution::Local;
114
116 return ConflictResolution::Remote;
117
118 assert(false);
119 return ConflictResolution::Stop;
120}
121
123{
124 wxLogMessage(
125 "Transfer stats: %f Kb transferred, %f secs",
126 stats.BytesTransferred / 1024.0,
127 std::chrono::duration<float>(stats.TransferDuration).count());
128}
129
130} // namespace
131
133{
134 return AllProjects {}.empty() ? nullptr : AllProjects {}.rbegin()->get();
135}
136
137AudacityProject* GetOpenedProject(std::string_view projectId)
138{
139 const auto begin = AllProjects {}.begin(), end = AllProjects {}.end();
140 auto iter = std::find_if(
141 begin, end,
142 [projectId](const AllProjects::value_type& ptr) {
143 return projectId ==
145 });
146
147 if (iter == end)
148 return nullptr;
149
150 return iter->get();
151}
152
154 AudacityProject* potentialTarget, std::string_view projectId,
155 std::string_view snapshotId, CloudSyncService::SyncMode mode)
156{
158
159 auto authResult = PerformBlockingAuth(potentialTarget);
160
161 if (authResult.Result != AuthResult::Status::Authorised)
162 {
163 LinkFailedDialog dialog { potentialTarget != nullptr ?
164 &ProjectWindow::Get(*potentialTarget) :
165 nullptr };
166 dialog.ShowModal();
167 return nullptr;
168 }
169
170 auto openedProject = GetOpenedProject(projectId);
171
172 if (openedProject != nullptr && mode != CloudSyncService::SyncMode::ForceNew)
173 {
175 {
176 ProjectWindow::Get(*openedProject).Close(true);
177 }
178 else
179 {
180 auto snapshotIdFuture =
181 CloudSyncService::Get().GetHeadSnapshotID(std::string(projectId));
182
183 auto snapshotIdResult = GetResult(snapshotIdFuture);
184
185 if (std::holds_alternative<std::string>(snapshotIdResult))
186 {
187 const auto headSnapshotId =
188 *std::get_if<std::string>(&snapshotIdResult);
189
190 if (
191 headSnapshotId !=
193 {
194 auto dialog = std::make_unique<ProjectVersionConflictDialog>(
196
197 const auto result = dialog->ShowDialog();
198 dialog.reset();
199
200 if (
202 {
203 auto newProject = ProjectManager::New();
205 newProject, projectId, headSnapshotId,
207 }
208 }
209 }
210
211 ProjectWindow::Get(*openedProject).Raise();
212 return openedProject;
213 }
214 }
215
216 auto progressDialog = MakeProgress();
217
218 auto future = CloudSyncService::Get().OpenFromCloud(
219 std::string(projectId), std::string(snapshotId), mode,
220 MakePoller(*progressDialog));
221
222 auto result = GetResult(future);
223 LogTransferStats(result.Stats);
224
225 progressDialog.reset();
226
227 const auto conflictResolution =
228 GetConfilctResolution(potentialTarget, result);
229
230 if (conflictResolution == ConflictResolution::Stop)
231 return nullptr;
232
233 if (conflictResolution == ConflictResolution::Remote)
234 {
236 potentialTarget, projectId, snapshotId,
238 }
239
240 if (HandleFailure(result))
241 return nullptr;
242
244 GetPotentialTarget(), audacity::ToWXString(result.ProjectPath), true,
245 false);
246
247 if (project != nullptr && mode == CloudSyncService::SyncMode::ForceNew)
249
250 return project;
251}
252
254 AudacityProject* potentialTarget, std::string_view projectId,
255 std::string_view snapshotId, bool forceNew)
256{
258 potentialTarget, projectId, snapshotId,
261}
262
264 AudacityProject& project, std::string_view path, bool force)
265{
267
268 if (!CloudSyncService::Get().IsCloudProject(std::string(path)))
269 return true;
270
271 auto authResult = PerformBlockingAuth(&project);
272
273 if (authResult.Result != AuthResult::Status::Authorised)
274 {
276 dialog.ShowModal();
277 return false;
278 }
279
280 auto progressDialog = MakeProgress();
281
282 auto future = CloudSyncService::Get().SyncProject(
283 project, std::string(path), force, MakePoller(*progressDialog));
284
285 auto result = GetResult(future);
286 LogTransferStats(result.Stats);
287
288 progressDialog.reset();
289
290 const auto conflictResolution = GetConfilctResolution(&project, result);
291
292 if (conflictResolution == ConflictResolution::Stop)
293 return false;
294
295 if (conflictResolution == ConflictResolution::Remote)
296 return SyncCloudProject(project, path, true);
297
298 if (HandleFailure(result))
299 return false;
300
301 return true;
302}
303
304bool HandleProjectLink(std::string_view uri)
305{
307
308 const auto parsedUri = ParseUri(uri);
309
310 if (parsedUri.Scheme != "audacity" || parsedUri.Host != "open")
311 return false;
312
313 const auto queryParameters = ParseUriQuery(parsedUri.Query);
314
315 if (queryParameters.empty())
316 return false;
317
318 const auto projectId = queryParameters.find("projectId");
319
320 if (projectId == queryParameters.end())
321 return false;
322
323 const auto snapshotId = queryParameters.find("snapshotId");
324
325 const auto forceNew = queryParameters.find("new") != queryParameters.end();
326
328 GetPotentialTarget(), projectId->second,
329 snapshotId != queryParameters.end() ? snapshotId->second :
330 std::string_view {},
331 forceNew);
332
333 return true;
334}
335
337{
338 auto& projectCloudExtension = ProjectCloudExtension::Get(project);
339
340 if (!projectCloudExtension.IsCloudProject())
341 return;
342
344 [&project,
345 projectId = std::string(projectCloudExtension.GetCloudProjectId())]
346 {
347 auto newProject = ProjectManager::New();
348 ProjectWindow::Get(project).Close(true);
349 OpenProjectFromCloud(
350 newProject, projectId, {},
352 });
353}
354} // namespace audacity::cloud::audiocom::sync
Toolkit-neutral facade for basic user interface services.
#define ASSERT_MAIN_THREAD()
Definition: BasicUI.h:406
Declare functions to perform UTF-8 to std::wstring conversions.
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:157
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:212
void Yield()
Dispatch waiting events, including actions enqueued by CallAfter.
Definition: BasicUI.cpp:223
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:294
ConflictResolution GetConfilctResolution(AudacityProject *project, const ProjectSyncResult &result)
bool SyncCloudProject(AudacityProject &project, std::string_view path, bool force)
void ReopenProject(AudacityProject &project)
bool HandleProjectLink(std::string_view uri)
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, 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