Changeset View
Changeset View
Standalone View
Standalone View
src/matrix-sdk.cpp
| Show First 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | void post(Fn &&fn) | ||||
| ); | ); | ||||
| } | } | ||||
| void finish() {} | void finish() {} | ||||
| void pause() { throw std::runtime_error{"not implemented!"}; } | void pause() { throw std::runtime_error{"not implemented!"}; } | ||||
| void resume() { throw std::runtime_error{"not implemented!"}; } | void resume() { throw std::runtime_error{"not implemented!"}; } | ||||
| }; | }; | ||||
| QCoro::Task<std::pair<bool, QString>> setupAndImport(SdkModel model, DbStore *dbStore, std::string userDataDir) | |||||
| { | |||||
| auto res = co_await dbStore->setup( | |||||
| userDataDir, | |||||
| model.c().userId, | |||||
| model.c().deviceId | |||||
| ); | |||||
| if (!res.first) { | |||||
| co_return res; | |||||
| } | |||||
| co_return co_await dbStore->importAllFrom(model); | |||||
| } | |||||
| std::filesystem::path sessionDirForUserAndDeviceId(std::filesystem::path userDataDir, std::string userId, std::string deviceId) | std::filesystem::path sessionDirForUserAndDeviceId(std::filesystem::path userDataDir, std::string userId, std::string deviceId) | ||||
| { | { | ||||
| auto encodedUserId = encodeBase64(userId, Base64Opts::urlSafe); | auto encodedUserId = encodeBase64(userId, Base64Opts::urlSafe); | ||||
| auto sessionDir = userDataDir / "sessions" | auto sessionDir = userDataDir / "sessions" | ||||
| / encodedUserId / deviceId; | / encodedUserId / deviceId; | ||||
| return sessionDir; | return sessionDir; | ||||
| } | } | ||||
| struct PendingSaveEvents : public SaveEventsRequested | |||||
| { | |||||
| using DataT = immer::map<std::string, EventList>; | |||||
| void add(SaveEventsRequested s) | |||||
| { | |||||
| timelineEvents = addOneType(std::move(timelineEvents), s.timelineEvents); | |||||
| nonTimelineEvents = addOneType(std::move(nonTimelineEvents), s.nonTimelineEvents); | |||||
| } | |||||
| static DataT addOneType(DataT orig, DataT addon) | |||||
| { | |||||
| for (const auto &[roomId, el]: addon) { | |||||
| orig = std::move(orig).update(roomId, [&el](EventList origEl) { | |||||
| // This works without explicit dedupe because we do not do parallel | |||||
| // inserts and the insert order is the same as list order. | |||||
| return origEl + el; | |||||
| }); | |||||
| } | |||||
| return orig; | |||||
| } | |||||
| }; | |||||
| enum DbStatus | |||||
| { | |||||
| Pending, | |||||
| Ready, | |||||
| Failed, | |||||
| }; | |||||
| struct MatrixSdkPrivate | struct MatrixSdkPrivate | ||||
| { | { | ||||
| MatrixSdkPrivate(MatrixSdk *q, bool testing, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore); | MatrixSdkPrivate(MatrixSdk *q, bool testing, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore); | ||||
| MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore); | MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore); | ||||
| bool testing; | bool testing; | ||||
| std::string userDataDir; | std::string userDataDir; | ||||
| std::unique_ptr<KazvSessionLockGuard> lockGuard; | std::unique_ptr<KazvSessionLockGuard> lockGuard; | ||||
| RandomInterface randomGenerator; | RandomInterface randomGenerator; | ||||
| std::unique_ptr<DbStore> dbStore; | std::unique_ptr<DbStore> dbStore; | ||||
| QThread *thread; | QThread *thread; | ||||
| QObject *obj; | QObject *obj; | ||||
| QtJobHandler *jobHandler; | QtJobHandler *jobHandler; | ||||
| LagerStoreEventEmitter ee; | LagerStoreEventEmitter ee; | ||||
| LagerStoreEventEmitter::Watchable watchable; | LagerStoreEventEmitter::Watchable watchable; | ||||
| SdkT sdk; | SdkT sdk; | ||||
| QTimer saveTimer; | QTimer saveTimer; | ||||
| using SecondaryRootT = decltype(sdk.createSecondaryRoot(std::declval<lager::with_qt_event_loop>())); | using SecondaryRootT = decltype(sdk.createSecondaryRoot(std::declval<lager::with_qt_event_loop>())); | ||||
| SecondaryRootT secondaryRoot; | SecondaryRootT secondaryRoot; | ||||
| Client clientOnSecondaryRoot; | Client clientOnSecondaryRoot; | ||||
| NotificationHandler notificationHandler; | NotificationHandler notificationHandler; | ||||
| DbStatus dbStatus; | |||||
| PendingSaveEvents pendingSaveEvents{}; | |||||
| void runIoContext() { | void runIoContext() { | ||||
| thread->start(); | thread->start(); | ||||
| } | } | ||||
| void stopIoContext() { | void stopIoContext() { | ||||
| thread->quit(); | thread->quit(); | ||||
| } | } | ||||
| void maybeSerialize() | void maybeSerialize() | ||||
| { | { | ||||
| if (!testing) { | if (!testing) { | ||||
| serializeClientToFile(clientOnSecondaryRoot); | serializeClientToFile(clientOnSecondaryRoot); | ||||
| } | } | ||||
| } | } | ||||
| void serializeClientToFile(Client c); | void serializeClientToFile(Client c); | ||||
| void saveOrQueueEvents(SaveEventsRequested s) | |||||
| { | |||||
| if (dbStatus == Ready) { | |||||
| saveEvents(std::move(s)); | |||||
| } else if (dbStatus == Pending) { | |||||
| pendingSaveEvents.add(std::move(s)); | |||||
| } | |||||
| // Database open failed, do not do anything | |||||
| } | |||||
| void saveEvents(SaveEventsRequested s) | |||||
| { | |||||
| dbStore->saveEvents(std::move(s)) | |||||
| .then([](const auto &stat) { | |||||
| if (!stat.first) { | |||||
| qCWarning(kazvLog) << "Cannot save events:" << stat.second; | |||||
| } | |||||
| }); | |||||
| } | |||||
| }; | }; | ||||
| // Cleaning up notes: | // Cleaning up notes: | ||||
| // 0. Callback functions may store the context for an indefinite time | // 0. Callback functions may store the context for an indefinite time | ||||
| // 1. The QThread event loop can be stopped | // 1. The QThread event loop can be stopped | ||||
| // 2. QtJobHandler::submit() should only happen in the primary event loop thread | // 2. QtJobHandler::submit() should only happen in the primary event loop thread | ||||
| // 3. QtJobHandler lives in the primary event loop thread | // 3. QtJobHandler lives in the primary event loop thread | ||||
| // 4. QtJobs live in the primary event loop thread | // 4. QtJobs live in the primary event loop thread | ||||
| ▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | , sdk(makeDefaultSdkWithCryptoRandom( | ||||
| static_cast<JobInterface &>(*jobHandler), | static_cast<JobInterface &>(*jobHandler), | ||||
| static_cast<EventInterface &>(ee), | static_cast<EventInterface &>(ee), | ||||
| QtPromiseHandler(*obj), | QtPromiseHandler(*obj), | ||||
| zug::identity, | zug::identity, | ||||
| withRandomGenerator(randomGenerator))) | withRandomGenerator(randomGenerator))) | ||||
| , secondaryRoot(sdk.createSecondaryRoot(QtEventLoop{q})) | , secondaryRoot(sdk.createSecondaryRoot(QtEventLoop{q})) | ||||
| , clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot)) | , clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot)) | ||||
| , notificationHandler(clientOnSecondaryRoot.notificationHandler()) | , notificationHandler(clientOnSecondaryRoot.notificationHandler()) | ||||
| , dbStatus(this->dbStore ? Ready : Pending) | |||||
| { | { | ||||
| obj->moveToThread(thread); | obj->moveToThread(thread); | ||||
| } | } | ||||
| MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore) | MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model, std::unique_ptr<KazvSessionLockGuard> lockGuard, std::unique_ptr<DbStore> dbStore) | ||||
| : testing(testing) | : testing(testing) | ||||
| , userDataDir{kazvUserDataDir().toStdString()} | , userDataDir{kazvUserDataDir().toStdString()} | ||||
| , lockGuard(std::move(lockGuard)) | , lockGuard(std::move(lockGuard)) | ||||
| Show All 9 Lines | , sdk(makeSdk( | ||||
| static_cast<JobInterface &>(*jobHandler), | static_cast<JobInterface &>(*jobHandler), | ||||
| static_cast<EventInterface &>(ee), | static_cast<EventInterface &>(ee), | ||||
| QtPromiseHandler(*obj), | QtPromiseHandler(*obj), | ||||
| zug::identity, | zug::identity, | ||||
| withRandomGenerator(randomGenerator))) | withRandomGenerator(randomGenerator))) | ||||
| , secondaryRoot(sdk.createSecondaryRoot(QtEventLoop{q}, std::move(model))) | , secondaryRoot(sdk.createSecondaryRoot(QtEventLoop{q}, std::move(model))) | ||||
| , clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot)) | , clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot)) | ||||
| , notificationHandler(clientOnSecondaryRoot.notificationHandler()) | , notificationHandler(clientOnSecondaryRoot.notificationHandler()) | ||||
| , dbStatus(this->dbStore ? Ready : Pending) | |||||
| { | { | ||||
| obj->moveToThread(thread); | obj->moveToThread(thread); | ||||
| } | } | ||||
| MatrixSdk::MatrixSdk(std::unique_ptr<MatrixSdkPrivate> d, QObject *parent) | MatrixSdk::MatrixSdk(std::unique_ptr<MatrixSdkPrivate> d, QObject *parent) | ||||
| : QObject(parent) | : QObject(parent) | ||||
| , m_d(std::move(d)) | , m_d(std::move(d)) | ||||
| { | { | ||||
| init(); | init(); | ||||
| connect(this, &MatrixSdk::trigger, | connect(this, &MatrixSdk::trigger, | ||||
| this, [](KazvEvent e) { | this, [this](KazvEvent e) { | ||||
| qDebug() << "receiving trigger:"; | if (std::holds_alternative<SaveEventsRequested>(e)) { | ||||
| if (std::holds_alternative<LoginSuccessful>(e)) { | auto s = std::get<SaveEventsRequested>(e); | ||||
| qDebug() << "Login successful"; | m_d->saveOrQueueEvents(s); | ||||
| } | } | ||||
| }); | }); | ||||
| // loginSuccessful will be emitted only for new sessions (not for loaded | |||||
| // sessions), | |||||
| connect(this, &MatrixSdk::loginSuccessful, this, [this]() { | |||||
| if (m_d->dbStore) { | |||||
| qCWarning(kazvLog) << "Trying to replace current db store, ignoring"; | |||||
| return; | |||||
| } | } | ||||
| m_d->dbStore = std::make_unique<DbStore>(); | |||||
| // This must be called from the GUI thread | |||||
| auto model = m_d->secondaryRoot.get(); | |||||
| setupAndImport(std::move(model), m_d->dbStore.get(), m_d->userDataDir) | |||||
| .then([this](const auto &stat) { | |||||
| if (!stat.first) { | |||||
| qCWarning(kazvLog) << "Cannot set up database:" << stat.second; | |||||
| } | |||||
| Q_EMIT dbResolved(stat.first); | |||||
| }); | |||||
| }); | |||||
| connect(this, &MatrixSdk::dbResolved, this, [this](bool success) { | |||||
| if (!success) { | |||||
| m_d->dbStatus = Failed; | |||||
| } else { | |||||
| qCDebug(kazvLog) << "db loading succeeded"; | |||||
| auto pendingSaveEvents = std::move(m_d->pendingSaveEvents); | |||||
| m_d->pendingSaveEvents = {}; | |||||
| m_d->saveEvents(std::move(pendingSaveEvents)); | |||||
| m_d->dbStatus = Ready; | |||||
| } | |||||
| }); | |||||
| } | |||||
| void MatrixSdk::init() | void MatrixSdk::init() | ||||
| { | { | ||||
| LAGER_QT(serverUrl) = m_d->clientOnSecondaryRoot.serverUrl().xform(strToQt); Q_EMIT serverUrlChanged(serverUrl()); | LAGER_QT(serverUrl) = m_d->clientOnSecondaryRoot.serverUrl().xform(strToQt); Q_EMIT serverUrlChanged(serverUrl()); | ||||
| LAGER_QT(userId) = m_d->clientOnSecondaryRoot.userId().xform(strToQt); Q_EMIT userIdChanged(userId()); | LAGER_QT(userId) = m_d->clientOnSecondaryRoot.userId().xform(strToQt); Q_EMIT userIdChanged(userId()); | ||||
| LAGER_QT(token) = m_d->clientOnSecondaryRoot.token().xform(strToQt); Q_EMIT tokenChanged(token()); | LAGER_QT(token) = m_d->clientOnSecondaryRoot.token().xform(strToQt); Q_EMIT tokenChanged(token()); | ||||
| LAGER_QT(deviceId) = m_d->clientOnSecondaryRoot.deviceId().xform(strToQt); Q_EMIT deviceIdChanged(deviceId()); | LAGER_QT(deviceId) = m_d->clientOnSecondaryRoot.deviceId().xform(strToQt); Q_EMIT deviceIdChanged(deviceId()); | ||||
| LAGER_QT(specVersions) = m_d->clientOnSecondaryRoot.supportVersions(); Q_EMIT specVersionsChanged(specVersions()); | LAGER_QT(specVersions) = m_d->clientOnSecondaryRoot.supportVersions(); Q_EMIT specVersionsChanged(specVersions()); | ||||
| ▲ Show 20 Lines • Show All 373 Lines • ▼ Show 20 Lines | auto loadFromSession = [this, sessionName](StdPath sessionDir, std::unique_ptr<KazvSessionLockGuard> lockGuard) { | ||||
| qDebug() << "Finished loading session store file"; | qDebug() << "Finished loading session store file"; | ||||
| } catch (const std::exception &e) { | } catch (const std::exception &e) { | ||||
| qDebug() << "Error when loading session:" << QString::fromStdString(e.what()); | qDebug() << "Error when loading session:" << QString::fromStdString(e.what()); | ||||
| Q_EMIT loadSessionFinished(sessionName, SessionDeserializeFailed); | Q_EMIT loadSessionFinished(sessionName, SessionDeserializeFailed); | ||||
| return; | return; | ||||
| } | } | ||||
| auto dbStore = std::make_unique<DbStore>(); | auto dbStore = std::make_unique<DbStore>(); | ||||
| auto stat = QCoro::waitFor([](SdkModel model, DbStore *dbStore, std::string userDataDir) -> QCoro::Task<std::pair<bool, QString>> { | auto stat = QCoro::waitFor(setupAndImport(model, dbStore.get(), m_d->userDataDir)); | ||||
| qCDebug(kazvLog) << "open db:" << userDataDir << model.c().userId << model.c().deviceId; | |||||
| auto res = co_await dbStore->setup( | |||||
| userDataDir, | |||||
| model.c().userId, | |||||
| model.c().deviceId | |||||
| ); | |||||
| if (!res.first) { | |||||
| co_return res; | |||||
| } | |||||
| co_return co_await dbStore->importAllFrom(model); | |||||
| }(model, dbStore.get(), m_d->userDataDir)); | |||||
| if (!stat.first) { | if (!stat.first) { | ||||
| qCWarning(kazvLog) << "Unable to open db store:" << stat.second; | qCWarning(kazvLog) << "Unable to open db store:" << stat.second; | ||||
| Q_EMIT loadSessionFinished(sessionName, SessionDeserializeFailed); | Q_EMIT loadSessionFinished(sessionName, SessionDeserializeFailed); | ||||
| return; | return; | ||||
| } | } | ||||
| QMetaObject::invokeMethod(this, [this, model=std::move(model), lockGuard=std::move(lockGuard), dbStore=std::move(dbStore)]() mutable { | QMetaObject::invokeMethod(this, [this, model=std::move(model), lockGuard=std::move(lockGuard), dbStore=std::move(dbStore)]() mutable { | ||||
| emplace(std::move(model), std::move(lockGuard), std::move(dbStore)); | emplace(std::move(model), std::move(lockGuard), std::move(dbStore)); | ||||
| }); | }); | ||||
| ▲ Show 20 Lines • Show All 258 Lines • Show Last 20 Lines | |||||