Audacity 3.2.0
CloudProjectFileIOExtensions.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 CloudProjectFileIOExtensions.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
11
13
17
18#include "OAuthService.h"
19#include "ServiceConfig.h"
20#include "UserService.h"
21
27
28#include "sync/CloudSyncDTO.h"
32
33#include "BasicUI.h"
34#include "CodeConversions.h"
35#include "Project.h"
36#include "ProjectFileIO.h"
38#include "ProjectFileManager.h"
39#include "ProjectWindow.h"
40
41namespace
42{
43using namespace audacity::cloud::audiocom;
45
47{
49 OnOpen(AudacityProject& project, const std::string& path) override
50 {
53 }
54
56 {
57 auto& projectCloudExtenstion = ProjectCloudExtension::Get(project);
58 projectCloudExtenstion.OnLoad();
59
60 if (projectCloudExtenstion.IsCloudProject())
62 projectCloudExtenstion,
64 }
65
67 AudacityProject& project, std::string name, std::string filePath,
68 const ProjectSaveCallback& projectSaveCallback, bool fileRenamed)
69 {
70 auto& projectCloudExtension = ProjectCloudExtension::Get(project);
71
72 projectCloudExtension.OnSyncStarted();
73
74 auto future = LocalProjectSnapshot::Create(
75 GetServiceConfig(), GetOAuthService(), projectCloudExtension, name,
76 mUploadMode);
77
78 mUploadMode = UploadMode::Normal;
79
80 // Do we need UI here?
81 // while (future.wait_for(std::chrono::milliseconds(50)) !=
82 // std::future_status::ready)
83 // BasicUI::Yield();
84
85 auto result = future.get();
86
87 if (!result.Response)
88 // Prevent any updates to the file to preserve the correct state.
89 // Errors would be handled by the UI extension
91
92 if (!projectSaveCallback(audacity::ToUTF8(filePath), fileRenamed))
93 {
94 if (result.Operation)
95 result.Operation->Abort();
96 else
97 projectCloudExtension.OnSyncCompleted(
98 nullptr, CloudSyncError { CloudSyncError::Aborted });
99 // Something has failed horrible during the save
101 }
102
103 if (mSnapshotCallback)
104 {
105 mSnapshotCallback(*result.Response);
106 mSnapshotCallback = {};
107 }
108
110 }
111
113 AudacityProject& project, const ProjectSaveCallback& projectSaveCallback)
114 {
115 auto authResult = PerformBlockingAuth(&project);
116
117 if (authResult.Result == AuthResult::Status::Failure)
118 {
120 dialog.ShowModal();
121 // Pretend we have canceled the save
123 }
124 else if (authResult.Result == AuthResult::Status::Cancelled)
125 {
127 }
128 else if (authResult.Result == AuthResult::Status::UseAlternative)
129 {
132 }
133
134 return PerformCloudSave(
135 project, audacity::ToUTF8(project.GetProjectName()),
137 projectSaveCallback, false);
138 }
139
142 const ProjectSaveCallback& projectSaveCallback) override
143 {
144 auto& projectCloudExtension = ProjectCloudExtension::Get(project);
145 auto& projectFileIO = ProjectFileIO::Get(project);
146
147 const bool isTemporary = projectFileIO.IsTemporary();
148 const bool isCloudProject = projectCloudExtension.IsCloudProject();
149
150 const bool pendingCloudSave = mForceCloudSave;
151 mForceCloudSave = false;
152
153 auto parent = &ProjectWindow::Get(project);
154
155 // Check location first
156 if (isTemporary && !pendingCloudSave)
157 {
158 CloudLocationDialog cloudLocationDialog { parent,
159 LocationDialogType::Save };
160 const auto saveAction = cloudLocationDialog.ShowDialog();
161
162 // Not doing a cloud save
163 if (saveAction == LocationDialogResult::Local)
165 else if (saveAction == LocationDialogResult::Cancel)
167 }
168
169 // For regular projects - do nothing
170 if (!isTemporary && !pendingCloudSave && !isCloudProject)
172
173 if (!isTemporary)
174 return SaveCloudProject(project, projectSaveCallback);
175
178 project.GetProjectName(), parent, false);
179
180 if (result.first == CloudProjectPropertiesDialog::Action::Cancel)
181 // Suppress the Save function completely
183 else if (
184 result.first == CloudProjectPropertiesDialog::Action::SaveLocally)
185 // Just let the things flow as usual
187
188 const auto dir = CloudProjectsSavePath.Read();
189 FileNames::MkDir(dir);
190
191 const auto filePath = sync::MakeSafeProjectPath(dir, result.second);
192
193 return PerformCloudSave(
194 project, result.second, audacity::ToUTF8(filePath),
195 projectSaveCallback, true);
196 }
197
199 {
200 auto& projectCloudExtension = ProjectCloudExtension::Get(project);
201
202 if (!projectCloudExtension.IsCloudProject())
204
205 return OnCloseHook::Call(project) ? OnCloseAction::Continue :
207 }
208
210 AudacityProject& project, const ProjectSerializer& serializer) override
211 {
212 auto& projectCloudExtension = ProjectCloudExtension::Get(project);
213
214 if (!projectCloudExtension.IsCloudProject())
215 return;
216
217 projectCloudExtension.OnUpdateSaved(serializer);
218
219 const int savesCount = projectCloudExtension.GetSavesCount();
220
221 if (savesCount < 2)
222 {
223 SyncInBackroundDialog { &project }.ShowDialog();
224 }
225
226 if (projectCloudExtension.IsFirstSyncDialogShown())
227 return;
228
230 [weakProject = project.weak_from_this()]
231 {
232 auto project = weakProject.lock();
233
234 if (!project)
235 return true;
236
237 return ProjectCloudExtension::Get(*project)
238 .GetCurrentSyncStatus() == ProjectSyncStatus::Synced;
239 },
240 [weakProject = project.weak_from_this()]
241 {
242 auto project = weakProject.lock();
243
244 if (
245 !project ||
246 ProjectCloudExtension::Get(*project).IsFirstSyncDialogShown())
247 return;
248
249 ProjectCloudExtension::Get(*project).SetFirstSyncDialogShown(true);
250
251 const auto result =
252 SyncSucceededDialog { project.get() }.ShowDialog();
253
254 if (result == SyncSucceededDialog::ViewOnlineIdentifier())
256 ProjectCloudExtension::Get(*project).GetCloudProjectPage()));
257 });
258 }
259
260 bool
261 IsBlockLocked(const AudacityProject& project, int64_t blockId) const override
262 {
263 return ProjectCloudExtension::Get(project).IsBlockLocked(blockId);
264 }
265
266public:
268 {
269 mForceCloudSave = true;
270 }
271
273 {
274 mUploadMode = mode;
275 }
276
278 {
279 mSnapshotCallback = std::move(callback);
280 }
281
282private:
283 // Snapshot callback for the next save
285 // Upload mode for the next save
286 UploadMode mUploadMode { UploadMode::Normal };
287 // Forces the next save to be a cloud save
288 bool mForceCloudSave {};
289};
290
292{
293 static IOExtension extension;
294 return extension;
295}
296
298} // namespace
299
301{
304 CreateSnapshotCallback snapshotCallback)
305{
307
308 auto& projectCloudExtension = ProjectCloudExtension::Get(project);
309
310 auto& ioExtension = GetExtension();
311
312 ioExtension.ForceCloudSave();
313 ioExtension.SetUploadModeForNextSave(mode);
314 ioExtension.SetSnapshotCallbackForNextSave(std::move(snapshotCallback));
315
317}
318
320{
321 // TODO: Delete the old file immediately?
323}
324
325} // 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.
const TranslatableString name
Definition: Distortion.cpp:76
std::function< bool(const std::string &path, bool nameChanged)> ProjectSaveCallback
OnOpenAction
Action the ProjectManager should take after the open hooks were called.
@ Continue
ProjectManager should continue with the open.
@ Cancel
Open was cancelled by the extension.
OnSaveAction
Action the ProjectManager should take after the save hooks were called.
@ Handled
Save was handled by the extension.
@ Continue
Save was not handled by the extension.
@ Cancelled
Save was cancelled by the extension.
OnCloseAction
Action the ProjectManager should take after the close hooks were called.
@ Veto
Extension vetoed the close.
@ Continue
Extension did not veto the close.
const auto project
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Set of hooks for project file I/O.
static ProjectFileIO & Get(AudacityProject &project)
const FilePath & GetFileName() const
bool SaveAs(bool allowOverwrite=false)
static ProjectFileManager & Get(AudacityProject &project)
a class used to (de)serialize the project catalog
static ProjectWindow & Get(AudacityProject &project)
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
OnSaveAction PerformCloudSave(AudacityProject &project, std::string name, std::string filePath, const ProjectSaveCallback &projectSaveCallback, bool fileRenamed)
OnSaveAction SaveCloudProject(AudacityProject &project, const ProjectSaveCallback &projectSaveCallback)
OnSaveAction OnSave(AudacityProject &project, const ProjectSaveCallback &projectSaveCallback) override
This hook is called before the project is saved.
bool IsBlockLocked(const AudacityProject &project, int64_t blockId) const override
This hook is called to check if a block is locked.
OnCloseAction OnClose(AudacityProject &project) override
This hook is called before the project is closed.
void OnLoad(AudacityProject &project) override
This hook is called after the project is loaded.
OnOpenAction OnOpen(AudacityProject &project, const std::string &path) override
This hook is called before the project is opened.
void OnUpdateSaved(AudacityProject &project, const ProjectSerializer &serializer) override
This hook is called after the project blob is saved.
static ProjectCloudExtension & Get(AudacityProject &project)
bool OpenInDefaultBrowser(const wxString &url)
Open an URL in default browser.
Definition: BasicUI.cpp:245
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:201
IMPORT_EXPORT_API ExportResult Show(ExportTask exportTask)
FILES_API wxString MkDir(const wxString &Str)
bool SyncCloudProject(AudacityProject &project, std::string_view path, bool force)
bool ResaveLocally(AudacityProject &project)
void SaveToCloud(AudacityProject &project, UploadMode mode, CreateSnapshotCallback snapshotCallback)
void ResumeProjectUpload(ProjectCloudExtension &projectCloudExtension, std::function< void()> onBeforeUploadStarts)
wxString MakeSafeProjectPath(const wxString &rootDir, const wxString &projectName)
std::function< void(const CreateSnapshotResponse &)> CreateSnapshotCallback
void ShowDialogOn(std::function< bool()> condition, std::function< void()> dialogFactory)
UserService & GetUserService()
OAuthService & GetOAuthService()
Returns the instance of the OAuthService.
AuthResult PerformBlockingAuth(AudacityProject *project, const TranslatableString &alternativeActionLabel)
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.
std::string ToUTF8(const std::wstring &wstr)
wxString ToWXString(const std::string &str)