libkazv
lagerstoreeventemitter.hpp
Go to the documentation of this file.
1 /*
2  * This file is part of libkazv.
3  * SPDX-FileCopyrightText: 2020 Tusooa Zhu
4  * SPDX-License-Identifier: AGPL-3.0-or-later
5  */
6 
7 #pragma once
8 #include <libkazv-config.hpp>
9 
10 #include <vector>
11 #include <memory>
12 #include <algorithm>
13 
14 
15 #include <lager/store.hpp>
16 #include <lager/reader.hpp>
17 #include <lager/event_loop/manual.hpp>
18 
19 #include "types.hpp"
20 
21 #include "eventinterface.hpp"
22 
23 namespace Kazv
24 {
26  {
27  struct Model { KazvEvent curEvent; };
28  struct Action { KazvEvent nextEvent; };
29 
30  struct ListenerHolder;
31 
32  using Result = std::pair<Model,
33  lager::effect<Action, lager::deps<ListenerHolder &>>>;
34 
35  using SlotT = std::function<void(KazvEvent)>;
36 
37  struct Listener
38  {
39  void emit(KazvEvent e) {
40  for (const auto &slot: m_slots) {
41  slot(e);
42  }
43  }
44 
45  void connect(SlotT slot) {
46  m_slots.push_back(std::move(slot));
47  }
48 
49  std::vector<SlotT> m_slots;
50  };
51 
52  using ListenerSP = std::shared_ptr<Listener>;
53  using ListenerWSP = std::weak_ptr<Listener>;
54 
55  struct ListenerHolder
56  {
57  void sendToListeners(KazvEvent e) {
58  bool needsCleanup = false;
59  for (auto listener : m_listeners) {
60  auto strongListener = listener.lock();
61  if (strongListener) {
62  strongListener->emit(e);
63  } else {
64  needsCleanup = true;
65  }
66  }
67 
68  if (needsCleanup) {
69  std::remove_if(m_listeners.begin(),
70  m_listeners.end(),
71  [](auto ptr) {
72  return ptr.expired();
73  });
74  }
75 
76  }
77 
78  std::vector<ListenerWSP> m_listeners;
79  };
80 
81  inline static Result update(Model, Action a) {
82  return {
83  Model{a.nextEvent},
84  [=](auto &&ctx) {
85  auto &holder = lager::get<ListenerHolder &>(ctx);
86  holder.sendToListeners(a.nextEvent);
87  }
88  };
89  }
90 
91  public:
92  template<class EventLoop>
93  LagerStoreEventEmitter(EventLoop loop)
94  : m_holder{}
95  , m_store(
96  lager::make_store<Action>(
97  Model{},
98  loop,
99  lager::with_reducer(&update),
100  lager::with_deps(std::ref(m_holder))))
101  , m_postingFunc(
102  [loop=loop](auto &&func) mutable {
103  loop.post(std::forward<decltype(func)>(func));
104  }) {}
105  ~LagerStoreEventEmitter() override = default;
106 
107  void emit(KazvEvent e) override {
108  m_store.dispatch(Action{e});
109  }
110 
111  class Watchable
112  {
113  public:
115  : m_listener(std::make_shared<Listener>()) {
116  ee.addListener(m_listener);
117  }
118  template<class EventType, class Func>
119  void after(Func &&func) {
120  m_listener->connect(
121  [f=std::forward<Func>(func)](KazvEvent e) {
122  if (std::holds_alternative<EventType>(e)) {
123  f(std::get<EventType>(e));
124  }
125  });
126  }
127 
128  template<class Func>
129  void afterAll(Func &&func) {
130  m_listener->connect(
131  [f=std::forward<Func>(func)](KazvEvent e) {
132  f(e);
133  });
134  }
135 
136  private:
137  ListenerSP m_listener;
138  };
139 
149  return Watchable(*this);
150  }
151 
152 
153  private:
154  void addListener(ListenerSP listener) {
155  m_postingFunc([=]() {
156  m_holder.m_listeners.push_back(listener);
157  });
158  }
159 
160  using StoreT =
161  decltype(lager::make_store<Action>(
162  Model{},
163  lager::with_manual_event_loop{},
164  lager::with_reducer(&update),
165  lager::with_deps(std::ref(detail::declref<ListenerHolder>()))));
166 
167  using PostingFunc = std::function<void(std::function<void()>)>;
168 
169  ListenerHolder m_holder;
170  StoreT m_store;
171  PostingFunc m_postingFunc;
172  };
173 
174 }
Definition: eventinterface.hpp:15
Definition: lagerstoreeventemitter.hpp:112
void afterAll(Func &&func)
Definition: lagerstoreeventemitter.hpp:129
Watchable(LagerStoreEventEmitter &ee)
Definition: lagerstoreeventemitter.hpp:114
void after(Func &&func)
Definition: lagerstoreeventemitter.hpp:119
Definition: lagerstoreeventemitter.hpp:26
Watchable watchable()
An object you can watch for events.
Definition: lagerstoreeventemitter.hpp:148
void emit(KazvEvent e) override
Definition: lagerstoreeventemitter.hpp:107
~LagerStoreEventEmitter() override=default
LagerStoreEventEmitter(EventLoop loop)
Definition: lagerstoreeventemitter.hpp:93
Definition: location.hpp:10
std::variant< std::monostate, ReceivingPresenceEvent, ReceivingAccountDataEvent, ReceivingRoomTimelineEvent, ReceivingRoomStateEvent, RoomMembershipChanged, ReceivingRoomAccountDataEvent, ReceivingToDeviceMessage, LoginSuccessful, LoginFailed, SyncSuccessful, SyncFailed, PostInitialFiltersSuccessful, PostInitialFiltersFailed, PaginateSuccessful, PaginateFailed, CreateRoomSuccessful, CreateRoomFailed, InviteUserSuccessful, InviteUserFailed, JoinRoomSuccessful, JoinRoomFailed, LeaveRoomSuccessful, LeaveRoomFailed, ForgetRoomSuccessful, ForgetRoomFailed, SendMessageSuccessful, SendMessageFailed, SendToDeviceMessageSuccessful, SendToDeviceMessageFailed, InvalidMessageFormat, GetRoomStatesSuccessful, GetRoomStatesFailed, GetStateEventSuccessful, GetStateEventFailed, SendStateEventSuccessful, SendStateEventFailed, SetTypingSuccessful, SetTypingFailed, PostReceiptSuccessful, PostReceiptFailed, SetReadMarkerSuccessful, SetReadMarkerFailed, UploadContentSuccessful, UploadContentFailed, DownloadContentSuccessful, DownloadContentFailed, DownloadThumbnailSuccessful, DownloadThumbnailFailed, UploadIdentityKeysSuccessful, UploadIdentityKeysFailed, UploadOneTimeKeysSuccessful, UploadOneTimeKeysFailed, ClaimKeysSuccessful, ClaimKeysFailed, UnrecognizedResponse, ShouldQueryKeys > KazvEvent
Definition: kazvevents.hpp:390
Definition: clientutil.hpp:217