LCOV - code coverage report
Current view: top level - muse/framework/cloud/qml/Muse/Cloud - cloudsmodel.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 159 0
Test Date: 2026-11-05 04:07:02

            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              : }
        

Generated by: LCOV version 2.0-1