Skip to content

Commit 42bc471

Browse files
committed
added on project save callback for web
1 parent aefc6f7 commit 42bc471

File tree

14 files changed

+174
-30
lines changed

14 files changed

+174
-30
lines changed

src/app/webapi_export.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@
2727
using namespace mu::webbridge;
2828

2929
extern "C" {
30-
void onclickTest1(int num) { WebApi::onclickTest1(num); }
31-
void load(const void* source, unsigned int len) { WebApi::load(source, len); }
30+
void onclickTest1(int num) { WebApi::instance()->onclickTest1(num); }
31+
void load(const void* source, unsigned int len) { WebApi::instance()->load(source, len); }
3232
}

src/appshell_web/view/appmenumodel.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ void AppMenuModel::load()
5454
AbstractMenuModel::load();
5555

5656
MenuItemList items {
57+
makeFileMenu(),
5758
makeEditMenu(),
5859
makeViewMenu(),
5960
makeAddMenu(),
@@ -110,6 +111,15 @@ MenuItem* AppMenuModel::makeMenuItem(const ActionCode& actionCode, MenuItemRole
110111
return item;
111112
}
112113

114+
muse::uicomponents::MenuItem* AppMenuModel::makeFileMenu()
115+
{
116+
MenuItemList fileItems {
117+
makeMenuItem("file-save")
118+
};
119+
120+
return makeMenu(TranslatableString("appshell/menu/file", "&File"), fileItems, "menu-file");
121+
}
122+
113123
MenuItem* AppMenuModel::makeEditMenu()
114124
{
115125
MenuItemList editItems {

src/appshell_web/view/appmenumodel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class AppMenuModel : public muse::uicomponents::AbstractMenuModel
6767
using muse::uicomponents::AbstractMenuModel::makeMenuItem;
6868
muse::uicomponents::MenuItem* makeMenuItem(const muse::actions::ActionCode& actionCode, muse::uicomponents::MenuItemRole role);
6969

70+
muse::uicomponents::MenuItem* makeFileMenu();
7071
muse::uicomponents::MenuItem* makeEditMenu();
7172
muse::uicomponents::MenuItem* makeViewMenu();
7273
muse::uicomponents::MenuItem* makeAddMenu();

src/framework/stubs/extensions/extensionsproviderstub.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ std::vector<ExecPoint> ExtensionsProviderStub::execPoints(const Uri&) const
8888

8989
Ret ExtensionsProviderStub::performPoint(const ExecPointName&)
9090
{
91-
return muse::make_ret(Ret::Code::NotSupported);
91+
return muse::make_ret(Ret::Code::Ok);
9292
}
9393

9494
void ExtensionsProviderStub::performPointAsync(const ExecPointName&)

src/project/inotationproject.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424

2525
#include <memory>
2626

27-
#include "io/path.h"
28-
#include "types/ret.h"
27+
#include "global/io/path.h"
28+
#include "global/types/ret.h"
29+
#include "global/async/channel.h"
2930

3031
#include "iprojectaudiosettings.h"
3132
#include "notation/imasternotation.h"
@@ -69,8 +70,10 @@ class INotationProject
6970
virtual bool needAutoSave() const = 0;
7071
virtual void setNeedAutoSave(bool val) = 0;
7172

72-
virtual muse::Ret save(
73-
const muse::io::path_t& path = muse::io::path_t(), SaveMode saveMode = SaveMode::Save, bool createBackup = true) = 0;
73+
virtual muse::Ret save(const muse::io::path_t& path = muse::io::path_t(), SaveMode saveMode = SaveMode::Save,
74+
bool createBackup = true) = 0;
75+
virtual muse::async::Channel<muse::io::path_t, SaveMode> saveComplited() const = 0;
76+
7477
virtual muse::Ret writeToDevice(QIODevice* device) = 0;
7578

7679
virtual ProjectMeta metaInfo() const = 0;

src/project/internal/notationproject.cpp

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -466,13 +466,16 @@ Ret NotationProject::save(const muse::io::path_t& path, SaveMode saveMode, bool
466466
{
467467
TRACEFUNC;
468468

469+
Ret ret;
470+
muse::io::path_t savePath = path;
471+
469472
switch (saveMode) {
470-
case SaveMode::SaveSelection:
471-
return saveSelectionOnScore(path);
473+
case SaveMode::SaveSelection: {
474+
ret = saveSelectionOnScore(savePath);
475+
} break;
472476
case SaveMode::Save:
473477
case SaveMode::SaveAs:
474478
case SaveMode::SaveCopy: {
475-
muse::io::path_t savePath = path;
476479
if (savePath.empty()) {
477480
IF_ASSERT_FAILED(!m_path.empty()) {
478481
return false;
@@ -486,30 +489,40 @@ Ret NotationProject::save(const muse::io::path_t& path, SaveMode saveMode, bool
486489
// Whether a backup file will be created depends on both the caller's and user's will
487490
bool shouldCreateBackup = createBackup && configuration()->createBackupBeforeSaving();
488491

489-
Ret ret = saveScore(savePath, suffix, shouldCreateBackup);
492+
ret = saveScore(savePath, suffix, shouldCreateBackup);
490493
if (ret) {
491494
if (saveMode != SaveMode::SaveCopy) {
492495
markAsSaved(savePath);
493496
}
494497
}
495-
496-
return ret;
497-
}
498-
case SaveMode::AutoSave:
499-
std::string suffix = io::suffix(path);
498+
} break;
499+
case SaveMode::AutoSave: {
500+
std::string suffix = io::suffix(savePath);
500501
if (suffix == IProjectAutoSaver::AUTOSAVE_SUFFIX) {
501-
suffix = io::suffix(io::completeBasename(path));
502+
suffix = io::suffix(io::completeBasename(savePath));
502503
}
503504

504505
if (suffix.empty()) {
505506
// Then it must be a MSCX folder
506507
suffix = engraving::MSCX;
507508
}
508509

509-
return saveScore(path, suffix, false /*generateBackup*/, false /*createThumbnail*/, true /*isAutosave*/);
510+
ret = saveScore(savePath, suffix, false /*generateBackup*/, false /*createThumbnail*/, true /*isAutosave*/);
511+
} break;
512+
default:
513+
ret = muse::make_ret(Ret::Code::UnknownError);
514+
}
515+
516+
if (ret) {
517+
m_saved.send(savePath, saveMode);
510518
}
511519

512-
return make_ret(notation::Err::UnknownError);
520+
return ret;
521+
}
522+
523+
muse::async::Channel<path_t, SaveMode> NotationProject::saveComplited() const
524+
{
525+
return m_saved;
513526
}
514527

515528
Ret NotationProject::writeToDevice(QIODevice* device)

src/project/internal/notationproject.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ class NotationProject : public INotationProject, public muse::Injectable, public
9696

9797
muse::Ret save(
9898
const muse::io::path_t& path = muse::io::path_t(), SaveMode saveMode = SaveMode::Save, bool createBackup = true) override;
99+
muse::async::Channel<muse::io::path_t, SaveMode> saveComplited() const override;
100+
99101
muse::Ret writeToDevice(QIODevice* device) override;
100102

101103
ProjectMeta metaInfo() const override;
@@ -138,6 +140,8 @@ class NotationProject : public INotationProject, public muse::Injectable, public
138140

139141
muse::async::Notification m_needSaveNotification;
140142

143+
muse::async::Channel<muse::io::path_t, SaveMode> m_saved;
144+
141145
bool m_isNewlyCreated = false; /// true if the file has never been saved yet
142146
bool m_isImported = false;
143147
bool m_needAutoSave = false;

src/webbridge/distr/muapi.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,20 @@ const MuApi = {
1010
this.Module.HEAPU8.set(data, ptr);
1111
this.Module._load(ptr, data.length);
1212
this.Module._free(ptr);
13-
}
14-
13+
},
1514
}
1615

1716
let createMuApi = (function(config, onInited) {
1817

1918
MuApi.Module = config.muwasm
19+
20+
MuApi.Module.onProjectSaved = function(data) {
21+
console.log("[js muapi internal] onProjectSaved len: ", data.length)
22+
if (config.onProjectSaved) {
23+
config.onProjectSaved(data)
24+
}
25+
}
26+
2027
return MuApi
2128
})
2229

src/webbridge/internal/memfilesystem.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,5 @@ muse::DateTime MemFileSystem::lastModified(const muse::io::path_t& /*path*/) con
224224

225225
muse::Ret MemFileSystem::isWritable(const muse::io::path_t& /*path*/) const
226226
{
227-
NOT_IMPLEMENTED;
228-
return muse::Ret();
227+
return muse::make_ok();
229228
}

src/webbridge/viewer/viewer.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,12 @@
155155
function onInit() {
156156
console.log("on init mu api")
157157
window.muapi = createMuApi({
158-
muwasm: window.muwasm
158+
muwasm: window.muwasm,
159+
160+
// callbacks
161+
onProjectSaved: function(data) {
162+
console.log("[html] onProjectSaved len: ", data.length)
163+
}
159164
})
160165
}
161166

src/webbridge/webapi.cpp

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,70 @@
2121
*/
2222
#include "webapi.h"
2323

24+
#ifdef Q_OS_WASM
25+
#include <emscripten/bind.h>
26+
#include <emscripten/val.h>
27+
#endif
28+
2429
#include "global/io/file.h"
2530

2631
#include "log.h"
2732

2833
using namespace muse;
2934
using namespace mu::webbridge;
3035

36+
#ifdef Q_OS_WASM
37+
static void callJsWithBytes(const char* fnname, const uint8_t* data, size_t size)
38+
{
39+
emscripten::val jsArray = emscripten::val::global("Uint8Array").new_(
40+
emscripten::typed_memory_view(size, data)
41+
);
42+
43+
emscripten::val::module_property(fnname)(jsArray);
44+
}
45+
46+
#else
47+
static void callJsWithBytes(const char*, const uint8_t*, size_t)
48+
{
49+
NOT_SUPPORTED;
50+
}
51+
52+
#endif
53+
54+
WebApi* WebApi::instance()
55+
{
56+
static WebApi a;
57+
return &a;
58+
}
59+
60+
void WebApi::init()
61+
{
62+
auto onProjectChanged = [this]() {
63+
if (m_currentProject) {
64+
m_currentProject->saveComplited().resetOnReceive(this);
65+
}
66+
67+
m_currentProject = globalContext()->currentProject();
68+
69+
if (m_currentProject) {
70+
m_currentProject->saveComplited().onReceive(this, [this](const muse::io::path_t& path, project::SaveMode mode) {
71+
onProjectSaved(path, mode);
72+
});
73+
}
74+
};
75+
76+
globalContext()->currentProjectChanged().onNotify(this, onProjectChanged);
77+
78+
onProjectChanged();
79+
}
80+
81+
void WebApi::deinit()
82+
{
83+
if (m_currentProject) {
84+
m_currentProject->saveComplited().resetOnReceive(this);
85+
}
86+
}
87+
3188
void WebApi::onclickTest1(int num)
3289
{
3390
LOGI() << "num: " << num;
@@ -39,9 +96,29 @@ void WebApi::load(const void* source, unsigned int len)
3996
LOGI() << source << ", len: " << len;
4097
ByteArray data = ByteArray::fromRawData(reinterpret_cast<const char*>(source), len);
4198
io::path_t tempFilePath = "/mu/temp/current.mscz";
99+
100+
//! NOTE Remove last previous
101+
io::File::remove(tempFilePath);
102+
103+
//! NOTE Write new project
42104
io::File::writeFile(tempFilePath, data);
43105

44106
dispatcher()->dispatch("file-open", actions::ActionData::make_arg1(QUrl::fromLocalFile(tempFilePath.toQString())));
107+
}
45108

46-
io::File::remove(tempFilePath);
109+
void WebApi::onProjectSaved(const muse::io::path_t& path, mu::project::SaveMode)
110+
{
111+
IF_ASSERT_FAILED(io::File::exists(path)) {
112+
LOGE() << "file does not exist, path: " << path;
113+
return;
114+
}
115+
116+
ByteArray data;
117+
Ret ret = io::File::readFile(path, data);
118+
if (!ret) {
119+
LOGE() << "failed read file, path: " << path;
120+
return;
121+
}
122+
123+
callJsWithBytes("onProjectSaved", data.constData(), data.size());
47124
}

src/webbridge/webapi.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,36 @@
2121
*/
2222
#pragma once
2323

24+
#include "global/async/asyncable.h"
25+
2426
#include "global/modularity/ioc.h"
2527
#include "global/iinteractive.h"
2628
#include "actions/iactionsdispatcher.h"
29+
#include "context/iglobalcontext.h"
2730

2831
namespace mu::webbridge {
29-
class WebApi
32+
class WebApi : public muse::async::Asyncable
3033
{
3134
inline static muse::GlobalInject<muse::IInteractive> interactive;
3235
inline static muse::GlobalInject<muse::actions::IActionsDispatcher> dispatcher;
36+
inline static muse::GlobalInject<mu::context::IGlobalContext> globalContext;
3337

3438
public:
3539
WebApi() = default;
3640

37-
static void onclickTest1(int num);
38-
static void load(const void* source, unsigned int len);
41+
static WebApi* instance();
42+
43+
void init();
44+
void deinit();
45+
46+
void onclickTest1(int num);
47+
48+
void load(const void* source, unsigned int len);
49+
50+
private:
51+
52+
void onProjectSaved(const muse::io::path_t& path, mu::project::SaveMode mode);
53+
54+
project::INotationProjectPtr m_currentProject;
3955
};
4056
}

src/webbridge/webbridgemodule.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
#include "internal/webinteractive.h"
2929

30+
#include "webapi.h"
31+
3032
#include "log.h"
3133

3234
using namespace mu::webbridge;
@@ -52,6 +54,12 @@ void WebBridgeModule::registerExports()
5254
ioc()->registerExport<muse::IInteractive>(moduleName(), new WebInteractive(originInteractive));
5355
}
5456

55-
void WebBridgeModule::onStartApp()
57+
void WebBridgeModule::onInit(const muse::IApplication::RunMode&)
58+
{
59+
WebApi::instance()->init();
60+
}
61+
62+
void WebBridgeModule::onDeinit()
5663
{
64+
WebApi::instance()->deinit();
5765
}

src/webbridge/webbridgemodule.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class WebBridgeModule : public muse::modularity::IModuleSetup
3030

3131
std::string moduleName() const override;
3232
void registerExports() override;
33-
void onStartApp() override;
33+
void onInit(const muse::IApplication::RunMode& mode) override;
34+
void onDeinit() override;
3435
};
3536
}

0 commit comments

Comments
 (0)