File indexing completed on 2024-09-15 04:30:38
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 <QQuickStyle> 0005 #include <QtWebView> 0006 0007 #ifdef Q_OS_ANDROID 0008 #include "utils/androidutils.h" 0009 #include <QGuiApplication> 0010 #else 0011 #include <QApplication> 0012 #endif 0013 0014 #ifdef HAVE_KDBUSADDONS 0015 #include <KDBusService> 0016 #include <KWindowSystem> 0017 #endif 0018 #include <KLocalizedString> 0019 0020 #ifdef HAVE_KUNIFIEDPUSH 0021 #include <kunifiedpush/connector.h> 0022 #endif 0023 0024 #include "tokodon-version.h" 0025 0026 #include "account/accountmanager.h" 0027 #include "accountconfig.h" 0028 #include "admin/emailinfo.h" 0029 #include "admin/ipinfo.h" 0030 #include "config.h" 0031 #include "network/networkaccessmanagerfactory.h" 0032 #include "network/networkcontroller.h" 0033 #include "tokodon_debug.h" 0034 #include "utils/blurhashimageprovider.h" 0035 #include "utils/colorschemer.h" 0036 #include "utils/navigation.h" 0037 #include "utils/windowcontroller.h" 0038 0039 #ifdef Q_OS_WINDOWS 0040 #include <Windows.h> 0041 #endif 0042 0043 #ifdef TEST_MODE 0044 #include "autotests/helperreply.h" 0045 #include "autotests/mockaccount.h" 0046 #endif 0047 0048 using namespace Qt::Literals::StringLiterals; 0049 0050 void setupUnifiedPush() 0051 { 0052 #ifdef HAVE_KUNIFIEDPUSH 0053 auto connector = new KUnifiedPush::Connector(QStringLiteral("org.kde.tokodon")); 0054 QObject::connect(connector, &KUnifiedPush::Connector::endpointChanged, [=](const auto &endpoint) { 0055 NetworkController::instance().endpoint = endpoint; 0056 }); 0057 0058 NetworkController::instance().endpoint = connector->endpoint(); 0059 0060 connector->registerClient(i18n("Receiving push notifications")); 0061 #endif 0062 } 0063 0064 #ifdef Q_OS_ANDROID 0065 Q_DECL_EXPORT 0066 #endif 0067 int main(int argc, char *argv[]) 0068 { 0069 QNetworkProxyFactory::setUseSystemConfiguration(true); 0070 QtWebView::initialize(); 0071 0072 #ifdef Q_OS_ANDROID 0073 QGuiApplication app(argc, argv); 0074 app.connect(&app, &QGuiApplication::applicationStateChanged, [](Qt::ApplicationState state) { 0075 if (state == Qt::ApplicationActive) { 0076 AndroidUtils::instance().checkPendingIntents(); 0077 } 0078 }); 0079 QQuickStyle::setStyle(QStringLiteral("org.kde.breeze")); 0080 #else 0081 QApplication app(argc, argv); 0082 // Default to org.kde.desktop style unless the user forces another style 0083 if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) { 0084 QQuickStyle::setStyle(QStringLiteral("org.kde.desktop")); 0085 } 0086 #endif 0087 0088 #ifdef Q_OS_WINDOWS 0089 if (AttachConsole(ATTACH_PARENT_PROCESS)) { 0090 freopen("CONOUT$", "w", stdout); 0091 freopen("CONOUT$", "w", stderr); 0092 } 0093 0094 QApplication::setStyle(QStringLiteral("breeze")); 0095 auto font = app.font(); 0096 font.setPointSize(10); 0097 app.setFont(font); 0098 #endif 0099 KLocalizedString::setApplicationDomain(QByteArrayLiteral("tokodon")); 0100 0101 QCoreApplication::setOrganizationName(QStringLiteral("KDE")); 0102 0103 KAboutData about(QStringLiteral("tokodon"), 0104 i18n("Tokodon"), 0105 QStringLiteral(TOKODON_VERSION_STRING), 0106 i18n("Mastodon client"), 0107 KAboutLicense::GPL_V3, 0108 i18n("© 2021-2023 Carl Schwan, 2021-2023 KDE Community")); 0109 about.addAuthor(i18n("Carl Schwan"), 0110 i18n("Maintainer"), 0111 QStringLiteral("carl@carlschwan.eu"), 0112 QStringLiteral("https://carlschwan.eu"), 0113 QUrl(QStringLiteral("https://carlschwan.eu/avatar.png"))); 0114 about.addAuthor(i18n("Joshua Goins"), 0115 i18n("Maintainer"), 0116 QStringLiteral("josh@redstrate.com"), 0117 QStringLiteral("https://redstrate.com/"), 0118 QUrl(QStringLiteral("https://redstrate.com/rss-image.png"))); 0119 about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails")); 0120 about.setOrganizationDomain("kde.org"); 0121 about.setBugAddress("https://bugs.kde.org/describecomponents.cgi?product=tokodon"); 0122 0123 KAboutData::setApplicationData(about); 0124 QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.tokodon"))); 0125 0126 QCommandLineParser parser; 0127 parser.setApplicationDescription(i18n("Client for decentralized social networks like Mastodon")); 0128 parser.addPositionalArgument(QStringLiteral("urls"), i18n("Supports https and web+ap url scheme")); 0129 0130 QCommandLineOption shareOption(QStringLiteral("share"), i18n("Share a line of text in the standalone composer."), i18n("The text to share.")); 0131 shareOption.setFlags(QCommandLineOption::Flag::HiddenFromHelp); 0132 parser.addOption(shareOption); 0133 0134 QCommandLineOption notifyOption(QStringLiteral("dbus-activated"), i18n("Internal usage only.")); 0135 notifyOption.setFlags(QCommandLineOption::Flag::HiddenFromHelp); 0136 parser.addOption(notifyOption); 0137 0138 about.setupCommandLine(&parser); 0139 parser.process(app); 0140 about.processCommandLine(&parser); 0141 0142 auto &colorSchemer = ColorSchemer::instance(); 0143 auto config = Config::self(); 0144 if (!config->colorScheme().isEmpty()) { 0145 colorSchemer.apply(config->colorScheme()); 0146 } 0147 0148 qmlRegisterSingletonInstance("org.kde.tokodon.private", 1, 0, "Config", config); 0149 qmlRegisterUncreatableType<AccountConfig>("org.kde.tokodon.private", 1, 0, "AccountConfig", QStringLiteral("Use via Account.config")); 0150 qmlRegisterUncreatableMetaObject(Notification::staticMetaObject, "org.kde.tokodon", 1, 0, "Notification", QStringLiteral("Only for enums")); 0151 qmlRegisterUncreatableMetaObject(IpInfo::staticMetaObject, "org.kde.tokodon", 1, 0, "IpInfo", QStringLiteral("Only for enums")); 0152 qmlRegisterUncreatableMetaObject(EmailInfo::staticMetaObject, "org.kde.tokodon", 1, 0, "EmailInfo", QStringLiteral("Only for enums")); 0153 0154 QQmlApplicationEngine engine; 0155 0156 #ifdef HAVE_KUNIFIEDPUSH 0157 if (parser.isSet(notifyOption)) { 0158 qInfo(TOKODON_LOG) << "Beginning to check for notifications..."; 0159 0160 #ifdef HAVE_KDBUSADDONS 0161 // We want to be replaceable by the main client 0162 KDBusService service(KDBusService::Replace); 0163 #endif 0164 0165 setupUnifiedPush(); 0166 0167 // create the lazy instance 0168 AccountManager::instance().loadFromSettings(); 0169 0170 QObject::connect(&AccountManager::instance(), &AccountManager::accountsReady, [] { 0171 qInfo(TOKODON_LOG) << "Accounts have finished loading. Checking notification queue..."; 0172 // queue notification 0173 AccountManager::instance().queueNotifications(); 0174 }); 0175 0176 QObject::connect(&AccountManager::instance(), &AccountManager::finishedNotificationQueue, [] { 0177 // Sleep for a bit so the notifications stay open 0178 // TODO: make this depend on all of the knotifications closing 0179 QTimer *timer = new QTimer(); 0180 timer->setInterval(1000); 0181 timer->connect(timer, &QTimer::timeout, qApp, &QCoreApplication::quit); 0182 }); 0183 0184 return QCoreApplication::exec(); 0185 } 0186 #endif 0187 0188 #ifdef HAVE_KDBUSADDONS 0189 KDBusService service(KDBusService::Unique); 0190 #endif 0191 0192 setupUnifiedPush(); 0193 0194 #ifdef HAVE_KDBUSADDONS 0195 QObject::connect(&service, &KDBusService::activateRequested, &engine, [&engine](const QStringList &arguments, const QString & /*workingDirectory*/) { 0196 const auto rootObjects = engine.rootObjects(); 0197 for (auto obj : rootObjects) { 0198 if (auto view = qobject_cast<QQuickWindow *>(obj)) { 0199 KWindowSystem::updateStartupId(view); 0200 KWindowSystem::activateWindow(view); 0201 0202 if (arguments.isEmpty()) { 0203 return; 0204 } 0205 0206 auto args = arguments; 0207 args.removeFirst(); 0208 0209 if (arguments.length() >= 1) { 0210 if (args[0].startsWith("web+ap"_L1)) { 0211 NetworkController::instance().openWebApLink(args[0]); 0212 } else if (args[0].startsWith("tokodon"_L1)) { 0213 NetworkController::instance().setAuthCode(QUrl(args[0])); 0214 } else if (args[0] == "--share"_L1) { 0215 NetworkController::instance().startComposing(args[1]); 0216 } else { 0217 NetworkController::instance().openWebApLink(args[0]); 0218 } 0219 } 0220 0221 return; 0222 } 0223 } 0224 }); 0225 #endif 0226 engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); 0227 QObject::connect(&engine, &QQmlApplicationEngine::quit, &app, &QCoreApplication::quit); 0228 0229 NetworkAccessManagerFactory namFactory; 0230 engine.setNetworkAccessManagerFactory(&namFactory); 0231 0232 engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider); 0233 0234 #ifdef TEST_MODE 0235 AccountManager::instance().setTestMode(true); 0236 0237 auto account = new MockAccount(); 0238 AccountManager::instance().addAccount(account, true); 0239 AccountManager::instance().selectAccount(account); 0240 0241 QUrl url = account->apiUrl(QStringLiteral("/api/v2/search")); 0242 url.setQuery(QUrlQuery{{QStringLiteral("q"), QStringLiteral("myquery")}}); 0243 account->registerGet(url, new TestReply(QStringLiteral("search-result.json"), account)); 0244 0245 account->registerGet(account->apiUrl(QStringLiteral("/api/v1/timelines/home")), new TestReply(QStringLiteral("statuses.json"), account)); 0246 #else 0247 AccountManager::instance().migrateSettings(); 0248 AccountManager::instance().loadFromSettings(); 0249 #endif 0250 0251 if (parser.isSet(shareOption)) { 0252 engine.loadFromModule("org.kde.tokodon", "StandaloneComposer"); 0253 0254 NetworkController::instance().startComposing(parser.value(shareOption)); 0255 } else { 0256 engine.loadFromModule("org.kde.tokodon", "Main"); 0257 0258 if (parser.positionalArguments().length() > 0) { 0259 NetworkController::instance().openWebApLink(parser.positionalArguments()[0]); 0260 } 0261 } 0262 0263 if (engine.rootObjects().isEmpty()) { 0264 return -1; 0265 } 0266 #ifdef HAVE_KDBUSADDONS 0267 QQuickWindow *window = nullptr; 0268 0269 const auto rootObjects = engine.rootObjects(); 0270 for (auto obj : rootObjects) { 0271 auto view = qobject_cast<QQuickWindow *>(obj); 0272 if (view) { 0273 window = view; 0274 break; 0275 } 0276 } 0277 0278 if (window != nullptr) { 0279 auto controller = engine.singletonInstance<WindowController *>(QStringLiteral("org.kde.tokodon"), QStringLiteral("WindowController")); 0280 controller->setWindow(window); 0281 controller->restoreGeometry(); 0282 } 0283 #endif 0284 return QCoreApplication::exec(); 0285 }