New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

react-native-meteor

Package Overview
Dependencies
Maintainers
1
Versions
69
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-native-meteor - npm Package Compare versions

Comparing version 1.0.0-beta3 to 1.0.0-beta30

docs/FSCollection.md

21

docs/Install.md

@@ -0,6 +1,16 @@

# Android
Add this to your AndroidManifest.xml file to autoreconnect fastly to DDP server if your device reconnects to network
```xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
```
# Installing decorators
## With RN >= 0.20.0 (Babel 6)
## With RN >= 0.16.0 (Babel 6)
- `npm i --save-dev babel-plugin-transform-decorators-legacy babel-preset-react-native` in your projet
- `npm i --save-dev babel-plugin-transform-decorators-legacy babel-preset-react-native` in your project
- Create a .babelrc file at the root of your project :

@@ -15,10 +25,5 @@

## With RN >= 0.16.0 && <= 0.19.0 (Babel 6)
Looking for your help. The RN 0.20.0 solution might be working, please let me know ;)
## With RN <0.16.0 (Babel 5)
Use a .babelrc file at the root of your projet that contains :
Use a .babelrc file at the root of your project that contains :

@@ -25,0 +30,0 @@ ```json

{
"name": "react-native-meteor",
"version": "1.0.0-beta3",
"description": "DDP React-native Client",
"version": "1.0.0-beta30",
"description": "Full Meteor Client for React Native",
"main": "src/Meteor.js",

@@ -29,8 +29,10 @@ "scripts": {

"dependencies": {
"ddp.js": "1.1.0",
"base-64": "^0.1.0",
"crypto-js": "^3.1.6",
"ejson": "^2.1.2",
"minimongo-cache": "0.0.48",
"react-mixin": "^3.0.3",
"trackr": "^2.0.2"
"trackr": "^2.0.2",
"wolfy87-eventemitter": "^4.3.0"
}
}

@@ -1,17 +0,5 @@

[![GitHub version](https://badge.fury.io/gh/inProgress-team%2Freact-native-meteor.svg)](https://badge.fury.io/gh/inProgress-team%2Freact-native-meteor)
[![npm version](https://badge.fury.io/js/react-native-meteor.svg)](http://badge.fury.io/js/react-native-meteor)
[![Dependency Status](https://david-dm.org/inProgress-team/react-native-meteor.svg)](https://david-dm.org/inProgress-team/react-native-meteor)
[![devDependency Status](https://david-dm.org/inProgress-team/react-native-meteor/dev-status.svg)](https://david-dm.org/inProgress-team/react-native-meteor#info=devDependencies)
[![MIT][license-badge]][license]
[![bitHound Score][bithound-badge]][bithound]
# react-native-meteor [![react-native-meteor](http://img.shields.io/npm/dm/react-native-meteor.svg)](https://www.npmjs.org/package/react-native-meteor) [![npm version](https://badge.fury.io/js/react-native-meteor.svg)](http://badge.fury.io/js/react-native-meteor) [![Dependency Status](https://david-dm.org/inProgress-team/react-native-meteor.svg)](https://david-dm.org/inProgress-team/react-native-meteor)
[bithound-badge]: https://www.bithound.io/github/inProgress-Team/react-native-meteor/badges/score.svg
[bithound]: https://www.bithound.io/github/inProgress-Team/react-native-meteor
[license-badge]: https://img.shields.io/dub/l/vibe-d.svg
[license]: https://github.com/inProgress-team/react-native-meteor/blob/master/LICENSE
Meteor-like methods for React Native. ! For old docs, see [v0.6.2 documentation](https://github.com/inProgress-team/react-native-meteor/tree/0.6.2) (classic ddp interface).
# react-native-meteor
Meteor-like methods for React Native. **Currently in v1.0.0-beta3** ! For old docs, see [v0.6.2 documentation](https://github.com/inProgress-team/react-native-meteor/tree/0.6.2) (classic ddp interface).
## What is it for ?

@@ -26,3 +14,3 @@

npm i --save react-native-meteor
npm i --save react-native-meteor@latest

@@ -36,3 +24,3 @@ [!! See detailed installation guide](https://github.com/inProgress-team/react-native-meteor/blob/master/docs/Install.md)

import { View, Text, Component } from 'react-native';
import Meteor, { connectMeteor } from 'react-native-meteor';
import Meteor, { connectMeteor, MeteorListView } from 'react-native-meteor';

@@ -57,21 +45,31 @@ /*

}
startMeteorSubscriptions() {
Meteor.subscribe('todos');
}
getMeteorData() {
const handle = Meteor.subscribe('todos');
Meteor.subscribe('settings');
return {
todos: Meteor.collection('todos').find()
todosReady: handle.ready(),
settings: Meteor.collection('settings').findOne()
};
}
renderRow(todo) {
return (
<Text>{todo.title}</Text>
);
}
render() {
const { todos } = this.data;
const { settings, todosReady } = this.data;
{todos.map(todo=>{
return (
<View key={todo._id}>
<Text>{todo.title}</Text>
</View>
)
})}
<View>
<Text>{settings.title}</Text>
{!todosReady && <Text>Not ready</Text>}
<MeteorListView
collection="todos"
selector={{done: true}}
options={{sort: {createdAt: -1}}}
renderRow={this.renderRow}
/>
</View>
}

@@ -81,39 +79,18 @@ }

# connectMeteor
## startMeteorSubscriptions
## getMeteorData
Inside this method, you can create subscriptions (see below) when component is mounted. It will automatically unsubscribe if the component is unmounted.
#### [Meteor.subscribe](http://docs.meteor.com/#/full/meteor_subscribe)
##### Example usage
* [Meteor.subscribe](http://docs.meteor.com/#/full/meteor_subscribe) : returns an handle
your server side:
```javascript
Meteor.publish('todos', function(selector, options){
var selector = selector || {};
var options = options || {};
return Todos.find(selector, options);
});
```
Inside getMeteorData, you can also access any Meteor reactive data source, which means :
your react-native client code:
```javascript
//Meteor subscribe can be used like on meteor official site
Meteor.subscribe('todos', {status: 'done'}, {limit: 10, sort: {createdAt: -1}});
```
##### NOTE
- Meteor subscribe parameter still not supporting EJSON, so you can't pass param value like date, boolean etc. For now it's only support object of string (JSON)
- Meteor subscribe parameter already support Publish Composite. If you are using this package, related published collections will be available too in subscriptions.
## getMeteorData
Inside getMeteorData, you can access any Meteor reactive data source, which means :
* Meteor.subscribe handle
* Meteor.collection(collectionName)
* [.find(selector, options)](http://docs.meteor.com/#/full/find)
* [.findOne(selector, options)](http://docs.meteor.com/#/full/findone)
* [.findOne(id)](http://docs.meteor.com/#/full/findone)
* [Meteor.user()](http://docs.meteor.com/#/full/meteor_user)

@@ -124,6 +101,52 @@ * [Meteor.userId()](http://docs.meteor.com/#/full/meteor_userid)

# Additionals collection methods
These methods (except update) work offline. That means that elements are correctly updated offline, and when you reconnect to ddp, Meteor calls are taken care of.
* Meteor.collection(collectionName)
* [.insert(doc, callback)](http://docs.meteor.com/#/full/insert)
* [.update(id, modifier, [options], [callback])](http://docs.meteor.com/#/full/update)
* [.remove(id, callback(err, countRemoved))](http://docs.meteor.com/#/full/remove)
# MeteorListView Component
Same as [ListView](https://facebook.github.io/react-native/docs/listview.html) Component but does not need dataSource and accepts three arguments :
- `collection` **string** *required*
- `selector` [**string** / **object**]
- `options` **object**
### Example usage
```javascript
<MeteorListView
collection="todos"
selector={{done: true}}
options={{sort: {createdAt: -1}}}
renderRow={this.renderItem}
/>
```
# MeteorComplexListView Component
Same as [ListView](https://facebook.github.io/react-native/docs/listview.html) Component but does not need dataSource and accepts one argument. You may need it if you make complex requests combining multiples collections.
- `elements` **function** *required* : a reactive function which returns an array of elements.
### Example usage
```javascript
<MeteorComplexListView
elements={()=>{return Meteor.collection('todos').find()}}
renderRow={this.renderItem}
/>
```
# API
## Meteor.connect(endpoint)
## Meteor DDP connection
#### Meteor.connect(endpoint, options)
Connect to a DDP server. You only have to do this once in your app.

@@ -134,17 +157,58 @@

- `url` **string** *required*
- `options` **object** Available options are :
- autoConnect **boolean** [true] whether to establish the connection to the server upon instantiation. When false, one can manually establish the connection with the Meteor.ddp.connect method.
- autoReconnect **boolean** [true] whether to try to reconnect to the server when the socket connection closes, unless the closing was initiated by a call to the disconnect method.
- reconnectInterval **number** [10000] the interval in ms between reconnection attempts.
#### Meteor.disconnect()
Disconnect from the DDP server.
## Meteor methods
* [Meteor.call](http://docs.meteor.com/#/full/meteor_call)
* [Meteor.loginWithPassword](http://docs.meteor.com/#/full/meteor_loginwithpassword) (Please note that user is auto-resigned in - like in Meteor Web applications - thanks to React Native AsyncStorage.)
* [Meteor.logout](http://docs.meteor.com/#/full/meteor_logout)
* [Meteor.call](http://docs.meteor.com/#/full/meteor_call)
* [Meteor.logoutOtherClients](http://docs.meteor.com/#/full/meteor_logoutotherclients)
##### NOTE
Meteor call parameter still not supporting EJSON, so you can't pass param value like date, boolean etc. For now it's only support object of string (JSON)
## Meteor.Accounts
* [Accounts.createUser](http://docs.meteor.com/#/full/accounts_createuser)
* [Accounts.changePassword](http://docs.meteor.com/#/full/accounts_forgotpassword)
* [Accounts.forgotPassword](http://docs.meteor.com/#/full/accounts_changepassword)
* [Accounts.onLogin](http://docs.meteor.com/#/full/accounts_onlogin)
* [Accounts.onLoginFailure](http://docs.meteor.com/#/full/accounts_onloginfailure)
## Meteor.ddp
Once connected to the ddp server, you can access every method available in [ddp.js](https://github.com/mondora/ddp.js/).
* Meteor.ddp.on('connected')
* Meteor.ddp.on('added')
* Meteor.ddp.on('changed')
* ...
## CollectionFS
* Meteor.FSCollection(collectionName) : Helper for [Meteor-CollectionFS](https://github.com/CollectionFS/Meteor-CollectionFS). Full documentation [here](https://github.com/inProgress-team/react-native-meteor/blob/master/docs/FSCollection.md)
* This plugin also exposes a FSCollectionImagesPreloader component which helps you preload every image you want in CollectionFS (only available on ios)
```javascript
import { FSCollectionImagesPreloader } from 'react-native-meteor';
<FSCollectionImagesPreloader
collection="imagesFiles"
selector={{metadata.owner: XXX}}
/>
```
## react-native-router-flux
* [Github repository](https://github.com/inProgress-team/react-native-meteor-router-flux)
* npm i --save react-native-meteor-router-flux@latest
* [Custom scene renderer](https://github.com/aksonov/react-native-router-flux#switch-new-feature) which allows to select tab scene to show depending from app state. It could be useful for authentication, restricted scenes, etc.
# Want to help ?
Pull Requests and issues reported are welcome ! :)

@@ -0,10 +1,22 @@

import React from 'react-native';
import minimongo from 'minimongo-cache';
process.nextTick = setImmediate;
const db = new minimongo();
db.debug = false;
db.batchedUpdates = React.addons.batchedUpdates;
export default {
_endpoint: null,
_options: null,
ddp: null,
subscriptions: {},
db: new minimongo(),
db: db,
calls: [],
hasBeenConnected: false,
getUrl() {
return this._endpoint.substring(0, this._endpoint.indexOf('/websocket'));
},
waitDdpReady(cb) {

@@ -20,14 +32,31 @@ if(this.ddp) {

_cbsLoggingIn: [],
_subscribeLoggingIn(cb) {
this._cbsLoggingIn.push(cb);
_cbs: [],
onChange(cb) {
this.db.on('change', cb);
this.ddp.on('connected', cb);
this.ddp.on('disconnected', cb);
this.on('loggingIn', cb);
},
_unsubscribeLoggingIn(cb) {
this._cbsLoggingIn.splice(this._cbsLoggingIn.indexOf(cb));
offChange(cb) {
this.db.off('change', cb);
this.ddp.off('connected', cb);
this.ddp.off('disconnected', cb);
this.off('loggingIn', cb);
},
_notifyLoggingIn() {
for(var i in this._cbsLoggingIn) {
this._cbsLoggingIn[i]();
}
on(eventName, cb) {
this._cbs.push({
eventName: eventName,
callback: cb
});
},
off(eventName, cb) {
this._cbs.splice(this._cbs.findIndex(_cb=>_cb.callback == cb && _cb.eventName == eventName), 1);
},
notify(eventName) {
this._cbs.map(cb=>{
if(cb.eventName == eventName && typeof cb.callback == 'function') {
cb.callback();
}
});
}
}

@@ -0,29 +1,38 @@

import { NetInfo, Platform, View } from 'react-native';
import reactMixin from 'react-mixin';
import Trackr from 'trackr';
import DDP from 'ddp.js';
import EJSON from 'ejson';
import DDP from '../lib/ddp.js';
import Random from '../lib/Random';
import Data from './Data';
import Mixin from './Mixin';
import User from './User';
import collection from './Collection';
import call from './Call';
import Mixin from './components/Mixin';
import ListView from './components/ListView';
import MeteorComplexListView from './components/ComplexListView';
import FSCollection from './CollectionFS/FSCollection';
import FSCollectionImagesPreloader from './CollectionFS/FSCollectionImagesPreloader';
import User from './user/User';
import Accounts from './user/Accounts';
module.exports = {
Accounts: Accounts,
MeteorListView: ListView,
MeteorComplexListView: MeteorComplexListView,
FSCollectionImagesPreloader: Platform.OS == 'android' ? View : FSCollectionImagesPreloader,
collection: collection,
FSCollection: FSCollection,
getData() {
return Data
},
connectMeteor(reactClass) {
return reactMixin.onClass(reactClass, Mixin);
},
collection(name) {
return {
find(selector, options) {
if(!Data.db || !Data.db[name]) return [];
if(typeof selector == 'string') return this.find({_id: selector}, options);
return Data.db[name].find(selector, options)
},
findOne(selector, options) {
if(!Data.db || !Data.db[name]) return null;
if(typeof selector == 'string') return this.findOne({_id: selector}, options);
return Data.db[name] && Data.db[name].findOne(selector, options)
}
};
},
...User,

@@ -39,26 +48,66 @@ status() {

},
call(eventName) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length && typeof args[args.length - 1] === "function") {
var callback = args.pop();
call: call,
disconnect() {
if(Data.ddp) {
Data.ddp.disconnect();
}
},
_subscriptionsRestart() {
for(var i in Data.subscriptions) {
const sub = Data.subscriptions[i];
Data.ddp.unsub(sub.subIdRemember);
sub.subIdRemember = Data.ddp.sub(sub.name, sub.params);
}
const id = Data.ddp.method(eventName, args);
Data.calls.push({
id: id,
callback: callback
});
},
connect(endpoint) {
waitDdpConnected(cb) {
if(Data.ddp && Data.ddp.status == 'connected') {
cb();
} else if(Data.ddp) {
Data.ddp.once('connected', cb);
} else {
setTimeout(()=>{ this.waitDdpConnected(cb) }, 500);
}
},
connect(endpoint, options) {
if(!endpoint) endpoint = Data._endpoint;
if(!options) options = Data._options;
Data._endpoint = endpoint;
Data._options = options;
this.ddp = Data.ddp = new DDP({
endpoint: endpoint,
SocketConstructor: WebSocket
SocketConstructor: WebSocket,
...options
});
NetInfo.isConnected.addEventListener('change', isConnected=>{
if(isConnected) {
Data.ddp.connect();
}
});
Data.ddp.on("connected", ()=>{
console.info("connected");
console.info("Connected to DDP server.");
this._loadInitialUser();
if(Data.hasBeenConnected) {
this._subscriptionsRestart();
} else {
Data.hasBeenConnected = true;
}
});
Data.ddp.on("disconnected", ()=>{
console.info("Disconnected from DDP server.");
this.handleLogout();
this.connect();
});
Data.ddp.on("added", message => {

@@ -72,3 +121,10 @@ if(!Data.db[message.collection]) {

Data.ddp.on("ready", message => {
//console.info('READY', message.subs);
for(var i in Data.subscriptions) {
const sub = Data.subscriptions[i];
sub.ready = true;
sub.readyDeps.changed();
sub.readyCallback && sub.readyCallback();
}
});

@@ -93,7 +149,8 @@

const sub = Data.subscriptions[i];
if(sub.id == message.id) {
console.log("No sub for", sub.name);
if(sub.subIdRemember == message.id) {
console.warn("No subscription existing for", sub.name);
}
}
});
},

@@ -112,53 +169,117 @@ subscribe(name) {

const stringKey = name+JSON.stringify(params);
// Is there an existing sub with the same name and param, run in an
// invalidated Computation? This will happen if we are rerunning an
// existing computation.
//
// For example, consider a rerun of:
//
// Tracker.autorun(function () {
// Meteor.subscribe("foo", Session.get("foo"));
// Meteor.subscribe("bar", Session.get("bar"));
// });
//
// If "foo" has changed but "bar" has not, we will match the "bar"
// subcribe to an existing inactive subscription in order to not
// unsub and resub the subscription unnecessarily.
//
// We only look for one such sub; if there are N apparently-identical subs
// being invalidated, we will require N matching subscribe calls to keep
// them all active.
if (!Data.subscriptions[stringKey]) {
// We're not currently subscribed to this. Go start a subscription.
//console.log("Subscribe to", name, "with params", params);
Data.subscriptions[stringKey] = {
references: 1,
let existing = false;
for(var i in Data.subscriptions) {
const sub = Data.subscriptions[i];
if(sub.inactive && sub.name === name && EJSON.equals(sub.params, params)) existing = sub;
}
let id;
if (existing) {
id = existing.id;
existing.inactive = false;
if (callbacks.onReady) {
// If the sub is not already ready, replace any ready callback with the
// one provided now. (It's not really clear what users would expect for
// an onReady callback inside an autorun; the semantics we provide is
// that at the time the sub first becomes ready, we call the last
// onReady callback provided, if any.)
if (!existing.ready)
existing.readyCallback = callbacks.onReady;
}
if (callbacks.onStop) {
existing.stopCallback = callbacks.onStop;
}
} else {
// New sub! Generate an id, save it locally, and send message.
id = Random.id();
const subIdRemember = Data.ddp.sub(name, params);
Data.subscriptions[id] = {
id: id,
subIdRemember: subIdRemember,
name: name,
params: params,
id: Data.ddp.sub(name, params),
stop: function () {
// Wait for all autoruns to finish rerunning before processing the unsubscription
// request. It's possible that stop() was called because an autorun is rerunning,
// so before doing anything drastic, we should wait to see if the autorun
// recreates the subscription when it is rerun.
this.references--;
Trackr.afterFlush(() => {
if (this.references === 0) {
// Nope, either the autorun did not recreate the subscription, or there
// were multiple calls to subscribeToMagazine() in the first place and not all
// of them have been stopped. Go ahead and cancel.
//console.log("Canceling our subscription to", name, "with params", params);
Data.ddp.unsub(Data.subscriptions[stringKey].id);
delete Data.subscriptions[stringKey];
}
});
params: EJSON.clone(params),
inactive: false,
ready: false,
readyDeps: new Trackr.Dependency,
readyCallback: callbacks.onReady,
stopCallback: callbacks.onStop,
stop: function() {
Data.ddp.unsub(this.subIdRemember);
delete Data.subscriptions[this.id];
this.ready && this.readyDeps.changed();
if (callbacks.onStop) {
callbacks.onStop();
}
}
};
} else {
// We already have a subscription to this magazine running. Increment the reference
// count to stop it from going away.
Data.subscriptions[stringKey].references++;
}
// return a handle to the application.
var handle = {
stop: function () {
if(Data.subscriptions[id])
Data.subscriptions[id].stop();
},
ready: function () {
if (!Data.subscriptions[id]) return false;
var record = Data.subscriptions[id];
record.readyDeps.depend();
return record.ready;
},
subscriptionId: id
};
if (Trackr.active) {
Trackr.onInvalidate((c) => {
// subscribeToMagazine was called from inside an autorun, and the autorun is
// about to rerun itself. Tentatively plan to cancel the subscription. If the
// autorun resubscribes to that same magazine when it is rerun, the logic in
// in stop() is smart enough to leave the subscription alone rather than
// canceling and immediately recreating it.
//
// (Tracker.onInvalidate is a shortcut for Tracker.currentComputation.onInvalidate.)
if (Data.subscriptions[stringKey]) {
Data.subscriptions[stringKey].stop();
// We're in a reactive computation, so we'd like to unsubscribe when the
// computation is invalidated... but not if the rerun just re-subscribes
// to the same subscription! When a rerun happens, we use onInvalidate
// as a change to mark the subscription "inactive" so that it can
// be reused from the rerun. If it isn't reused, it's killed from
// an afterFlush.
Trackr.onInvalidate(function (c) {
if(Data.subscriptions[id]) {
Data.subscriptions[id].inactive = true;
}
Trackr.afterFlush(function () {
if (Data.subscriptions[id] && Data.subscriptions[id].inactive) {
handle.stop();
}
});
});
}
return Data.subscriptions[stringKey];
return handle;
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc