Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F140274
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
62 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rL libkazv
Attached
Detach File
Event Timeline
Log In to Comment