Page MenuHomePhorge

No OneTemporary

Size
62 KB
Referenced Files
None
Subscribers
None
diff --git a/src/base/kazvevents.hpp b/src/base/kazvevents.hpp
index fb4954f..326a75a 100644
--- a/src/base/kazvevents.hpp
+++ b/src/base/kazvevents.hpp
@@ -1,591 +1,619 @@
/*
* Copyright (C) 2020 Tusooa Zhu
*
* This file is part of libkazv.
*
* libkazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* libkazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with libkazv. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <variant>
#include "types.hpp"
#include "event.hpp"
#include "basejob.hpp"
namespace Kazv
{
struct LoginSuccessful {};
inline bool operator==(LoginSuccessful, LoginSuccessful)
{
return true;
}
struct LoginFailed
{
std::string errorCode;
std::string error;
};
inline bool operator==(LoginFailed a, LoginFailed b)
{
return a.errorCode == b.errorCode
&& a.error == b.error;
}
struct SyncSuccessful
{
std::string nextToken;
};
inline bool operator==(SyncSuccessful a, SyncSuccessful b)
{
return a.nextToken == b.nextToken;
}
struct SyncFailed
{
};
inline bool operator==(SyncFailed, SyncFailed)
{
return true;
}
struct PostInitialFiltersSuccessful
{
};
inline bool operator==(PostInitialFiltersSuccessful, PostInitialFiltersSuccessful)
{
return true;
}
struct PostInitialFiltersFailed
{
std::string errorCode;
std::string error;
};
inline bool operator==(PostInitialFiltersFailed a, PostInitialFiltersFailed b)
{
return a.errorCode == b.errorCode
&& a.error == b.error;
}
struct ReceivingPresenceEvent { Event event; };
inline bool operator==(ReceivingPresenceEvent a, ReceivingPresenceEvent b)
{
return a.event == b.event;
}
struct ReceivingAccountDataEvent { Event event; };
inline bool operator==(ReceivingAccountDataEvent a, ReceivingAccountDataEvent b)
{
return a.event == b.event;
}
struct ReceivingRoomStateEvent {
Event event;
std::string roomId;
};
inline bool operator==(ReceivingRoomStateEvent a, ReceivingRoomStateEvent b)
{
return a.event == b.event && a.roomId == b.roomId;
}
struct ReceivingRoomTimelineEvent {
Event event;
std::string roomId;
};
inline bool operator==(ReceivingRoomTimelineEvent a, ReceivingRoomTimelineEvent b)
{
return a.event == b.event && a.roomId == b.roomId;
}
struct ReceivingRoomAccountDataEvent {
Event event;
std::string roomId;
};
inline bool operator==(ReceivingRoomAccountDataEvent a, ReceivingRoomAccountDataEvent b)
{
return a.event == b.event && a.roomId == b.roomId;
}
struct RoomMembershipChanged {
RoomMembership membership;
std::string roomId;
};
inline bool operator==(RoomMembershipChanged a, RoomMembershipChanged b)
{
return a.membership == b.membership && a.roomId == b.roomId;
}
struct PaginateSuccessful
{
std::string roomId;
};
inline bool operator==(PaginateSuccessful a, PaginateSuccessful b)
{
return a.roomId == b.roomId;
}
struct PaginateFailed
{
std::string roomId;
};
inline bool operator==(PaginateFailed a, PaginateFailed b)
{
return a.roomId == b.roomId;
}
struct CreateRoomSuccessful
{
std::string roomId;
};
inline bool operator==(CreateRoomSuccessful a, CreateRoomSuccessful b)
{
return a.roomId == b.roomId;
}
struct CreateRoomFailed
{
std::string errorCode;
std::string error;
};
inline bool operator==(CreateRoomFailed a, CreateRoomFailed b)
{
return a.errorCode == b.errorCode
&& a.error == b.error;
}
struct InviteUserSuccessful
{
std::string roomId;
std::string userId;
};
inline bool operator==(InviteUserSuccessful a, InviteUserSuccessful b)
{
return a.roomId == b.roomId
&& a.userId == b.userId;
}
struct InviteUserFailed
{
std::string roomId;
std::string userId;
std::string errorCode;
std::string error;
};
inline bool operator==(InviteUserFailed a, InviteUserFailed b)
{
return a.roomId == b.roomId
&& a.userId == b.userId
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct JoinRoomSuccessful
{
std::string roomIdOrAlias;
};
inline bool operator==(JoinRoomSuccessful a, JoinRoomSuccessful b)
{
return a.roomIdOrAlias == b.roomIdOrAlias;
};
struct JoinRoomFailed
{
std::string roomIdOrAlias;
std::string errorCode;
std::string error;
};
inline bool operator==(JoinRoomFailed a, JoinRoomFailed b)
{
return a.roomIdOrAlias == b.roomIdOrAlias
&& a.errorCode == b.errorCode
&& a.error == b.error;
};
struct LeaveRoomSuccessful
{
std::string roomId;
};
inline bool operator==(LeaveRoomSuccessful a, LeaveRoomSuccessful b)
{
return a.roomId == b.roomId;
};
struct LeaveRoomFailed
{
std::string roomId;
std::string errorCode;
std::string error;
};
inline bool operator==(LeaveRoomFailed a, LeaveRoomFailed b)
{
return a.roomId == b.roomId
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct ForgetRoomSuccessful
{
std::string roomId;
};
inline bool operator==(ForgetRoomSuccessful a, ForgetRoomSuccessful b)
{
return a.roomId == b.roomId;
};
struct ForgetRoomFailed
{
std::string roomId;
std::string errorCode;
std::string error;
};
inline bool operator==(ForgetRoomFailed a, ForgetRoomFailed b)
{
return a.roomId == b.roomId
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct SendMessageSuccessful
{
std::string roomId;
std::string eventId;
};
inline bool operator==(SendMessageSuccessful a, SendMessageSuccessful b)
{
return a.roomId == b.roomId
&& a.eventId == b.eventId;
}
struct SendMessageFailed
{
std::string roomId;
std::string errorCode;
std::string error;
};
inline bool operator==(SendMessageFailed a, SendMessageFailed b)
{
return a.roomId == b.roomId
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct InvalidMessageFormat
{
};
inline bool operator==(InvalidMessageFormat, InvalidMessageFormat)
{
return true;
}
struct GetRoomStatesSuccessful
{
std::string roomId;
};
inline bool operator==(GetRoomStatesSuccessful a, GetRoomStatesSuccessful b)
{
return a.roomId == b.roomId;
}
struct GetRoomStatesFailed
{
std::string roomId;
std::string errorCode;
std::string error;
};
inline bool operator==(GetRoomStatesFailed a, GetRoomStatesFailed b)
{
return a.roomId == b.roomId
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
+ struct GetStateEventSuccessful
+ {
+ std::string roomId;
+ JsonWrap content;
+ };
+
+ inline bool operator==(GetStateEventSuccessful a, GetStateEventSuccessful b)
+ {
+ return a.roomId == b.roomId
+ && a.content == b.content;
+ }
+
+ struct GetStateEventFailed
+ {
+ std::string roomId;
+ std::string errorCode;
+ std::string error;
+ };
+
+ inline bool operator==(GetStateEventFailed a, GetStateEventFailed b)
+ {
+ return a.roomId == b.roomId
+ && a.errorCode == b.errorCode
+ && a.error == b.error;
+ }
+
+
struct SendStateEventSuccessful
{
std::string roomId;
std::string eventId;
std::string eventType;
std::string stateKey;
};
inline bool operator==(SendStateEventSuccessful a, SendStateEventSuccessful b)
{
return a.roomId == b.roomId
&& a.eventId == b.eventId
&& a.eventType == b.eventType
&& a.stateKey == b.stateKey;
}
struct SendStateEventFailed
{
std::string roomId;
std::string eventType;
std::string stateKey;
std::string errorCode;
std::string error;
};
inline bool operator==(SendStateEventFailed a, SendStateEventFailed b)
{
return a.roomId == b.roomId
&& a.eventType == b.eventType
&& a.stateKey == b.stateKey
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct SetTypingSuccessful
{
std::string roomId;
};
inline bool operator==(SetTypingSuccessful a, SetTypingSuccessful b)
{
return a.roomId == b.roomId;
}
struct SetTypingFailed
{
std::string roomId;
std::string errorCode;
std::string error;
};
inline bool operator==(SetTypingFailed a, SetTypingFailed b)
{
return a.roomId == b.roomId
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct PostReceiptSuccessful
{
std::string roomId;
};
inline bool operator==(PostReceiptSuccessful a, PostReceiptSuccessful b)
{
return a.roomId == b.roomId;
}
struct PostReceiptFailed
{
std::string roomId;
std::string errorCode;
std::string error;
};
inline bool operator==(PostReceiptFailed a, PostReceiptFailed b)
{
return a.roomId == b.roomId
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct SetReadMarkerSuccessful
{
std::string roomId;
};
inline bool operator==(SetReadMarkerSuccessful a, SetReadMarkerSuccessful b)
{
return a.roomId == b.roomId;
}
struct SetReadMarkerFailed
{
std::string roomId;
std::string errorCode;
std::string error;
};
inline bool operator==(SetReadMarkerFailed a, SetReadMarkerFailed b)
{
return a.roomId == b.roomId
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct UploadContentSuccessful
{
std::string mxcUri;
std::string uploadId;
};
inline bool operator==(UploadContentSuccessful a, UploadContentSuccessful b)
{
return a.mxcUri == b.mxcUri
&& a.uploadId == b.uploadId;
};
struct UploadContentFailed
{
std::string uploadId;
std::string errorCode;
std::string error;
};
inline bool operator==(UploadContentFailed a, UploadContentFailed b)
{
return a.uploadId == b.uploadId
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct DownloadContentSuccessful
{
std::string mxcUri;
immer::box<Bytes> content;
std::optional<std::string> filename;
std::optional<std::string> contentType;
};
inline bool operator==(DownloadContentSuccessful a, DownloadContentSuccessful b)
{
return a.mxcUri == b.mxcUri
&& a.content == b.content
&& a.filename == b.filename
&& a.contentType == b.contentType;
};
struct DownloadContentFailed
{
std::string mxcUri;
std::string errorCode;
std::string error;
};
inline bool operator==(DownloadContentFailed a, DownloadContentFailed b)
{
return a.mxcUri == b.mxcUri
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct DownloadThumbnailSuccessful
{
std::string mxcUri;
immer::box<Bytes> content;
std::optional<std::string> contentType;
};
inline bool operator==(DownloadThumbnailSuccessful a, DownloadThumbnailSuccessful b)
{
return a.mxcUri == b.mxcUri
&& a.content == b.content
&& a.contentType == b.contentType;
};
struct DownloadThumbnailFailed
{
std::string mxcUri;
std::string errorCode;
std::string error;
};
inline bool operator==(DownloadThumbnailFailed a, DownloadThumbnailFailed b)
{
return a.mxcUri == b.mxcUri
&& a.errorCode == b.errorCode
&& a.error == b.error;
}
struct UnrecognizedResponse
{
Response response;
};
inline bool operator==(UnrecognizedResponse a, UnrecognizedResponse b)
{
return a.response == b.response;
}
using KazvEvent = std::variant<
// use this for placeholder of "no events yet"
// otherwise the first LoginSuccessful event cannot be detected
std::monostate,
// matrix events
ReceivingPresenceEvent,
ReceivingAccountDataEvent,
ReceivingRoomTimelineEvent,
ReceivingRoomStateEvent,
RoomMembershipChanged,
ReceivingRoomAccountDataEvent,
// auth
LoginSuccessful, LoginFailed,
// sync
SyncSuccessful, SyncFailed,
PostInitialFiltersSuccessful, PostInitialFiltersFailed,
// paginate
PaginateSuccessful, PaginateFailed,
// membership
CreateRoomSuccessful, CreateRoomFailed,
InviteUserSuccessful, InviteUserFailed,
JoinRoomSuccessful, JoinRoomFailed,
LeaveRoomSuccessful, LeaveRoomFailed,
ForgetRoomSuccessful, ForgetRoomFailed,
// send
SendMessageSuccessful, SendMessageFailed,
InvalidMessageFormat,
// states
GetRoomStatesSuccessful, GetRoomStatesFailed,
+ GetStateEventSuccessful, GetStateEventFailed,
SendStateEventSuccessful, SendStateEventFailed,
// ephemeral
SetTypingSuccessful, SetTypingFailed,
PostReceiptSuccessful, PostReceiptFailed,
SetReadMarkerSuccessful, SetReadMarkerFailed,
// content
UploadContentSuccessful, UploadContentFailed,
DownloadContentSuccessful, DownloadContentFailed,
DownloadThumbnailSuccessful, DownloadThumbnailFailed,
// general
UnrecognizedResponse
>;
inline bool operator!=(KazvEvent a, KazvEvent b)
{
return !(a == b);
}
using KazvEventList = immer::flex_vector<KazvEvent>;
}
diff --git a/src/client/actions/states.cpp b/src/client/actions/states.cpp
index 241fd74..3011155 100644
--- a/src/client/actions/states.cpp
+++ b/src/client/actions/states.cpp
@@ -1,113 +1,163 @@
/*
* Copyright (C) 2020 Tusooa Zhu
*
* This file is part of libkazv.
*
* libkazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* libkazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with libkazv. If not, see <https://www.gnu.org/licenses/>.
*/
#include <debug.hpp>
#include "clientutil.hpp"
#include "cursorutil.hpp"
#include "states.hpp"
namespace Kazv
{
ClientResult updateClient(ClientModel m, GetRoomStatesAction a)
{
auto roomId = a.roomId;
auto job = m.job<GetRoomStateJob>()
.make(roomId)
.withData(json{{"roomId", roomId}});
m.addJob(std::move(job));
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), lager::noop };
}
m.addTrigger(GetRoomStatesSuccessful{roomId});
auto events = r.data();
auto action = UpdateRoomAction{
std::move(roomId),
AddStateEventsAction{std::move(events)}
};
m.roomList = RoomListModel::update(std::move(m.roomList), action);
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), lager::noop };
+ }
+
+ 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 };
}
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), lager::noop };
}
m.addTrigger(SendStateEventSuccessful{roomId, r.eventId(), eventType, stateKey});
return { std::move(m), lager::noop };
}
}
diff --git a/src/client/actions/states.hpp b/src/client/actions/states.hpp
index fe37934..ab8870b 100644
--- a/src/client/actions/states.hpp
+++ b/src/client/actions/states.hpp
@@ -1,33 +1,35 @@
/*
* Copyright (C) 2020 Tusooa Zhu <tusooa@vista.aero>
*
* This file is part of libkazv.
*
* libkazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* libkazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with libkazv. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <csapi/rooms.hpp>
#include <csapi/room_state.hpp>
#include "client-model.hpp"
namespace Kazv
{
ClientResult updateClient(ClientModel m, GetRoomStatesAction a);
ClientResult processResponse(ClientModel m, GetRoomStateResponse r);
+ ClientResult updateClient(ClientModel m, GetStateEventAction a);
+ ClientResult processResponse(ClientModel m, GetRoomStateWithKeyResponse r);
ClientResult updateClient(ClientModel m, SendStateEventAction a);
ClientResult processResponse(ClientModel m, SetRoomStateWithKeyResponse r);
}
diff --git a/src/client/client-model.cpp b/src/client/client-model.cpp
index 22a2e35..24a4e68 100644
--- a/src/client/client-model.cpp
+++ b/src/client/client-model.cpp
@@ -1,105 +1,106 @@
/*
* Copyright (C) 2020 Tusooa Zhu <tusooa@vista.aero>
*
* This file is part of libkazv.
*
* libkazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* libkazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with libkazv. If not, see <https://www.gnu.org/licenses/>.
*/
#include <lager/util.hpp>
#include <lager/context.hpp>
#include <functional>
#include <immer/flex_vector_transient.hpp>
#include "debug.hpp"
#include "client-model.hpp"
#include "actions/states.hpp"
#include "actions/auth.hpp"
#include "actions/membership.hpp"
#include "actions/paginate.hpp"
#include "actions/send.hpp"
#include "actions/states.hpp"
#include "actions/sync.hpp"
#include "actions/ephemeral.hpp"
#include "actions/content.hpp"
namespace Kazv
{
auto ClientModel::update(ClientModel m, Action a) -> Result
{
return lager::match(std::move(a))(
[&](Error::Action a) -> Result {
m.error = Error::update(m.error, a);
return {std::move(m), lager::noop};
},
[&](RoomListAction a) -> Result {
m.roomList = RoomListModel::update(std::move(m.roomList), a);
return {std::move(m), lager::noop};
},
[&](ResubmitJobAction a) -> Result {
m.addJob(std::move(a.job));
return { std::move(m), lager::noop };
},
[&](auto a) -> decltype(updateClient(m, a)) {
return updateClient(m, a);
},
#define RESPONSE_FOR(_jobId) \
if (r.jobId() == #_jobId) { \
return processResponse(m, _jobId##Response{std::move(r)}); \
}
[&](ProcessResponseAction a) -> Result {
auto r = std::move(a.response);
// auth
RESPONSE_FOR(Login);
// paginate
RESPONSE_FOR(GetRoomEvents);
// sync
RESPONSE_FOR(Sync);
RESPONSE_FOR(DefineFilter);
// membership
RESPONSE_FOR(CreateRoom);
RESPONSE_FOR(InviteUser);
RESPONSE_FOR(JoinRoomById);
RESPONSE_FOR(JoinRoom);
RESPONSE_FOR(LeaveRoom);
RESPONSE_FOR(ForgetRoom);
// send
RESPONSE_FOR(SendMessage);
// states
RESPONSE_FOR(GetRoomState);
RESPONSE_FOR(SetRoomStateWithKey);
+ RESPONSE_FOR(GetRoomStateWithKey);
// ephemeral
RESPONSE_FOR(SetTyping);
RESPONSE_FOR(PostReceipt);
RESPONSE_FOR(SetReadMarker);
// content
RESPONSE_FOR(UploadContent);
RESPONSE_FOR(GetContent);
RESPONSE_FOR(GetContentThumbnail);
m.addTrigger(UnrecognizedResponse{std::move(r)});
return { std::move(m), lager::noop };
}
#undef RESPONSE_FOR
);
}
}
diff --git a/src/client/client-model.hpp b/src/client/client-model.hpp
index 3d99a71..7127e66 100644
--- a/src/client/client-model.hpp
+++ b/src/client/client-model.hpp
@@ -1,348 +1,359 @@
/*
* Copyright (C) 2020 Tusooa Zhu
*
* This file is part of libkazv.
*
* libkazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* libkazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with libkazv. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <tuple>
#include <variant>
#include <string>
#include <optional>
#include <lager/context.hpp>
#include <boost/hana.hpp>
#ifndef NDEBUG
#include <lager/debug/cereal/struct.hpp>
#endif
#include <csapi/sync.hpp>
#include <jobinterface.hpp>
#include <eventinterface.hpp>
#include "clientfwd.hpp"
#include "error.hpp"
#include "room/room-model.hpp"
namespace Kazv
{
inline const std::string DEFTXNID{"0"};
enum RoomVisibility
{
Private,
Public,
};
enum CreateRoomPreset
{
PrivateChat,
PublicChat,
TrustedPrivateChat,
};
enum ThumbnailResizingMethod
{
Crop,
Scale,
};
struct ClientModel
{
std::string serverUrl;
std::string userId;
std::string token;
std::string deviceId;
bool loggedIn{false};
Error error;
bool syncing{false};
std::string initialSyncFilterId;
std::string incrementalSyncFilterId;
std::optional<std::string> syncToken;
RoomListModel roomList;
immer::map<std::string /* sender */, Event> presence;
immer::map<std::string /* type */, Event> accountData;
std::string nextTxnId{DEFTXNID};
immer::flex_vector<BaseJob> nextJobs;
immer::flex_vector<KazvEvent> nextTriggers;
// helpers
template<class Job>
struct MakeJobT
{
template<class ...Args>
constexpr auto make(Args &&...args) const {
if constexpr (Job::needsAuth()) {
return Job(
serverUrl,
token,
std::forward<Args>(args)...);
} else {
return Job(
serverUrl,
std::forward<Args>(args)...);
}
}
std::string serverUrl;
std::string token;
};
template<class Job>
constexpr auto job() const {
return MakeJobT<Job>{serverUrl, token};
}
inline void addJob(BaseJob j) {
nextJobs = std::move(nextJobs).push_back(std::move(j));
}
inline auto popAllJobs() {
auto jobs = std::move(nextJobs);
nextJobs = DEFVAL;
return jobs;
};
inline void addTrigger(KazvEvent t) {
addTriggers({t});
}
inline void addTriggers(immer::flex_vector<KazvEvent> c) {
nextTriggers = std::move(nextTriggers) + c;
}
inline auto popAllTriggers() {
auto triggers = std::move(nextTriggers);
nextTriggers = DEFVAL;
return triggers;
}
using Action = ClientAction;
using Effect = ClientEffect;
using Result = ClientResult;
static Result update(ClientModel m, Action a);
};
// actions:
struct LoginAction {
std::string serverUrl;
std::string username;
std::string password;
std::optional<std::string> deviceName;
};
struct TokenLoginAction
{
std::string serverUrl;
std::string username;
std::string token;
std::string deviceId;
};
struct LogoutAction {};
struct SyncAction {};
struct PaginateTimelineAction
{
std::string roomId;
std::optional<int> limit;
};
struct SendMessageAction
{
std::string roomId;
Event event;
};
struct SendStateEventAction
{
std::string roomId;
Event event;
};
struct CreateRoomAction
{
using Visibility = RoomVisibility;
using Preset = CreateRoomPreset;
Visibility visibility;
std::optional<std::string> roomAliasName;
std::optional<std::string> name;
std::optional<std::string> topic;
immer::array<std::string> invite;
//immer::array<Invite3pid> invite3pid;
std::optional<std::string> roomVersion;
JsonWrap creationContent;
immer::array<Event> initialState;
std::optional<Preset> preset;
std::optional<bool> isDirect;
JsonWrap powerLevelContentOverride;
};
struct GetRoomStatesAction
{
std::string roomId;
};
+ struct GetStateEventAction
+ {
+ std::string roomId;
+ std::string type;
+ std::string stateKey;
+ };
+
struct InviteToRoomAction
{
std::string roomId;
std::string userId;
};
struct JoinRoomByIdAction
{
std::string roomId;
};
struct JoinRoomAction
{
std::string roomIdOrAlias;
immer::array<std::string> serverName;
};
struct LeaveRoomAction
{
std::string roomId;
};
struct ForgetRoomAction
{
std::string roomId;
};
struct SetTypingAction
{
std::string roomId;
bool typing;
std::optional<int> timeoutMs;
};
struct PostReceiptAction
{
std::string roomId;
std::string eventId;
};
struct SetReadMarkerAction
{
std::string roomId;
std::string eventId;
};
struct UploadContentAction
{
immer::box<Bytes> content;
std::optional<std::string> filename;
std::optional<std::string> contentType;
std::string uploadId; // to be used by library users
};
struct DownloadContentAction
{
std::string mxcUri;
};
struct DownloadThumbnailAction
{
std::string mxcUri;
int width;
int height;
std::optional<ThumbnailResizingMethod> method;
std::optional<bool> allowRemote;
};
struct ResubmitJobAction
{
BaseJob job;
};
struct ProcessResponseAction
{
Response response;
};
struct PostInitialFiltersAction
{
};
inline bool operator==(ClientModel a, ClientModel b)
{
return a.serverUrl == b.serverUrl
&& a.userId == b.userId
&& a.token == b.token
&& a.deviceId == b.deviceId
&& a.loggedIn == b.loggedIn
&& a.error == b.error
+ && a.syncing == b.syncing
+ && a.initialSyncFilterId == b.initialSyncFilterId
+ && a.incrementalSyncFilterId == b.incrementalSyncFilterId
&& a.syncToken == b.syncToken
&& a.roomList == b.roomList
&& a.presence == b.presence
&& a.accountData == b.accountData
&& a.nextTxnId == b.nextTxnId
&& a.nextJobs == b.nextJobs
&& a.nextTriggers == b.nextTriggers;
}
#ifndef NDEBUG
LAGER_CEREAL_STRUCT(LoginAction);
LAGER_CEREAL_STRUCT(TokenLoginAction);
LAGER_CEREAL_STRUCT(LogoutAction);
LAGER_CEREAL_STRUCT(SyncAction);
LAGER_CEREAL_STRUCT(PostInitialFiltersAction);
LAGER_CEREAL_STRUCT(PaginateTimelineAction);
LAGER_CEREAL_STRUCT(SendMessageAction);
LAGER_CEREAL_STRUCT(SendStateEventAction);
LAGER_CEREAL_STRUCT(CreateRoomAction);
LAGER_CEREAL_STRUCT(GetRoomStatesAction);
+ LAGER_CEREAL_STRUCT(GetStateEventAction);
LAGER_CEREAL_STRUCT(InviteToRoomAction);
LAGER_CEREAL_STRUCT(JoinRoomByIdAction);
LAGER_CEREAL_STRUCT(JoinRoomAction);
LAGER_CEREAL_STRUCT(LeaveRoomAction);
LAGER_CEREAL_STRUCT(ForgetRoomAction);
LAGER_CEREAL_STRUCT(SetTypingAction);
LAGER_CEREAL_STRUCT(PostReceiptAction);
LAGER_CEREAL_STRUCT(ProcessResponseAction);
LAGER_CEREAL_STRUCT(SetReadMarkerAction);
LAGER_CEREAL_STRUCT(UploadContentAction);
LAGER_CEREAL_STRUCT(DownloadContentAction);
LAGER_CEREAL_STRUCT(DownloadThumbnailAction);
LAGER_CEREAL_STRUCT(ResubmitJobAction);
#endif
template<class Archive>
void serialize(Archive &ar, ClientModel &m, std::uint32_t const /*version*/)
{
ar(m.serverUrl, m.userId, m.token, m.deviceId, m.loggedIn,
m.error,
m.syncToken,
m.roomList,
m.presence,
m.accountData,
m.nextTxnId);
}
}
CEREAL_CLASS_VERSION(Kazv::ClientModel, 0);
diff --git a/src/client/client.hpp b/src/client/client.hpp
index c7a6113..68649fb 100644
--- a/src/client/client.hpp
+++ b/src/client/client.hpp
@@ -1,166 +1,176 @@
/*
* Copyright (C) 2020 Tusooa Zhu
*
* This file is part of libkazv.
*
* libkazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* libkazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with libkazv. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <lager/reader.hpp>
#include <immer/box.hpp>
#include <immer/map.hpp>
#include <immer/flex_vector.hpp>
#include <immer/flex_vector_transient.hpp>
#include "client/client-model.hpp"
+#include "client/actions/content.hpp"
+
#include "room/room.hpp"
namespace Kazv
{
class Client
{
public:
inline Client(lager::reader<ClientModel> client,
lager::context<ClientAction> ctx)
: m_client(std::move(client))
, m_ctx(std::move(ctx)) {}
/* lager::reader<immer::map<std::string, Room>> */
inline auto rooms() const {
return m_client
[&ClientModel::roomList]
[&RoomListModel::rooms];
}
/* lager::reader<RangeT<std::string>> */
inline auto roomIds() const {
return rooms().xform(
zug::map([](auto m) {
return intoImmer(
immer::flex_vector<std::string>{},
zug::map([](auto val) { return val.first; }),
m);
}));
}
KAZV_WRAP_ATTR(ClientModel, m_client, serverUrl)
KAZV_WRAP_ATTR(ClientModel, m_client, loggedIn)
KAZV_WRAP_ATTR(ClientModel, m_client, userId)
KAZV_WRAP_ATTR(ClientModel, m_client, token)
KAZV_WRAP_ATTR(ClientModel, m_client, deviceId)
/* Room */
inline auto room(std::string id) const {
return Room(rooms()[std::move(id)]
[lager::lenses::or_default].make(),
m_ctx);
}
inline auto roomByCursor(lager::reader<std::string> id) const {
return Room(lager::with(rooms(), id)
.xform(zug::map([](auto rooms, auto id) {
return rooms[id];
})).make(),
m_ctx);
}
inline void passwordLogin(std::string homeserver, std::string username,
std::string password, std::string deviceName) const {
m_ctx.dispatch(LoginAction{
homeserver, username, password, deviceName});
}
inline void tokenLogin(std::string homeserver, std::string username,
std::string token, std::string deviceId) const {
m_ctx.dispatch(TokenLoginAction{
homeserver, username, token, deviceId});
}
inline void createRoom(RoomVisibility v,
std::optional<std::string> name = {},
std::optional<std::string> alias = {},
immer::array<std::string> invite = {},
std::optional<bool> isDirect = {},
bool allowFederate = true,
std::optional<std::string> topic = {}) const {
CreateRoomAction a;
a.visibility = v;
a.name = name;
a.roomAliasName = alias;
a.invite = invite;
a.isDirect = isDirect;
a.topic = topic;
// Synapse won't buy it if we do not provide
// a creationContent object.
a.creationContent = json{
{"m.federate", allowFederate}
};
m_ctx.dispatch(std::move(a));
}
inline void joinRoomById(std::string roomId) const {
m_ctx.dispatch(JoinRoomByIdAction{roomId});
}
inline void joinRoom(std::string roomId, immer::array<std::string> serverName) const {
m_ctx.dispatch(JoinRoomAction{roomId, serverName});
}
inline void uploadContent(immer::box<Bytes> content,
std::string uploadId,
std::optional<std::string> filename = std::nullopt,
std::optional<std::string> contentType = std::nullopt) const {
m_ctx.dispatch(UploadContentAction{content, filename, contentType, uploadId});
}
+ inline std::string mxcUriToHttp(std::string mxcUri) const {
+ using namespace CursorOp;
+ auto [serverName, mediaId] = mxcUriToMediaDesc(mxcUri);
+ return (+m_client)
+ .job<GetContentJob>()
+ .make(serverName, mediaId).url();
+ }
+
inline void downloadContent(std::string mxcUri) const {
m_ctx.dispatch(DownloadContentAction{mxcUri});
}
inline void downloadThumbnail(std::string mxcUri,
int width,
int height,
std::optional<ThumbnailResizingMethod> method = std::nullopt) const {
m_ctx.dispatch(DownloadThumbnailAction{mxcUri, width, height, method, std::nullopt});
}
// lager::reader<bool>
inline auto syncing() const {
return m_client[&ClientModel::syncing];
}
inline void startSyncing() const {
using namespace Kazv::CursorOp;
if (+syncing()) {
return;
}
// filters are incomplete
if (! (+m_client[&ClientModel::initialSyncFilterId]).empty()
&& ! (+m_client[&ClientModel::incrementalSyncFilterId]).empty()) {
m_ctx.dispatch(PostInitialFiltersAction{});
} else { // sync is just interrupted
m_ctx.dispatch(SyncAction{});
}
}
private:
lager::reader<ClientModel> m_client;
lager::context<ClientAction> m_ctx;
};
}
diff --git a/src/client/clientfwd.hpp b/src/client/clientfwd.hpp
index 0b3d5b2..213af76 100644
--- a/src/client/clientfwd.hpp
+++ b/src/client/clientfwd.hpp
@@ -1,104 +1,106 @@
/*
* Copyright (C) 2020 Tusooa Zhu
*
* This file is part of libkazv.
*
* libkazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* libkazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with libkazv. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <tuple>
#include <variant>
#include <lager/context.hpp>
#include "error.hpp"
#include "room/room-model.hpp"
namespace Kazv
{
class JobInterface;
class EventInterface;
struct LoginAction;
struct TokenLoginAction;
struct LogoutAction;
struct SyncAction;
struct PostInitialFiltersAction;
struct PaginateTimelineAction;
struct SendMessageAction;
struct SendStateEventAction;
struct CreateRoomAction;
struct GetRoomStatesAction;
+ struct GetStateEventAction;
struct InviteToRoomAction;
struct JoinRoomByIdAction;
struct EmitKazvEventsAction;
struct JoinRoomAction;
struct LeaveRoomAction;
struct ForgetRoomAction;
struct ProcessResponseAction;
struct SetTypingAction;
struct PostReceiptAction;
struct SetReadMarkerAction;
struct UploadContentAction;
struct DownloadContentAction;
struct DownloadThumbnailAction;
struct ResubmitJobAction;
struct ClientModel;
using ClientAction = std::variant<
RoomListAction,
Error::Action,
LoginAction,
TokenLoginAction,
LogoutAction,
SyncAction,
PostInitialFiltersAction,
PaginateTimelineAction,
SendMessageAction,
SendStateEventAction,
CreateRoomAction,
GetRoomStatesAction,
+ GetStateEventAction,
InviteToRoomAction,
JoinRoomByIdAction,
JoinRoomAction,
LeaveRoomAction,
ForgetRoomAction,
ProcessResponseAction,
SetTypingAction,
PostReceiptAction,
SetReadMarkerAction,
UploadContentAction,
DownloadContentAction,
DownloadThumbnailAction,
ResubmitJobAction
>;
using ClientEffect = lager::effect<ClientAction, lager::deps<>>;
using ClientResult = std::pair<ClientModel, ClientEffect>;
}
diff --git a/src/client/room/room-model.hpp b/src/client/room/room-model.hpp
index 71abcee..a670e25 100644
--- a/src/client/room/room-model.hpp
+++ b/src/client/room/room-model.hpp
@@ -1,190 +1,191 @@
/*
* Copyright (C) 2020 Tusooa Zhu
*
* This file is part of libkazv.
*
* libkazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* libkazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with libkazv. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <lager/debug/cereal/struct.hpp>
#include <lager/debug/cereal/immer_flex_vector.hpp>
#include <string>
#include <variant>
#include <immer/flex_vector.hpp>
#include <immer/map.hpp>
#include <csapi/sync.hpp>
#include <event.hpp>
#include "data/cereal_map.hpp"
#include "clientutil.hpp"
namespace Kazv
{
struct AddStateEventsAction
{
immer::flex_vector<Event> stateEvents;
};
struct AppendTimelineAction
{
immer::flex_vector<Event> events;
};
struct PrependTimelineAction
{
immer::flex_vector<Event> events;
std::string paginateBackToken;
};
struct AddAccountDataAction
{
immer::flex_vector<Event> events;
};
struct ChangeMembershipAction
{
RoomMembership membership;
};
struct ChangeInviteStateAction
{
immer::flex_vector<Event> events;
};
struct AddEphemeralAction
{
EventList events;
};
struct SetLocalDraftAction
{
std::string localDraft;
};
struct RoomModel
{
using Membership = RoomMembership;
std::string roomId;
immer::map<KeyOfState, Event> stateEvents;
immer::map<KeyOfState, Event> inviteState;
immer::flex_vector<std::string> timeline;
immer::map<std::string, Event> messages;
immer::map<std::string, Event> accountData;
Membership membership{};
std::string paginateBackToken;
/// whether this room has earlier events to be fetched
bool canPaginateBack{true};
immer::map<std::string, Event> ephemeral;
std::string localDraft;
using Action = std::variant<
AddStateEventsAction,
AppendTimelineAction,
PrependTimelineAction,
AddAccountDataAction,
ChangeMembershipAction,
ChangeInviteStateAction,
AddEphemeralAction,
SetLocalDraftAction
>;
static RoomModel update(RoomModel r, Action a);
};
using RoomAction = RoomModel::Action;
inline bool operator==(RoomModel a, RoomModel b)
{
return a.roomId == b.roomId
&& a.stateEvents == b.stateEvents
&& a.inviteState == b.inviteState
&& a.timeline == b.timeline
&& a.messages == b.messages
&& a.accountData == b.accountData
&& a.membership == b.membership
&& a.paginateBackToken == b.paginateBackToken
&& a.canPaginateBack == b.canPaginateBack
+ && a.ephemeral == b.ephemeral
&& a.localDraft == b.localDraft;
}
struct UpdateRoomAction
{
std::string roomId;
RoomAction roomAction;
};
struct RoomListModel
{
immer::map<std::string, RoomModel> rooms;
inline auto at(std::string id) const { return rooms.at(id); }
inline auto operator[](std::string id) const { return rooms[id]; }
inline bool has(std::string id) const { return rooms.find(id); }
using Action = std::variant<
UpdateRoomAction
>;
static RoomListModel update(RoomListModel l, Action a);
};
using RoomListAction = RoomListModel::Action;
inline bool operator==(RoomListModel a, RoomListModel b)
{
return a.rooms == b.rooms;
}
#ifndef NDEBUG
LAGER_CEREAL_STRUCT(AddStateEventsAction);
LAGER_CEREAL_STRUCT(AppendTimelineAction);
LAGER_CEREAL_STRUCT(PrependTimelineAction);
LAGER_CEREAL_STRUCT(AddAccountDataAction);
LAGER_CEREAL_STRUCT(ChangeMembershipAction);
LAGER_CEREAL_STRUCT(SetLocalDraftAction);
LAGER_CEREAL_STRUCT(ChangeInviteStateAction);
LAGER_CEREAL_STRUCT(UpdateRoomAction);
#endif
template<class Archive>
void serialize(Archive &ar, RoomModel &r, std::uint32_t const /*version*/)
{
ar(r.roomId,
r.stateEvents,
r.inviteState,
r.timeline,
r.messages,
r.accountData,
r.membership,
r.paginateBackToken,
r.canPaginateBack);
}
template<class Archive>
void serialize(Archive &ar, RoomListModel &l, std::uint32_t const /*version*/)
{
ar(l.rooms);
}
}
CEREAL_CLASS_VERSION(Kazv::RoomModel, 0);
CEREAL_CLASS_VERSION(Kazv::RoomListModel, 0);
diff --git a/src/client/room/room.hpp b/src/client/room/room.hpp
index 8d8c9e0..9fb849d 100644
--- a/src/client/room/room.hpp
+++ b/src/client/room/room.hpp
@@ -1,331 +1,356 @@
/*
* Copyright (C) 2020 Tusooa Zhu
*
* This file is part of libkazv.
*
* libkazv is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* libkazv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with libkazv. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <lager/reader.hpp>
#include <lager/context.hpp>
#include <lager/with.hpp>
#include <lager/lenses/optional.hpp>
#include <zug/transducer/map.hpp>
#include <zug/transducer/filter.hpp>
#include <zug/sequence.hpp>
#include <immer/flex_vector_transient.hpp>
#include "debug.hpp"
#include "client-model.hpp"
#include "room-model.hpp"
#include "client/cursorutil.hpp"
namespace Kazv
{
class Room
{
public:
inline Room(lager::reader<RoomModel> room, lager::context<ClientAction> ctx)
: m_room(room)
, m_ctx(ctx) {}
/* lager::reader<MapT<KeyOfState, Event>> */
inline auto stateEvents() const {
return m_room
[&RoomModel::stateEvents];
}
/* lager::reader<std::optional<Event>> */
inline auto stateOpt(KeyOfState k) const {
return stateEvents()
[std::move(k)];
}
/* lager::reader<Event> */
inline auto state(KeyOfState k) const {
return stateOpt(k)
[lager::lenses::or_default];
}
/* lager::reader<RangeT<Event>> */
inline auto timelineEvents() const {
return m_room
.xform(zug::map([](auto r) {
auto messages = r.messages;
auto timeline = r.timeline;
return intoImmer(
immer::flex_vector<Event>{},
zug::map([=](auto eventId) {
return messages[eventId];
}),
timeline);
}));
}
/* lager::reader<std::string> */
inline auto name() const {
using namespace lager::lenses;
return stateEvents()
[KeyOfState{"m.room.name", ""}]
[or_default]
.xform(zug::map([](Event ev) {
auto content = ev.content().get();
return
content.contains("name")
? std::string(content["name"])
// TODO: use heroes to generate a name
: "<no name>";
}));
}
+ /* lager::reader<std::string> */
+ inline auto avatarMxcUri() const {
+ using namespace lager::lenses;
+ return stateEvents()
+ [KeyOfState{"m.room.avatar", ""}]
+ [or_default]
+ .xform(zug::map([](Event ev) {
+ auto content = ev.content().get();
+ return
+ content.contains("avatar")
+ ? std::string(content["avatar"])
+ : "";
+ }));
+ }
+
/* lager::reader<RangeT<std::string>> */
inline auto members() const {
using MemberNode = std::pair<std::string, Kazv::Event>;
auto memberNameTransducer =
zug::filter(
[](auto val) {
auto [k, v] = val;
auto [type, stateKey] = k;
return type == "m.room.member"s;
})
| zug::map(
[](auto val) {
auto [k, v] = val;
auto [type, stateKey] = k;
return MemberNode{stateKey, v};
})
| zug::filter(
[](auto val) {
auto [stateKey, ev] = val;
return ev.content().get()
.at("membership"s) == "join"s;
})
| zug::map(
[](auto val) {
auto [stateKey, ev] = val;
return stateKey;
});
return m_room
[&RoomModel::stateEvents]
.xform(zug::map(
[=](auto eventMap) {
return intoImmer(
immer::flex_vector<std::string>{},
memberNameTransducer,
eventMap);
}));
}
/* lager::reader<std::optional<Event>> */
inline auto memberEventFor(std::string userId) const {
return m_room
[&RoomModel::stateEvents]
.xform(containerMap(immer::flex_vector<Event>{},
zug::filter([=](auto val) {
auto [k, v] = val;
auto [type, stateKey] = k;
return type == "m.room.member"s && stateKey == userId;
}) // -> RangeT<pair<KeyofState{...}, Event>>
| zug::map([](auto val) {
auto [k, event] = val;
return event;
})))
[0];
}
lager::reader<bool> encrypted() const;
/*lager::reader<std::string>*/
KAZV_WRAP_ATTR(RoomModel, m_room, roomId);
/*lager::reader<RoomMembership>*/
KAZV_WRAP_ATTR(RoomModel, m_room, membership);
/*lager::reader<std::string>*/
KAZV_WRAP_ATTR(RoomModel, m_room, localDraft);
inline void setLocalDraft(std::string localDraft) const {
using namespace CursorOp;
m_ctx.dispatch(UpdateRoomAction{+roomId(), SetLocalDraftAction{localDraft}});
}
inline void sendMessage(Event msg) const {
using namespace CursorOp;
m_ctx.dispatch(SendMessageAction{+roomId(), msg});
}
inline void sendTextMessage(std::string text) const {
json j{
{"type", "m.room.message"},
{"content", {
{"msgtype", "m.text"},
{"body", text}
}
}
};
Event e{j};
sendMessage(e);
}
+ inline void refreshRoomState() const {
+ using namespace CursorOp;
+ m_ctx.dispatch(GetRoomStatesAction{+roomId()});
+ }
+
+ inline void getStateEvent(std::string type, std::string stateKey) const {
+ using namespace CursorOp;
+ m_ctx.dispatch(GetStateEventAction{+roomId(), type, stateKey});
+ }
+
inline void sendStateEvent(Event state) const {
using namespace CursorOp;
m_ctx.dispatch(SendStateEventAction{+roomId(), state});
}
inline void setName(std::string name) const {
json j{
{"type", "m.room.name"},
{"content", {
{"name", name}
}
}
};
Event e{j};
sendStateEvent(e);
}
// lager::reader<std::string>
inline auto topic() const {
using namespace lager::lenses;
return stateEvents()
[KeyOfState{"m.room.topic", ""}]
[or_default]
.xform(eventContent
| jsonAtOr("topic"s, ""s));
}
inline void setTopic(std::string topic) const {
json j{
{"type", "m.room.topic"},
{"content", {
{"topic", topic}
}
}
};
Event e{j};
sendStateEvent(e);
}
inline void invite(std::string userId) const {
using namespace CursorOp;
m_ctx.dispatch(InviteToRoomAction{+roomId(), userId});
}
/* lager::reader<MapT<std::string, Event>> */
inline auto ephemeralEvents() const {
return m_room
[&RoomModel::ephemeral];
}
/* lager::reader<std::optional<Event>> */
inline auto ephemeralOpt(std::string type) const {
return m_room
[&RoomModel::ephemeral]
[type];
}
/* lager::reader<Event> */
inline auto ephemeral(std::string type) const {
return m_room
[&RoomModel::ephemeral]
[type]
[lager::lenses::or_default];
}
/* lager::reader<RangeT<std::string>> */
inline auto typingUsers() const {
using namespace lager::lenses;
return ephemeral("m.typing")
.xform(eventContent
| jsonAtOr("user_ids",
immer::flex_vector<std::string>{}));
}
inline void setTyping(bool typing, std::optional<int> timeoutMs) const {
using namespace CursorOp;
m_ctx.dispatch(SetTypingAction{+roomId(), typing, timeoutMs});
}
/* lager::reader<MapT<std::string, Event>> */
inline auto accountDataEvents() const {
return m_room
[&RoomModel::accountData];
}
/* lager::reader<std::optional<Event>> */
inline auto accountDataOpt(std::string type) const {
return m_room
[&RoomModel::accountData]
[type];
}
/* lager::reader<Event> */
inline auto accountData(std::string type) const {
return m_room
[&RoomModel::accountData]
[type]
[lager::lenses::or_default];
}
/* lager::reader<std::string> */
inline auto readMarker() const {
using namespace lager::lenses;
return accountData("m.fully_read")
.xform(eventContent
| jsonAtOr("event_id", std::string{}));
}
inline void leave() const {
using namespace CursorOp;
m_ctx.dispatch(LeaveRoomAction{+roomId()});
}
inline void forget() const {
using namespace CursorOp;
m_ctx.dispatch(ForgetRoomAction{+roomId()});
}
/* lager::reader<JsonWrap> */
inline auto avatar() const {
return state(KeyOfState{"m.room.avatar", ""})
.xform(eventContent);
}
/* lager::reader<RangeT<std::string>> */
inline auto pinnedEvents() const {
return state(KeyOfState{"m.room.pinned_events", ""})
.xform(eventContent
| jsonAtOr("pinned", immer::flex_vector<std::string>{}));
}
inline void setPinnedEvents(immer::flex_vector<std::string> eventIds) const {
json j{
{"type", "m.room.pinned_events"},
{"content", {
{"pinned", eventIds}
}
}
};
Event e{j};
sendStateEvent(e);
}
private:
lager::reader<RoomModel> m_room;
lager::context<ClientAction> m_ctx;
};
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jan 19, 4:22 PM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55318
Default Alt Text
(62 KB)

Event Timeline