Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "cloudsmodel.h"
24 :
25 : #include "translation.h"
26 : #include "log.h"
27 :
28 : using namespace muse::cloud;
29 :
30 : static constexpr int INVALID_INDEX = -1;
31 :
32 : namespace prv {
33 : const QString CLOUD_CODE("cloudCode");
34 : const QString CLOUD_TITLE("cloudTitle");
35 : const QString USER_IS_AUTHORIZED("userIsAuthorized");
36 : const QString USER_NAME("userName");
37 : const QString USER_PROFILE_URL("userProfileUrl");
38 : const QString USER_AVATAR_URL("userAvatarUrl");
39 : const QString USER_COLLECTION_URL("userCollectionUrl");
40 : const QString CLOUD_LOGO_URL("cloudLogoUrl");
41 : const QString CLOUD_LOGO_COLOR("cloudLogoColor");
42 :
43 : const QString DIALOG_TITLE_TEXT("titleText");
44 : const QString REPLACE_BUTTON_TEXT("replaceButtonText");
45 : const QString NEW_BUTTON_TEXT("newButtonText");
46 : const QString SAVE_BUTTON_TEXT("saveButtonText");
47 : }
48 :
49 0 : static QVariantMap makeCloudInfoMap(const QString& title, const AccountInfo& accountInfo)
50 : {
51 0 : QVariantMap cloudInfoMap;
52 :
53 0 : cloudInfoMap[prv::CLOUD_TITLE] = title;
54 0 : cloudInfoMap[prv::USER_NAME] = accountInfo.userName;
55 0 : cloudInfoMap[prv::USER_PROFILE_URL] = accountInfo.profileUrl;
56 0 : cloudInfoMap[prv::USER_AVATAR_URL] = accountInfo.avatarUrl;
57 0 : cloudInfoMap[prv::USER_COLLECTION_URL] = accountInfo.collectionUrl;
58 :
59 0 : return cloudInfoMap;
60 0 : }
61 :
62 0 : CloudsModel::CloudsModel(QObject* parent)
63 0 : : QAbstractListModel(parent), Contextable(muse::iocCtxForQmlObject(this))
64 : {
65 0 : }
66 :
67 0 : QVariant CloudsModel::data(const QModelIndex& index, int role) const
68 : {
69 0 : if (!index.isValid()) {
70 0 : return QVariant();
71 : }
72 :
73 0 : IAuthorizationServicePtr cloud = m_clouds[index.row()];
74 :
75 0 : const CloudInfo& cloudInfo = cloud->cloudInfo();
76 0 : const AccountInfo& accountInfo = cloud->accountInfo();
77 0 : const bool isAuthorized = cloud->userAuthorized().val;
78 :
79 0 : switch (role) {
80 0 : case rCloudCode:
81 0 : return cloudInfo.code;
82 0 : case rCloudTitle:
83 0 : return cloudInfo.title;
84 0 : case rUserIsAuthorized:
85 0 : return isAuthorized;
86 0 : case rUserName:
87 0 : return accountInfo.userName;
88 0 : case rUserProfileUrl:
89 0 : return accountInfo.profileUrl;
90 0 : case rUserAvatarUrl:
91 0 : return accountInfo.avatarUrl;
92 0 : case rUserCollectionUrl:
93 0 : return accountInfo.profileUrl.host() + accountInfo.profileUrl.path();
94 : }
95 :
96 0 : return QVariant();
97 0 : }
98 :
99 0 : int CloudsModel::rowCount(const QModelIndex&) const
100 : {
101 0 : return static_cast<int>(m_clouds.size());
102 : }
103 :
104 0 : QHash<int, QByteArray> CloudsModel::roleNames() const
105 : {
106 : static const QHash<int, QByteArray> roles = {
107 0 : { rCloudCode, prv::CLOUD_CODE.toUtf8() },
108 0 : { rCloudTitle, prv::CLOUD_TITLE.toUtf8() },
109 0 : { rUserIsAuthorized, prv::USER_IS_AUTHORIZED.toUtf8() },
110 0 : { rUserName, prv::USER_NAME.toUtf8() },
111 0 : { rUserProfileUrl, prv::USER_PROFILE_URL.toUtf8() },
112 0 : { rUserAvatarUrl, prv::USER_AVATAR_URL.toUtf8() },
113 0 : { rUserCollectionUrl, prv::USER_COLLECTION_URL.toUtf8() }
114 0 : };
115 :
116 0 : return roles;
117 0 : }
118 :
119 0 : bool CloudsModel::userAuthorized() const
120 : {
121 0 : int index = indexOfFirstAuthorizedCloud();
122 0 : return index != INVALID_INDEX;
123 : }
124 :
125 0 : QVariant CloudsModel::firstAuthorizedCloudInfo() const
126 : {
127 0 : int index = indexOfFirstAuthorizedCloud();
128 0 : if (index == INVALID_INDEX) {
129 0 : return QVariant();
130 : }
131 :
132 0 : QString cloudTitle = m_clouds[index]->cloudInfo().title;
133 0 : const AccountInfo& accountInfo = m_clouds[index]->accountInfo();
134 :
135 0 : return makeCloudInfoMap(cloudTitle, accountInfo);
136 0 : }
137 :
138 0 : QVariant CloudsModel::cloudInfo(const QString& cloudCode) const
139 : {
140 0 : int index = indexByCode(cloudCode);
141 0 : if (index == INVALID_INDEX) {
142 0 : return QVariant();
143 : }
144 :
145 0 : QString cloudTitle = m_clouds[index]->cloudInfo().title;
146 0 : const AccountInfo& accountInfo = m_clouds[index]->accountInfo();
147 :
148 0 : QVariantMap cloudInfoMap = makeCloudInfoMap(cloudTitle, accountInfo);
149 :
150 0 : cloudInfoMap[prv::CLOUD_LOGO_URL] = m_clouds[index]->cloudInfo().logoUrl;
151 0 : cloudInfoMap[prv::CLOUD_LOGO_COLOR] = m_clouds[index]->cloudInfo().logoColor;
152 :
153 0 : return cloudInfoMap;
154 0 : }
155 :
156 0 : QVariantList CloudsModel::visibilityModel(const QString& cloudCode) const
157 : {
158 0 : QVariantList visibilityTypes;
159 :
160 0 : QVariantMap publicVisibility;
161 0 : publicVisibility.insert("value", int(Visibility::Public));
162 0 : publicVisibility.insert("text", muse::qtrc("project/save", "Public"));
163 0 : visibilityTypes.append(publicVisibility);
164 :
165 0 : QVariantMap unlistedVisibility;
166 0 : unlistedVisibility.insert("value", int(Visibility::Unlisted));
167 0 : unlistedVisibility.insert("text", muse::qtrc("project/save", "Unlisted"));
168 0 : visibilityTypes.append(unlistedVisibility);
169 :
170 0 : if (cloudCode == cloud::MUSESCORE_COM_CLOUD_CODE) {
171 0 : QVariantMap privateVisibility;
172 0 : privateVisibility.insert("value", int(Visibility::Private));
173 0 : privateVisibility.insert("text", muse::qtrc("project/save", "Private"));
174 0 : visibilityTypes.append(privateVisibility);
175 0 : }
176 :
177 0 : return visibilityTypes;
178 0 : }
179 :
180 0 : QVariant CloudsModel::dialogText(const QString& cloudCode, const QString& existingScoreOrAudioUrl) const
181 : {
182 0 : QVariantMap dialogTextMap;
183 :
184 0 : if (cloudCode == cloud::MUSESCORE_COM_CLOUD_CODE) {
185 0 : dialogTextMap[prv::DIALOG_TITLE_TEXT] = muse::qtrc("project/save", "Publish to MuseScore.com");
186 :
187 0 : if (!existingScoreOrAudioUrl.isEmpty()) {
188 : //: The text between `<a href=\"%1\">` and `</a>` will be a clickable link to the online score in question
189 0 : dialogTextMap[prv::REPLACE_BUTTON_TEXT] = muse::qtrc("project/save", "Replace the existing <a href=\"%1\">online score</a>")
190 0 : .arg(existingScoreOrAudioUrl);
191 :
192 0 : dialogTextMap[prv::NEW_BUTTON_TEXT] = muse::qtrc("project/save", "Publish as new online score");
193 : }
194 :
195 0 : dialogTextMap[prv::SAVE_BUTTON_TEXT] = muse::qtrc("project/save", "Publish");
196 :
197 0 : return dialogTextMap;
198 0 : } else if (cloudCode == cloud::AUDIO_COM_CLOUD_CODE) {
199 0 : dialogTextMap[prv::DIALOG_TITLE_TEXT] = muse::qtrc("project/save", "Share on Audio.com");
200 :
201 0 : if (!existingScoreOrAudioUrl.isEmpty()) {
202 : //: The text between `<a href=\"%1\">` and `</a>` will be a clickable link to the online audio in question
203 0 : dialogTextMap[prv::REPLACE_BUTTON_TEXT] = muse::qtrc("project/save", "Replace the <a href=\"%1\">existing audio</a>")
204 0 : .arg(existingScoreOrAudioUrl);
205 :
206 0 : dialogTextMap[prv::NEW_BUTTON_TEXT] = muse::qtrc("project/save", "Upload as new audio file");
207 : }
208 :
209 0 : dialogTextMap[prv::SAVE_BUTTON_TEXT] = muse::qtrc("project/save", "Share");
210 :
211 0 : return dialogTextMap;
212 : }
213 :
214 0 : LOGE() << "Unknown cloud code: " + cloudCode;
215 :
216 0 : return dialogTextMap;
217 0 : }
218 :
219 0 : void CloudsModel::load()
220 : {
221 0 : beginResetModel();
222 0 : m_clouds.clear();
223 :
224 0 : m_clouds = {
225 : #ifdef MUSE_MODULE_CLOUD_MUSESCORECOM
226 0 : museScoreComService()->authorization(),
227 : #endif
228 0 : audioComService()->authorization()
229 0 : };
230 :
231 0 : for (const IAuthorizationServicePtr& cloud : m_clouds) {
232 0 : QString cloudCode = cloud->cloudInfo().code;
233 : static const QVector<int> AUTHORIZATION_ROLES
234 0 : = { rUserIsAuthorized, rUserName, rUserProfileUrl, rUserAvatarUrl, rUserCollectionUrl };
235 :
236 0 : ValCh<bool> userAuthorizedCh = cloud->userAuthorized();
237 0 : userAuthorizedCh.ch.onReceive(this, [this, cloudCode](bool) {
238 0 : QModelIndex index = createIndex(indexByCode(cloudCode), 0);
239 0 : emit dataChanged(index, index, AUTHORIZATION_ROLES);
240 :
241 0 : emit userAuthorizedChanged();
242 0 : });
243 0 : }
244 :
245 0 : emit userAuthorizedChanged();
246 :
247 0 : endResetModel();
248 0 : }
249 :
250 0 : int CloudsModel::indexByCode(const QString& code) const
251 : {
252 0 : for (size_t i = 0; i < m_clouds.size(); ++i) {
253 0 : const IAuthorizationServicePtr cloud = m_clouds[i];
254 0 : if (cloud->cloudInfo().code == code) {
255 0 : return static_cast<int>(i);
256 : }
257 0 : }
258 :
259 0 : return INVALID_INDEX;
260 : }
261 :
262 0 : int CloudsModel::indexOfFirstAuthorizedCloud() const
263 : {
264 0 : for (size_t i = 0; i < m_clouds.size(); ++i) {
265 0 : const IAuthorizationServicePtr cloud = m_clouds[i];
266 0 : if (cloud->userAuthorized().val) {
267 0 : return static_cast<int>(i);
268 : }
269 0 : }
270 :
271 0 : return INVALID_INDEX;
272 : }
273 :
274 0 : void CloudsModel::createAccount(const QString& code)
275 : {
276 0 : int index = indexByCode(code);
277 0 : if (index == INVALID_INDEX) {
278 0 : return;
279 : }
280 :
281 0 : m_clouds[index]->signUp();
282 : }
283 :
284 0 : void CloudsModel::signIn(const QString& code)
285 : {
286 0 : int index = indexByCode(code);
287 0 : if (index == INVALID_INDEX) {
288 0 : return;
289 : }
290 :
291 0 : m_clouds[index]->signIn();
292 : }
293 :
294 0 : void CloudsModel::signOut(const QString& code)
295 : {
296 0 : int index = indexByCode(code);
297 0 : if (index == INVALID_INDEX) {
298 0 : return;
299 : }
300 :
301 0 : m_clouds[index]->signOut();
302 : }
|