File indexing completed on 2023-10-03 11:33:25

0001 // SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
0002 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003 
0004 #include <QCommandLineParser>
0005 #include <QFontDatabase>
0006 #include <QNetworkProxy>
0007 #include <QNetworkProxyFactory>
0008 #include <QQmlApplicationEngine>
0009 #include <QQmlContext>
0010 #include <QQuickStyle>
0011 #include <QQuickWindow>
0012 #include <QtWebView>
0013 #include <clocale>
0014 
0015 #ifdef Q_OS_ANDROID
0016 #include "utils/androidutils.h"
0017 #include <QGuiApplication>
0018 #else
0019 #include <QApplication>
0020 #endif
0021 
0022 #include <KAboutData>
0023 #ifdef HAVE_KDBUSADDONS
0024 #include <KDBusService>
0025 #include <KWindowSystem>
0026 #endif
0027 #include <KConfig>
0028 #include <KLocalizedContext>
0029 #include <KLocalizedString>
0030 #include <KWindowConfig>
0031 
0032 #include "tokodon-version.h"
0033 
0034 #include "account/accountmanager.h"
0035 #include "accountconfig.h"
0036 #include "config.h"
0037 #include "network/networkaccessmanagerfactory.h"
0038 #include "network/networkcontroller.h"
0039 #include "utils/blurhashimageprovider.h"
0040 #include "utils/colorschemer.h"
0041 #include "utils/navigation.h"
0042 
0043 #ifdef Q_OS_WINDOWS
0044 #include <Windows.h>
0045 #endif
0046 
0047 #ifdef TEST_MODE
0048 #include "autotests/helperreply.h"
0049 #include "autotests/mockaccount.h"
0050 #endif
0051 
0052 using namespace Qt::Literals::StringLiterals;
0053 
0054 #ifdef Q_OS_ANDROID
0055 Q_DECL_EXPORT
0056 #endif
0057 int main(int argc, char *argv[])
0058 {
0059     QNetworkProxyFactory::setUseSystemConfiguration(true);
0060     QtWebView::initialize();
0061 
0062 #ifdef Q_OS_ANDROID
0063     QGuiApplication app(argc, argv);
0064     app.connect(&app, &QGuiApplication::applicationStateChanged, [](Qt::ApplicationState state) {
0065         if (state == Qt::ApplicationActive) {
0066             AndroidUtils::instance().checkPendingIntents();
0067         }
0068     });
0069     QQuickStyle::setStyle(QStringLiteral("org.kde.breeze"));
0070 #else
0071     QApplication app(argc, argv);
0072     // Default to org.kde.desktop style unless the user forces another style
0073     if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) {
0074         QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
0075     }
0076 #endif
0077 
0078 #ifdef Q_OS_WINDOWS
0079     if (AttachConsole(ATTACH_PARENT_PROCESS)) {
0080         freopen("CONOUT$", "w", stdout);
0081         freopen("CONOUT$", "w", stderr);
0082     }
0083 
0084     QApplication::setStyle(QStringLiteral("breeze"));
0085     auto font = app.font();
0086     font.setPointSize(10);
0087     app.setFont(font);
0088 #endif
0089     KLocalizedString::setApplicationDomain("tokodon");
0090 
0091     QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
0092 
0093     KAboutData about(QStringLiteral("tokodon"),
0094                      i18n("Tokodon"),
0095                      QStringLiteral(TOKODON_VERSION_STRING),
0096                      i18n("Mastodon client"),
0097                      KAboutLicense::GPL_V3,
0098                      i18n("© 2021-2023 Carl Schwan, 2021-2023 KDE Community"));
0099     about.addAuthor(i18n("Carl Schwan"),
0100                     i18n("Maintainer"),
0101                     QStringLiteral("carl@carlschwan.eu"),
0102                     QStringLiteral("https://carlschwan.eu"),
0103                     QStringLiteral("https://carlschwan.eu/avatar.png"));
0104     about.addAuthor(i18n("Joshua Goins"),
0105                     i18n("Maintainer"),
0106                     QStringLiteral("josh@redstrate.com"),
0107                     QStringLiteral("https://redstrate.com/"),
0108                     QStringLiteral("https://redstrate.com/rss-image.png"));
0109     about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
0110     about.setOrganizationDomain("kde.org");
0111     about.setBugAddress("https://bugs.kde.org/describecomponents.cgi?product=tokodon");
0112 
0113     KAboutData::setApplicationData(about);
0114     QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.tokodon")));
0115 
0116     QCommandLineParser parser;
0117     parser.setApplicationDescription(i18n("Client for the decentralized social network: mastodon"));
0118     parser.addPositionalArgument(QStringLiteral("urls"), i18n("Supports web+ap: url scheme"));
0119 
0120     QCommandLineOption shareOption(QStringLiteral("share"), i18n("Share a line of text in the standalone composer."), i18n("The text to share."));
0121     parser.addOption(shareOption);
0122 
0123     about.setupCommandLine(&parser);
0124     parser.process(app);
0125     about.processCommandLine(&parser);
0126 
0127     // Qt sets the locale in the QGuiApplication constructor, but libmpv
0128     // requires the LC_NUMERIC category to be set to "C", so change it back.
0129     setlocale(LC_NUMERIC, "C");
0130 
0131     auto &colorSchemer = ColorSchemer::instance();
0132     auto config = Config::self();
0133     if (!config->colorScheme().isEmpty()) {
0134         colorSchemer.apply(config->colorScheme());
0135     }
0136 
0137     qmlRegisterSingletonInstance("org.kde.tokodon.private", 1, 0, "Config", config);
0138     qmlRegisterUncreatableType<AccountConfig>("org.kde.tokodon.private", 1, 0, "AccountConfig", QStringLiteral("Use via Account.config"));
0139 
0140     QQmlApplicationEngine engine;
0141 #ifdef HAVE_KDBUSADDONS
0142     KDBusService service(KDBusService::Unique);
0143     QObject::connect(&service, &KDBusService::activateRequested, &engine, [&engine](const QStringList &arguments, const QString & /*workingDirectory*/) {
0144         const auto rootObjects = engine.rootObjects();
0145         for (auto obj : rootObjects) {
0146             if (auto view = qobject_cast<QQuickWindow *>(obj)) {
0147                 KWindowSystem::updateStartupId(view);
0148                 KWindowSystem::activateWindow(view);
0149 
0150                 if (arguments.isEmpty()) {
0151                     return;
0152                 }
0153 
0154                 auto args = arguments;
0155                 args.removeFirst();
0156 
0157                 if (arguments.length() >= 1) {
0158                     if (args[0].startsWith("web+ap"_L1)) {
0159                         NetworkController::instance().openWebApLink(args[0]);
0160                     } else {
0161                         NetworkController::instance().setAuthCode(QUrl(args[0]));
0162                     }
0163                     if (args[0] == "--share"_L1) {
0164                         NetworkController::instance().startComposing(args[1]);
0165                     } else {
0166                         NetworkController::instance().openWebApLink(args[0]);
0167                     }
0168                 }
0169 
0170                 return;
0171             }
0172         }
0173     });
0174 #endif
0175     engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
0176     QObject::connect(&engine, &QQmlApplicationEngine::quit, &app, &QCoreApplication::quit);
0177 
0178     NetworkAccessManagerFactory namFactory;
0179     engine.setNetworkAccessManagerFactory(&namFactory);
0180 
0181     engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);
0182 
0183 #ifdef TEST_MODE
0184     AccountManager::instance().setTestMode(true);
0185 
0186     auto account = new MockAccount();
0187     AccountManager::instance().addAccount(account, true);
0188     AccountManager::instance().selectAccount(account);
0189 
0190     QUrl url = account->apiUrl(QStringLiteral("/api/v2/search"));
0191     url.setQuery(QUrlQuery{{QStringLiteral("q"), QStringLiteral("myquery")}});
0192     account->registerGet(url, new TestReply(QStringLiteral("search-result.json"), account));
0193 
0194     account->registerGet(account->apiUrl(QStringLiteral("/api/v1/timelines/home")), new TestReply(QStringLiteral("statuses.json"), account));
0195 #else
0196     AccountManager::instance().migrateSettings();
0197     AccountManager::instance().loadFromSettings();
0198 #endif
0199 
0200     if (parser.isSet(shareOption)) {
0201         engine.loadFromModule("org.kde.tokodon", "StandaloneComposer");
0202 
0203         NetworkController::instance().startComposing(parser.value(shareOption));
0204     } else {
0205         engine.loadFromModule("org.kde.tokodon", "Main");
0206 
0207         if (parser.positionalArguments().length() > 0) {
0208             NetworkController::instance().openWebApLink(parser.positionalArguments()[0]);
0209         }
0210     }
0211 
0212     if (engine.rootObjects().isEmpty()) {
0213         return -1;
0214     }
0215 #ifdef HAVE_KDBUSADDONS
0216     const auto rootObjects = engine.rootObjects();
0217     for (auto obj : rootObjects) {
0218         auto view = qobject_cast<QQuickWindow *>(obj);
0219         if (view) {
0220             KConfig dataResource(QStringLiteral("data"), KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
0221             KConfigGroup windowGroup(&dataResource, "Window");
0222             KWindowConfig::restoreWindowSize(view, windowGroup);
0223             KWindowConfig::restoreWindowPosition(view, windowGroup);
0224             break;
0225         }
0226     }
0227 #endif
0228     return QCoreApplication::exec();
0229 }