Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85832927
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
31 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/client/actions/ephemeral.cpp b/src/client/actions/ephemeral.cpp
index 2f318c4..5e65a45 100644
--- a/src/client/actions/ephemeral.cpp
+++ b/src/client/actions/ephemeral.cpp
@@ -1,94 +1,94 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <status-utils.hpp>
#include "ephemeral.hpp"
namespace Kazv
{
ClientResult updateClient(ClientModel m, SetTypingAction a)
{
auto job = m.job<SetTypingJob>().make(
m.userId,
a.roomId,
a.typing,
a.timeoutMs)
.withData(json{{"roomId", a.roomId}});
m.addJob(std::move(job));
return { std::move(m), lager::noop };
}
ClientResult processResponse(ClientModel m, SetTypingResponse r)
{
auto roomId = r.dataStr("roomId");
if (! r.success()) {
m.addTrigger(SetTypingFailed{roomId, r.errorCode(), r.errorMessage()});
return { std::move(m), failWithResponse(r) };
}
m.addTrigger(SetTypingSuccessful{roomId});
return { std::move(m), lager::noop };
}
ClientResult updateClient(ClientModel m, PostReceiptAction a)
{
auto job = m.job<PostReceiptJob>().make(
a.roomId,
/* receiptType = */ "m.read"s,
a.eventId)
.withData(json{{"roomId", a.roomId}});
m.addJob(std::move(job));
return { std::move(m), lager::noop };
}
ClientResult processResponse(ClientModel m, PostReceiptResponse r)
{
auto roomId = r.dataStr("roomId");
if (! r.success()) {
m.addTrigger(PostReceiptFailed{roomId, r.errorCode(), r.errorMessage()});
- return { std::move(m), lager::noop };
+ return { std::move(m), failWithResponse(r) };
}
m.addTrigger(PostReceiptSuccessful{roomId});
return { std::move(m), lager::noop };
}
ClientResult updateClient(ClientModel m, SetReadMarkerAction a)
{
auto job = m.job<SetReadMarkerJob>().make(
a.roomId,
/* mFullyRead = */ a.eventId)
.withData(json{{"roomId", a.roomId}});
m.addJob(std::move(job));
return { std::move(m), lager::noop };
}
ClientResult processResponse(ClientModel m, SetReadMarkerResponse r)
{
auto roomId = r.dataStr("roomId");
if (! r.success()) {
m.addTrigger(SetReadMarkerFailed{roomId, r.errorCode(), r.errorMessage()});
- return { std::move(m), lager::noop };
+ return { std::move(m), failWithResponse(r) };
}
m.addTrigger(SetReadMarkerSuccessful{roomId});
return { std::move(m), lager::noop };
}
}
diff --git a/src/client/actions/paginate.cpp b/src/client/actions/paginate.cpp
index 2f2bae3..8cf9309 100644
--- a/src/client/actions/paginate.cpp
+++ b/src/client/actions/paginate.cpp
@@ -1,91 +1,97 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020 Tusooa Zhu
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
// eager requires tuplify but did not include it.
// This is a bug.
#include <zug/tuplify.hpp>
#include <zug/transducer/eager.hpp>
#include <zug/into.hpp>
#include <lager/util.hpp>
#include <types.hpp>
#include <debug.hpp>
#include <cursorutil.hpp>
#include <status-utils.hpp>
#include "paginate.hpp"
namespace Kazv
{
ClientResult updateClient(ClientModel m, PaginateTimelineAction a)
{
auto roomId = a.roomId;
auto room = m.roomList.at(roomId);
if (! room.timelineGaps.find(a.fromEventId)) {
kzo.client.dbg() << "There is no such gap at " << a.fromEventId << std::endl;
- return { m, simpleFail };
+ return {
+ std::move(m),
+ returnEffect(EffectStatus(/* succ = */ false, json{
+ {"errorCode", "MOE_KAZV_MXC_KAZV_NO_SUCH_GAP"},
+ {"error", "No such gap"},
+ }))
+ };
}
auto paginateBackToken = room.timelineGaps[a.fromEventId];
auto job = m.job<GetRoomEventsJob>().make(
roomId,
paginateBackToken, // from
"b"s, // dir
std::nullopt, // to
a.limit).withData(json{{"roomId", roomId},
{"gapEventId", a.fromEventId}});
m.addJob(std::move(job));
return { m, lager::noop };
}
ClientResult processResponse(ClientModel m, GetRoomEventsResponse r)
{
auto roomId = r.dataStr("roomId");
auto gapEventId = r.dataStr("gapEventId");
if (! r.success()) {
kzo.client.dbg() << "Get room events failed" << std::endl;
m.addTrigger(PaginateFailed{roomId});
return { m, failWithResponse(r) };
}
auto paginateBackToken = r.end();
auto chunk = r.chunk();
try {
// kzo.client.dbg() << "We got " << chunk.size() << " events here" << std::endl;
auto room = m.roomList.at(roomId);
immer::flex_vector_transient<Event> events{};
// The timeline from paginate backwards is returned
// in reversed order, so restore the order.
zug::into(events, zug::reversed, chunk);
AddToTimelineAction action
{events.persistent(), paginateBackToken, std::nullopt, gapEventId};
m.roomList = RoomListModel::update(
std::move(m.roomList),
UpdateRoomAction{roomId, action});
m.addTrigger(PaginateSuccessful{roomId});
return { std::move(m), lager::noop };
} catch (const std::out_of_range &e) {
// Ignore it, the client model is modified
// such that it knows nothing about this room.
// May happen in debugger.
return { std::move(m), simpleFail };
}
}
}
diff --git a/src/client/actions/send.cpp b/src/client/actions/send.cpp
index 9a3e364..12420c5 100644
--- a/src/client/actions/send.cpp
+++ b/src/client/actions/send.cpp
@@ -1,130 +1,142 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <debug.hpp>
#include <types.hpp>
#include "send.hpp"
#include "status-utils.hpp"
namespace Kazv
{
static std::string increaseTxnId(std::string cur)
{
return std::to_string(std::stoull(cur) + 1);
}
ClientResult updateClient(ClientModel m, SendMessageAction a)
{
auto event = std::move(a.event);
auto roomId = a.roomId;
auto origJson = event.originalJson().get();
if (!origJson.contains("type") || !origJson.contains("content")) {
m.addTrigger(InvalidMessageFormat{});
- return { std::move(m), lager::noop };
+ return {
+ std::move(m),
+ returnEffect(EffectStatus(/* succ = */ false, json{
+ {"errorCode", "MOE_KAZV_MXC_KAZV_INVALID_MESSAGE_FORMAT"},
+ {"error", "Invalid message format"},
+ }))
+ };
}
// We do not use event.type() etc. because we want
// encrypted events stay encrypted.
auto type = origJson["type"];
auto content = origJson["content"];
kzo.client.dbg() << "Sending message of type " << type
<< " with content " << content.dump()
<< " to " << a.roomId
<< " as #" << m.nextTxnId << std::endl;
// We combine the hash of json, the timestamp,
// and a numeric count in the client to avoid collision.
auto txnId = std::to_string(std::hash<json>{}(origJson))
+ std::to_string(std::chrono::system_clock::now()
.time_since_epoch().count())
+ m.nextTxnId;
m.nextTxnId = increaseTxnId(m.nextTxnId);
auto job = m.job<SendMessageJob>()
.make(a.roomId, type, txnId, content)
.withData(json{{"roomId", a.roomId}});
m.addJob(std::move(job));
return { std::move(m), lager::noop };
}
ClientResult processResponse(ClientModel m, SendMessageResponse r)
{
auto roomId = r.dataStr("roomId");
if (! r.success()) {
kzo.client.dbg() << "Send message failed" << std::endl;
m.addTrigger(SendMessageFailed{roomId, r.errorCode(), r.errorMessage()});
return { std::move(m), failWithResponse(r) };
}
m.addTrigger(SendMessageSuccessful{roomId, r.eventId()});
return { std::move(m), lager::noop };
}
ClientResult updateClient(ClientModel m, SendToDeviceMessageAction a)
{
auto origJson = a.event.originalJson().get();
if (!origJson.contains("type") || !origJson.contains("content")) {
- return { std::move(m), simpleFail };
+ return {
+ std::move(m),
+ returnEffect(EffectStatus(/* succ = */ false, json{
+ {"errorCode", "MOE_KAZV_MXC_KAZV_INVALID_MESSAGE_FORMAT"},
+ {"error", "Invalid message format"},
+ }))
+ };
}
// We do not use event.type() etc. because we want
// encrypted events stay encrypted.
auto type = origJson["type"];
auto content = origJson["content"];
auto txnId = std::to_string(std::hash<json>{}(origJson))
+ std::to_string(std::chrono::system_clock::now()
.time_since_epoch().count())
+ m.nextTxnId;
m.nextTxnId = increaseTxnId(m.nextTxnId);
auto messages =
immer::map<std::string, immer::map<std::string, JsonWrap>>{};
for (auto [userId, devices] : a.devicesToSend) {
auto deviceIdToContentMap = immer::map<std::string, JsonWrap>{};
for (auto deviceId : devices) {
deviceIdToContentMap = std::move(deviceIdToContentMap).set(deviceId, content);
}
messages = std::move(messages).set(userId, deviceIdToContentMap);
}
auto job = m.job<SendToDeviceJob>()
.make(type, txnId, messages)
.withData(json{{"devicesToSend", a.devicesToSend},
{"txnId", txnId}});
m.addJob(std::move(job));
return { std::move(m), lager::noop };
}
ClientResult processResponse(ClientModel m, SendToDeviceResponse r)
{
auto devicesToSend = r.dataJson("devicesToSend");
auto txnId = r.dataStr("txnId");
if (! r.success()) {
m.addTrigger(SendToDeviceMessageFailed{devicesToSend, txnId, r.errorCode(), r.errorMessage()});
return { std::move(m), failWithResponse(r) };
}
m.addTrigger(SendToDeviceMessageSuccessful{devicesToSend, txnId});
return { std::move(m), lager::noop };
}
}
diff --git a/src/client/actions/states.cpp b/src/client/actions/states.cpp
index 03fd014..8bc471a 100644
--- a/src/client/actions/states.cpp
+++ b/src/client/actions/states.cpp
@@ -1,165 +1,171 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020 Tusooa Zhu
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <debug.hpp>
#include "clientutil.hpp"
#include "cursorutil.hpp"
#include <status-utils.hpp>
#include "states.hpp"
namespace Kazv
{
BaseJob clientPerform(ClientModel m, GetRoomStatesAction a)
{
auto roomId = a.roomId;
return m.job<GetRoomStateJob>()
.make(roomId)
.withData(json{{"roomId", roomId}});
}
ClientResult updateClient(ClientModel m, GetRoomStatesAction a)
{
m.addJob(clientPerform(m, a));
return { std::move(m), lager::noop };
}
ClientResult processResponse(ClientModel m, GetRoomStateResponse r)
{
auto roomId = r.dataStr("roomId");
if (! r.success()) {
m.addTrigger(GetRoomStatesFailed{roomId, r.errorCode(), r.errorMessage()});
return { std::move(m), failWithResponse(r) };
}
m.addTrigger(GetRoomStatesSuccessful{roomId});
auto events = r.data();
auto action = UpdateRoomAction{
roomId,
AddStateEventsAction{std::move(events)}
};
m.roomList = RoomListModel::update(std::move(m.roomList), action);
if (m.crypto && m.roomList[roomId].encrypted) {
m.deviceLists.track(m.roomList[roomId].joinedMemberIds());
}
m.roomList = RoomListModel::update(std::move(m.roomList),
UpdateRoomAction{roomId, MarkMembersFullyLoadedAction{}});
//m.addTrigger(ShouldQueryKeys{false});
return {std::move(m), lager::noop};
}
ClientResult updateClient(ClientModel m, GetStateEventAction a)
{
auto job = m.job<GetRoomStateWithKeyJob>()
.make(a.roomId, a.type, a.stateKey)
.withData(json{
{"roomId", a.roomId},
{"type", a.type},
{"stateKey", a.stateKey}
});
m.addJob(std::move(job));
return { std::move(m), lager::noop };
}
ClientResult processResponse(ClientModel m, GetRoomStateWithKeyResponse r)
{
auto roomId = r.dataStr("roomId");
auto type = r.dataStr("type");
auto stateKey = r.dataStr("stateKey");
if (! r.success()) {
m.addTrigger(GetStateEventFailed{roomId, r.errorCode(), r.errorMessage()});
return { std::move(m), failWithResponse(r) };
}
auto content = r.jsonBody();
m.addTrigger(GetStateEventSuccessful{roomId, content});
auto k = KeyOfState{type, stateKey};
auto eventJson = m.roomList[roomId].stateEvents[k]
.originalJson().get();
eventJson["content"] = content;
eventJson["type"] = type;
eventJson["state_key"] = stateKey;
auto a = AddStateEventsAction{EventList{Event{eventJson}}};
auto l = RoomListModel::update(
std::move(m.roomList),
UpdateRoomAction{roomId, a});
m.roomList = std::move(l);
return { std::move(m), lager::noop };
}
ClientResult updateClient(ClientModel m, SendStateEventAction a)
{
auto event = a.event;
if (event.type() == ""s) {
m.addTrigger(InvalidMessageFormat{});
- return { std::move(m), lager::noop };
+ return {
+ std::move(m),
+ returnEffect(EffectStatus(/* succ = */ false, json{
+ {"errorCode", "MOE_KAZV_MXC_KAZV_INVALID_MESSAGE_FORMAT"},
+ {"error", "Invalid message format"},
+ }))
+ };
}
auto type = event.type();
auto content = event.content();
auto stateKey = event.stateKey();
kzo.client.dbg() << "Sending state event of type " << type
<< " with content " << content.get().dump()
<< " to " << a.roomId
<< " with state key #" << stateKey << std::endl;
auto job = m.job<SetRoomStateWithKeyJob>().make(
a.roomId,
type,
stateKey,
content)
.withData(json{
{"roomId", a.roomId},
{"eventType", type},
{"stateKey", stateKey},
});
m.addJob(std::move(job));
return { std::move(m), lager::noop };
}
ClientResult processResponse(ClientModel m, SetRoomStateWithKeyResponse r)
{
auto roomId = r.dataStr("roomId");
auto eventType = r.dataStr("eventType");
auto stateKey = r.dataStr("stateKey");
if (! r.success()) {
kzo.client.dbg() << "Send state event failed" << std::endl;
m.addTrigger(SendStateEventFailed{roomId, eventType, stateKey, r.errorCode(), r.errorMessage()});
return { std::move(m), failWithResponse(r) };
}
m.addTrigger(SendStateEventSuccessful{roomId, r.eventId(), eventType, stateKey});
return { std::move(m), lager::noop };
}
}
diff --git a/src/client/actions/sync.cpp b/src/client/actions/sync.cpp
index 544d469..ce468f0 100644
--- a/src/client/actions/sync.cpp
+++ b/src/client/actions/sync.cpp
@@ -1,357 +1,357 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <lager/util.hpp>
#include <zug/transducer/map.hpp>
#include <zug/transducer/cat.hpp>
#include <zug/transducer/filter.hpp>
#include <zug/sequence.hpp>
#include <jobinterface.hpp>
#include <debug.hpp>
#include "cursorutil.hpp"
#include "sync.hpp"
#include "encryption.hpp"
#include "status-utils.hpp"
namespace Kazv
{
// Atomicity guaranteed: if the sync action is created
// before an action that reasonably changes Client
// (e.g. roll back to an earlier state, obtain other
// events), but executed
// after that action, the sync will still give continuous
// data about the events. (Sync will not "skip" events)
// This is because this function takes the sync token
// from the ClientModel model it is passed.
ClientResult updateClient(ClientModel m, SyncAction)
{
kzo.client.dbg() << "Start syncing with token " <<
(m.syncToken ? m.syncToken.value() : "<null>") << std::endl;
bool isInitialSync = ! m.syncToken;
m.syncing = true;
std::string filter = m.syncToken ? m.incrementalSyncFilterId : m.initialSyncFilterId;
m.addJob(m.job<SyncJob>()
.make(filter,
m.syncToken,
std::nullopt, // fullState
std::nullopt, // setPresence
// Let initial sync return immediately
isInitialSync ? 0 : m.syncTimeoutMs
)
.withData(json{{"is", isInitialSync ? "initial" : "incremental"}}));
return { m, lager::noop };
}
static KazvEventList loadRoomsFromSyncInPlace(ClientModel &m, SyncJob::Rooms rooms)
{
auto l = std::move(m.roomList);
auto eventsToEmit = KazvEventList{}.transient();
auto updateRoomImpl =
[&l](auto id, auto a) {
l = RoomListModel::update(
std::move(l),
UpdateRoomAction{std::move(id), std::move(a)});
};
auto updateSingleRoom =
[&, updateRoomImpl](const auto &id, const auto &room, auto membership) {
if (!l.has(id) || l[id].membership != membership) {
eventsToEmit.push_back(RoomMembershipChanged{membership, id});
}
updateRoomImpl(id, ChangeMembershipAction{membership});
auto timelineEvents =
intoImmer(
EventList{},
zug::map([=](Event e) {
return Event::fromSync(e, id);
}),
room.timeline.events);
eventsToEmit.append(
intoImmer(
KazvEventList{},
zug::map([=](Event e) -> KazvEvent {
return ReceivingRoomTimelineEvent{std::move(e), id};
}),
timelineEvents).transient());
updateRoomImpl(id, AddToTimelineAction{timelineEvents,
room.timeline.prevBatch,
room.timeline.limited,
std::nullopt // we do not have a gapEventId
});
if (room.state) {
eventsToEmit.append(
intoImmer(
KazvEventList{},
zug::map([=](Event e) -> KazvEvent {
return ReceivingRoomStateEvent{std::move(e), id};
}),
room.state.value().events).transient());
updateRoomImpl(id, AddStateEventsAction{room.state.value().events});
}
// Process state events in timeline, which should have arrived later
// than those in room.state .
updateRoomImpl(id, AddStateEventsAction{
intoImmer(EventList{},
zug::filter([=](Event e) {
return e.isState();
}),
timelineEvents)});
if (room.accountData) {
eventsToEmit.append(
intoImmer(
KazvEventList{},
zug::map([=](Event e) -> KazvEvent {
return ReceivingRoomAccountDataEvent{std::move(e), id};
}),
room.state.value().events).transient());
updateRoomImpl(id, AddAccountDataAction{room.accountData.value().events});
}
};
auto updateRoomSummary =
[=](const auto &id, const auto &room) {
if (!room.summary.has_value()) {
return;
}
if (!room.summary->mHeroes.empty()) {
auto newHeroes = room.summary->mHeroes;
updateRoomImpl(id, SetHeroIdsAction{immer::flex_vector<std::string>(newHeroes.begin(), newHeroes.end())});
}
};
auto updateJoinedRoom =
[=](const auto &id, const auto &room) {
updateSingleRoom(id, room, RoomMembership::Join);
if (room.ephemeral) {
updateRoomImpl(id, AddEphemeralAction{room.ephemeral.value().events});
}
// TODO update other info such as
// notification and summary
updateRoomSummary(id, room);
};
auto updateInvitedRoom =
[=](const auto &id, const auto &room) {
updateRoomImpl(id, ChangeMembershipAction{RoomMembership::Invite});
if (room.inviteState) {
updateRoomImpl(id, ChangeInviteStateAction{room.inviteState.value().events});
}
};
auto updateLeftRoom =
[=](const auto &id, const auto &room) {
updateSingleRoom(id, room, RoomMembership::Leave);
};
for (const auto &[id, room]: rooms.join) {
updateJoinedRoom(id, room);
}
// TODO update info for invited rooms
for (const auto &[id, room]: rooms.invite) {
updateInvitedRoom(id, room);
}
for (const auto &[id, room]: rooms.leave) {
updateLeftRoom(id, room);
}
m.roomList = std::move(l);
return eventsToEmit.persistent();
}
static KazvEventList loadPresenceFromSyncInPlace(ClientModel &m, EventList presence)
{
auto eventsToEmit = intoImmer(
KazvEventList{},
zug::map([](Event e) { return ReceivingPresenceEvent{e}; }),
presence);
m.presence = merge(std::move(m.presence), presence, keyOfPresence);
return eventsToEmit;
}
static KazvEventList loadAccountDataFromSyncInPlace(ClientModel &m, EventList accountData)
{
auto eventsToEmit = intoImmer(
KazvEventList{},
zug::map([](Event e) { return ReceivingPresenceEvent{e}; }),
accountData);
m.accountData = merge(std::move(m.accountData), accountData, keyOfAccountData);
return eventsToEmit;
}
static KazvEventList loadToDeviceFromSyncInPlace(ClientModel &m, JsonWrap toDevice)
{
if (toDevice.get().contains("events")) {
auto events = toDevice.get()["events"];
auto msgs = intoImmer(
EventList{},
zug::map([](const json &j) { return Event(j); }),
events);
m.toDevice = std::move(m.toDevice) + msgs;
return intoImmer(
KazvEventList{},
zug::map([](Event e) { return ReceivingToDeviceMessage{e}; }),
msgs);
}
return {};
}
ClientResult processResponse(ClientModel m, SyncResponse r)
{
if (! r.success()) {
m.addTrigger(SyncFailed{});
kzo.client.dbg() << "Sync failed" << std::endl;
kzo.client.dbg() << r.statusCode << std::endl;
if (isBodyJson(r.body)) {
auto j = r.jsonBody();
kzo.client.dbg() << "Json says: " << j.get().dump() << std::endl;
} else {
kzo.client.dbg() << "Response body: "
<< std::get<BaseJob::BytesBody>(r.body) << std::endl;
}
return { std::move(m), failWithResponse(r) };
}
kzo.client.dbg() << "Sync successful" << std::endl;
auto rooms = r.rooms();
auto accountData = r.accountData();
auto presence = r.presence();
// load the info that has been sync'd
m.syncToken = r.nextBatch();
if (rooms) {
m.addTriggers(loadRoomsFromSyncInPlace(m, std::move(rooms.value())));
}
if (presence) {
m.addTriggers(loadPresenceFromSyncInPlace(m, std::move(presence.value().events)));
}
if (accountData) {
m.addTriggers(loadAccountDataFromSyncInPlace(m, std::move(accountData.value().events)));
}
m.addTriggers(loadToDeviceFromSyncInPlace(m, r.toDevice()));
auto is = r.dataStr("is");
auto isInitialSync = is == "initial";
if (m.crypto) {
kzo.client.dbg() << "E2EE is on. Processing device lists and one-time key counts." << std::endl;
auto &crypto = m.crypto.value();
// process deviceLists
if (isInitialSync) {
auto encryptedUsers =
zug::sequence(
zug::map([](auto n) { return n.second; })
| zug::filter([](auto room) { return room.encrypted; })
| zug::map([](auto room) { return room.joinedMemberIds(); })
| zug::cat,
// no need to use distinct here as the map will overwrite
m.roomList.rooms);
m.deviceLists.track(std::move(encryptedUsers));
} else {
const auto &l = r.deviceLists().get();
if (l.contains("changed")) {
const auto &changed = l.at("changed");
m.deviceLists.track(changed);
}
if (l.contains("left")) {
const auto &left = l.at("left");
m.deviceLists.untrack(left);
}
}
// deviceOneTimeKeysCount
crypto.setUploadedOneTimeKeysCount(r.deviceOneTimeKeysCount());
auto model = tryDecryptEvents(std::move(m));
m = std::move(model);
}
m.addTrigger(SyncSuccessful{r.nextBatch()});
return { std::move(m), lager::noop };
}
ClientResult updateClient(ClientModel m, SetShouldSyncAction a)
{
m.shouldSync = a.shouldSync;
return { std::move(m), lager::noop };
}
ClientResult updateClient(ClientModel m, PostInitialFiltersAction)
{
if (m.syncing) {
return { std::move(m), lager::noop };
}
Filter initialSyncFilter;
initialSyncFilter.room.timeline.limit = 1;
initialSyncFilter.room.state.lazyLoadMembers = true;
auto firstJob = m.job<DefineFilterJob>()
.make(m.userId, initialSyncFilter)
.withData(json{{"is", "initialSyncFilter"}})
.withQueue("post-filter", CancelFutureIfFailed);
m.addJob(firstJob);
Filter incrementalSyncFilter;
incrementalSyncFilter.room.timeline.limit = 20;
incrementalSyncFilter.room.state.lazyLoadMembers = true;
m.addJob(m.job<DefineFilterJob>()
.make(m.userId, incrementalSyncFilter)
.withData(json{{"is", "incrementalSyncFilter"}})
.withQueue("post-filter", CancelFutureIfFailed));
m.syncing = true;
return { std::move(m), lager::noop };
}
ClientResult processResponse(ClientModel m, DefineFilterResponse r)
{
auto is = r.dataStr("is");
if (! r.success()) {
m.syncing = false;
kzo.client.dbg() << "posting filter failed: " << r.errorCode() << r.errorMessage() << std::endl;
m.addTrigger(PostInitialFiltersFailed{r.errorCode(), r.errorMessage()});
- return { std::move(m), lager::noop };
+ return { std::move(m), failWithResponse(r) };
}
kzo.client.dbg() << "filter " << is << " is posted" << std::endl;
if (is == "incrementalSyncFilter") {
m.incrementalSyncFilterId = r.filterId();
m.addTrigger(PostInitialFiltersSuccessful{});
} else {
m.initialSyncFilterId = r.filterId();
}
return { std::move(m), lager::noop };
}
}
diff --git a/src/client/status-utils.hpp b/src/client/status-utils.hpp
index 6429463..ae47484 100644
--- a/src/client/status-utils.hpp
+++ b/src/client/status-utils.hpp
@@ -1,45 +1,64 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2020 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <context.hpp>
namespace Kazv
{
namespace detail
{
struct SimpleFailT
{
template<class Context>
EffectStatus operator()(Context &&) const {
return EffectStatus(/* succ = */ false);
};
};
+
+ struct ReturnStatusT
+ {
+ EffectStatus m_status;
+
+ template<class Context>
+ EffectStatus operator()(Context &&) const {
+ return m_status;
+ };
+ };
}
/**
* A effect that returns a failed EffectStatus upon invocation.
*/
constexpr auto simpleFail = detail::SimpleFailT{};
+ /**
+ * @param s The EffectStatus to return in the effect.
+ * @return An effect that returns s upon invocation.
+ */
+ inline auto returnEffect(EffectStatus s)
+ {
+ return detail::ReturnStatusT{s};
+ };
+
/**
* A effect that returns a failed EffectStatus upon invocation.
*/
template<class Resp>
constexpr auto failWithResponse(Resp &&r)
{
auto code = r.errorCode();
auto msg = std::forward<Resp>(r).errorMessage();
auto d = json{
{"error", msg},
{"errorCode", code},
};
return [data=JsonWrap(d)](auto &&) {
return EffectStatus(/* succ = */ false, data);
};
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Nov 2, 9:28 AM (3 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1804210
Default Alt Text
(31 KB)
Attached To
Mode
rL libkazv
Attached
Detach File
Event Timeline