poc change notification
This commit is contained in:
parent
6d86f8e91f
commit
4d24b29550
|
@ -48,6 +48,10 @@ export default class TodoApp extends Component {
|
|||
|
||||
// This is a Results object, which will live-update.
|
||||
this.todoLists = todoLists;
|
||||
todoLists.addListener(function() {
|
||||
console.log("changed");
|
||||
});
|
||||
console.log("registered listener");
|
||||
|
||||
// Bind all the methods that we will be passing as props.
|
||||
this.renderScene = this.renderScene.bind(this);
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
029048101C0428DF00ABDED4 /* rpc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rpc.hpp; sourceTree = "<group>"; };
|
||||
029048351C042A3C00ABDED4 /* platform.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = platform.hpp; sourceTree = "<group>"; };
|
||||
029048381C042A8F00ABDED4 /* platform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platform.mm; sourceTree = "<group>"; };
|
||||
0290934A1CEFA9170009769E /* js_observable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_observable.hpp; sourceTree = "<group>"; };
|
||||
02A3C7A41BC4341500B1A7BE /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
||||
02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmJSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
|
@ -264,6 +265,7 @@
|
|||
F6874A441CAD2ACD00EEEE36 /* JSC */,
|
||||
F62BF9001CAC72C40022BCDC /* Node */,
|
||||
F62A35141C18E783004A917D /* Object Store */,
|
||||
0290934A1CEFA9170009769E /* js_observable.hpp */,
|
||||
F60102F71CBDA6D400EC01BA /* js_collection.hpp */,
|
||||
029048041C0428DF00ABDED4 /* js_list.hpp */,
|
||||
029048061C0428DF00ABDED4 /* js_realm_object.hpp */,
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "js_class.hpp"
|
||||
#include "js_observable.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
@ -27,7 +28,7 @@ namespace js {
|
|||
class Collection {};
|
||||
|
||||
template<typename T>
|
||||
struct CollectionClass : ClassDefinition<T, Collection> {
|
||||
struct CollectionClass : ClassDefinition<T, Collection, ObservableClass<T>> {
|
||||
std::string const name = "Collection";
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
|
|||
using ContextType = typename T::Context;
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
using FunctionType = typename T::Function;
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
|
@ -58,7 +59,12 @@ struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
|
|||
static void filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
||||
|
||||
|
||||
// observable
|
||||
static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
std::string const name = "List";
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
|
@ -71,6 +77,9 @@ struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
|
|||
{"filtered", wrap<filtered>},
|
||||
{"sorted", wrap<sorted>},
|
||||
{"isValid", wrap<is_valid>},
|
||||
{"addListener", wrap<add_listener>},
|
||||
{"removeListener", wrap<remove_listener>},
|
||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||
};
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
|
@ -230,6 +239,41 @@ template<typename T>
|
|||
void ListClass<T>::is_valid(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, ListClass<T>>(this_object)->is_valid());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::add_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
Protected<FunctionType> protected_callback(ctx, callback);
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
list->add_notification_callback([=](CollectionChangeSet change_set, std::exception_ptr exception) {
|
||||
ValueType arguments[2];
|
||||
arguments[0] = protected_this;
|
||||
arguments[1] = Value::from_undefined(protected_ctx);
|
||||
Function<T>::call(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
}, (size_t)(ValueType)callback);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::remove_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
list->remove_notification_callback((size_t)(ValueType)callback);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
list->remove_all_notification_callbacks();
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "js_class.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
// Empty class that merely serves as useful type for now.
|
||||
class Observable {};
|
||||
|
||||
template<typename T>
|
||||
struct ObservableClass : ClassDefinition<T, Observable> {
|
||||
std::string const name = "Observable";
|
||||
};
|
||||
|
||||
} // js
|
||||
} // realm
|
|
@ -28,6 +28,7 @@
|
|||
#include "js_list.hpp"
|
||||
#include "js_results.hpp"
|
||||
#include "js_schema.hpp"
|
||||
#include "js_observable.hpp"
|
||||
|
||||
#include "shared_realm.hpp"
|
||||
#include "binding_context.hpp"
|
||||
|
@ -124,7 +125,7 @@ void set_default_path(std::string path);
|
|||
void delete_all_realms();
|
||||
|
||||
template<typename T>
|
||||
class RealmClass : public ClassDefinition<T, SharedRealm> {
|
||||
class RealmClass : public ClassDefinition<T, SharedRealm, ObservableClass<T>> {
|
||||
using GlobalContextType = typename T::GlobalContext;
|
||||
using ContextType = typename T::Context;
|
||||
using FunctionType = typename T::Function;
|
||||
|
|
|
@ -34,6 +34,7 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
|
|||
using ContextType = typename T::Context;
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
using FunctionType = typename T::Function;
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
|
@ -57,6 +58,11 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
|
|||
static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
||||
|
||||
// observable
|
||||
static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
std::string const name = "Results";
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
|
@ -64,6 +70,9 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
|
|||
{"filtered", wrap<filtered>},
|
||||
{"sorted", wrap<sorted>},
|
||||
{"isValid", wrap<is_valid>},
|
||||
{"addListener", wrap<add_listener>},
|
||||
{"removeListener", wrap<remove_listener>},
|
||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||
};
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
|
@ -239,6 +248,41 @@ template<typename T>
|
|||
void ResultsClass<T>::is_valid(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, ResultsClass<T>>(this_object)->is_valid());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::add_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
auto list = get_internal<T, ResultsClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
Protected<FunctionType> protected_callback(ctx, callback);
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
list->add_notification_callback([=](CollectionChangeSet change_set, std::exception_ptr exception) {
|
||||
ValueType arguments[2];
|
||||
arguments[0] = protected_this;
|
||||
arguments[1] = Value::from_undefined(protected_ctx);
|
||||
Function<T>::call(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
}, (size_t)(ValueType)callback);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::remove_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
results->remove_notification_callback((size_t)(ValueType)callback);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
results->remove_all_notification_callbacks();
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
|
|
@ -165,22 +165,19 @@ CollectionNotifier::~CollectionNotifier()
|
|||
unregister();
|
||||
}
|
||||
|
||||
size_t CollectionNotifier::add_callback(CollectionChangeCallback callback)
|
||||
size_t CollectionNotifier::add_callback(CollectionChangeCallback callback, size_t token)
|
||||
{
|
||||
m_realm->verify_thread();
|
||||
|
||||
auto next_token = [=] {
|
||||
size_t token = 0;
|
||||
std::lock_guard<std::mutex> lock(m_callback_mutex);
|
||||
if (token == 0) {
|
||||
for (auto& callback : m_callbacks) {
|
||||
if (token <= callback.token) {
|
||||
token = callback.token + 1;
|
||||
}
|
||||
}
|
||||
return token;
|
||||
};
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_callback_mutex);
|
||||
auto token = next_token();
|
||||
m_callbacks.push_back({std::move(callback), token, false});
|
||||
if (m_callback_index == npos) { // Don't need to wake up if we're already sending notifications
|
||||
Realm::Internal::get_coordinator(*m_realm).send_commit_notifications();
|
||||
|
@ -216,6 +213,12 @@ void CollectionNotifier::remove_callback(size_t token)
|
|||
}
|
||||
}
|
||||
|
||||
void CollectionNotifier::remove_all_callbacks()
|
||||
{
|
||||
m_callbacks.clear();
|
||||
m_have_callbacks = false;
|
||||
}
|
||||
|
||||
void CollectionNotifier::unregister() noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_realm_mutex);
|
||||
|
|
|
@ -109,12 +109,13 @@ public:
|
|||
// Add a callback to be called each time the collection changes
|
||||
// This can only be called from the target collection's thread
|
||||
// Returns a token which can be passed to remove_callback()
|
||||
size_t add_callback(CollectionChangeCallback callback);
|
||||
size_t add_callback(CollectionChangeCallback callback, size_t token = 0);
|
||||
// Remove a previously added token. The token is no longer valid after
|
||||
// calling this function and must not be used again. This function can be
|
||||
// called from any thread.
|
||||
void remove_callback(size_t token);
|
||||
|
||||
void remove_all_callbacks();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// API for RealmCoordinator to manage running things and calling callbacks
|
||||
|
||||
|
|
|
@ -184,12 +184,27 @@ size_t hash<realm::List>::operator()(realm::List const& list) const
|
|||
}
|
||||
}
|
||||
|
||||
NotificationToken List::add_notification_callback(CollectionChangeCallback cb)
|
||||
size_t List::add_notification_callback(CollectionChangeCallback cb, size_t token)
|
||||
{
|
||||
verify_attached();
|
||||
if (!m_notifier) {
|
||||
m_notifier = std::make_shared<ListNotifier>(m_link_view, m_realm);
|
||||
RealmCoordinator::register_notifier(m_notifier);
|
||||
}
|
||||
return {m_notifier, m_notifier->add_callback(std::move(cb))};
|
||||
return m_notifier->add_callback(std::move(cb), token);
|
||||
}
|
||||
|
||||
NotificationToken List::add_notification_callback(CollectionChangeCallback cb)
|
||||
{
|
||||
return {m_notifier, add_notification_callback(cb, 0)};
|
||||
}
|
||||
|
||||
void List::remove_notification_callback(size_t token)
|
||||
{
|
||||
m_notifier->remove_callback(token);
|
||||
}
|
||||
|
||||
void List::remove_all_notification_callbacks()
|
||||
{
|
||||
m_notifier->remove_all_callbacks();
|
||||
}
|
||||
|
|
|
@ -74,7 +74,10 @@ public:
|
|||
bool operator==(List const& rgt) const noexcept;
|
||||
|
||||
NotificationToken add_notification_callback(CollectionChangeCallback cb);
|
||||
|
||||
size_t add_notification_callback(CollectionChangeCallback cb, size_t token);
|
||||
void remove_notification_callback(size_t token);
|
||||
void remove_all_notification_callbacks();
|
||||
|
||||
// These are implemented in object_accessor.hpp
|
||||
template <typename ValueType, typename ContextType>
|
||||
void add(ContextType ctx, ValueType value);
|
||||
|
|
|
@ -497,6 +497,22 @@ NotificationToken Results::add_notification_callback(CollectionChangeCallback cb
|
|||
return {m_notifier, m_notifier->add_callback(std::move(cb))};
|
||||
}
|
||||
|
||||
void Results::add_notification_callback(CollectionChangeCallback cb, size_t token)
|
||||
{
|
||||
prepare_async();
|
||||
m_notifier->add_callback(std::move(cb), token);
|
||||
}
|
||||
|
||||
void Results::remove_notification_callback(size_t token)
|
||||
{
|
||||
m_notifier->remove_callback(token);
|
||||
}
|
||||
|
||||
void Results::remove_all_notification_callbacks()
|
||||
{
|
||||
m_notifier->remove_all_callbacks();
|
||||
}
|
||||
|
||||
bool Results::is_in_table_order() const
|
||||
{
|
||||
switch (m_mode) {
|
||||
|
|
|
@ -180,7 +180,10 @@ public:
|
|||
// and then rerun after each commit (if needed) and redelivered if it changed
|
||||
NotificationToken async(std::function<void (std::exception_ptr)> target);
|
||||
NotificationToken add_notification_callback(CollectionChangeCallback cb);
|
||||
|
||||
void add_notification_callback(CollectionChangeCallback cb, size_t token);
|
||||
void remove_notification_callback(size_t token);
|
||||
void remove_all_notification_callbacks();
|
||||
|
||||
bool wants_background_updates() const { return m_wants_background_updates; }
|
||||
|
||||
// Returns whether the rows are guaranteed to be in table order.
|
||||
|
|
Loading…
Reference in New Issue