File indexing completed on 2024-04-28 05:02:12

0001 // SPDX-FileCopyrightText: 2021 kaniini <https://git.pleroma.social/kaniini>
0002 // SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
0003 // SPDX-License-Identifier: GPL-3.0-only
0004 
0005 #pragma once
0006 
0007 #include "post.h"
0008 
0009 #include <KAboutData>
0010 
0011 #include <QAbstractListModel>
0012 #include <QJSEngine>
0013 
0014 class AbstractAccount;
0015 class QNetworkAccessManager;
0016 
0017 /// Handles managing accounts in Tokodon, and tracks state such as which one is currently selected.
0018 class AccountManager : public QAbstractListModel
0019 {
0020     Q_OBJECT
0021     QML_ELEMENT
0022     QML_SINGLETON
0023 
0024     Q_PROPERTY(bool isReady READ isReady NOTIFY accountsReady)
0025     Q_PROPERTY(bool hasAccounts READ hasAccounts NOTIFY accountsChanged)
0026     Q_PROPERTY(bool hasAnyAccounts READ hasAnyAccounts NOTIFY accountsChanged)
0027     Q_PROPERTY(AbstractAccount *selectedAccount READ selectedAccount WRITE selectAccount NOTIFY accountSelected)
0028     Q_PROPERTY(QString selectedAccountId READ selectedAccountId NOTIFY accountSelected)
0029     Q_PROPERTY(int selectedIndex READ selectedIndex NOTIFY accountSelected)
0030     Q_PROPERTY(KAboutData aboutData READ aboutData WRITE setAboutData NOTIFY aboutDataChanged)
0031     Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT)
0032     Q_PROPERTY(bool selectedAccountHasIssue READ selectedAccountHasIssue NOTIFY accountSelected)
0033     Q_PROPERTY(bool testMode READ testMode CONSTANT)
0034 
0035 public:
0036     static AccountManager *create(QQmlEngine *, QJSEngine *)
0037     {
0038         auto inst = &instance();
0039         QJSEngine::setObjectOwnership(inst, QJSEngine::ObjectOwnership::CppOwnership);
0040         return inst;
0041     }
0042 
0043     /// Custom roles for the AccountManager model
0044     enum CustomRoles {
0045         AccountRole = Qt::UserRole + 1, ///< Account object
0046         DisplayNameRole, ///< Display name of the account. Uses the display name if set, otherwise falls back to the username
0047         DescriptionRole, ///< Username of the account
0048         InstanceRole, ///< Instance name of the account
0049     };
0050 
0051     static AccountManager &instance();
0052 
0053     /// Load accounts from disk
0054     void loadFromSettings();
0055 
0056     /// Migrates old Tokodon settings to newer formats
0057     void migrateSettings();
0058 
0059     /// Enables or disables test mode. Used internally for tokodon-offline
0060     /// \param enabled Whether test mode should be enabled
0061     void setTestMode(bool enabled);
0062 
0063     /// Returns if testing mode is enabled
0064     bool testMode() const;
0065 
0066     /// Whether or not the account manager is completely ready
0067     /// This doesn't mean it has accounts, simply that it's done reading configs and the keychain
0068     bool isReady() const;
0069 
0070     /// If there any valid accounts loaded
0071     bool hasAccounts() const;
0072 
0073     /// If there are any accounts in the config
0074     bool hasAnyAccounts() const;
0075 
0076     /// Adds a new account
0077     /// \param account The account to manage
0078     /// \param skipAuthenticationCheck Whether the account manager should internally check if the account is valid
0079     Q_INVOKABLE void addAccount(AbstractAccount *account, bool skipAuthenticationCheck);
0080 
0081     /// Removes an existing account
0082     /// \param account The account to remove
0083     Q_INVOKABLE void removeAccount(AbstractAccount *account);
0084 
0085     /// Re-validates every account's credentials
0086     void reloadAccounts();
0087     void queueNotifications();
0088 
0089     /// Returns if the currently selected account has issues with authentication
0090     Q_INVOKABLE bool selectedAccountHasIssue() const;
0091 
0092     /// If the selected account has a login issue, returns a localized string explaining why
0093     Q_INVOKABLE QString selectedAccountLoginIssue() const;
0094 
0095     /// Switches to an existing account
0096     /// \param explicitUserAction If true, considers this an explicit user action and the new selected account will be written to disk
0097     void selectAccount(AbstractAccount *account, bool explicitUserAction = true);
0098 
0099     /// The currently selected account
0100     AbstractAccount *selectedAccount() const;
0101 
0102     /// The currently selected account's id
0103     QString selectedAccountId() const;
0104 
0105     /// The index of the selected account in the account list
0106     int selectedIndex() const;
0107 
0108     /// Sets the application about data
0109     /// \param aboutData The new about data
0110     void setAboutData(const KAboutData &aboutData);
0111 
0112     /// Returns the application's about data
0113     [[nodiscard]] KAboutData aboutData() const;
0114 
0115     int rowCount(const QModelIndex &index = QModelIndex()) const override;
0116 
0117     QVariant data(const QModelIndex &index, int role) const override;
0118 
0119     QHash<int, QByteArray> roleNames() const override;
0120 
0121     /// Creates a new account, and adds it to the manager
0122     /// \param instanceUri The URI of the instance
0123     /// \param ignoreSslErrors Whether or ignore SSL errors from this URI
0124     /// \param admin Request admin scopes
0125     Q_INVOKABLE AbstractAccount *createNewAccount(const QString &instanceUri, bool ignoreSslErrors = false, bool admin = true);
0126 
0127     /// Returns whether or not Tokodon is built as a Flatpak
0128     bool isFlatpak() const;
0129 
0130     /// Returns the preferred settings group name for an account name and an instance uri.
0131     /// It's preferred to use AbstractAccount::settingsGroupName as it fills in the relevant information.
0132     static QString settingsGroupName(const QString &name, const QString &instanceUri);
0133 
0134     /// Returns the preferred key name for the client secret given a settings group name.
0135     /// It's preferred to use AbstractAccount::clientSecretKey as it fills in the relevant information.
0136     /// \param name The settings group name, from AbstractAccount::settingsGroupName()
0137     static QString clientSecretKey(const QString &name);
0138 
0139     /// Returns the preferred key name for the access token.
0140     /// It's preferred to use AbstractAccount::accessTokenKey as it fills in the relevant information.
0141     /// \param name The settings group name, from AbstractAccount::settingsGroupName()
0142     static QString accessTokenKey(const QString &name);
0143 
0144 Q_SIGNALS:
0145 
0146     void accountAdded(AbstractAccount *account);
0147 
0148     void accountRemoved(AbstractAccount *account);
0149 
0150     void accountsChanged();
0151 
0152     void accountsReady();
0153 
0154     void accountsReloaded();
0155 
0156     void accountSelected(AbstractAccount *account);
0157 
0158     void identityChanged(AbstractAccount *account);
0159 
0160     void fetchedTimeline(AbstractAccount *account, QString original_name, QList<Post *> posts);
0161 
0162     void invalidated(AbstractAccount *account);
0163 
0164     void fetchedInstanceMetadata(AbstractAccount *account);
0165 
0166     void invalidatedPost(AbstractAccount *account, Post *post);
0167 
0168     void notification(AbstractAccount *account, std::shared_ptr<Notification> n);
0169 
0170     void aboutDataChanged();
0171 
0172     void webapLink(QString id);
0173 
0174     void finishedNotificationQueue();
0175 
0176 public Q_SLOTS:
0177 
0178     void childIdentityChanged(AbstractAccount *account);
0179 
0180 private:
0181     explicit AccountManager(QObject *parent = nullptr);
0182 
0183     ~AccountManager() override;
0184 
0185     QList<AbstractAccount *> m_accounts;
0186     AbstractAccount *m_selected_account = nullptr;
0187     KAboutData m_aboutData;
0188     QNetworkAccessManager *m_qnam;
0189 
0190     enum class AccountStatus { NotLoaded, Loaded, InvalidCredentials };
0191 
0192     QList<AccountStatus> m_accountStatus;
0193     QList<QString> m_accountStatusStrings;
0194 
0195     bool m_ready = false;
0196     bool m_hasAnyAccounts = false;
0197     bool m_testMode = false;
0198 
0199     void checkIfLoadingFinished();
0200 };