Skip to content

Commit 6f9d1b2

Browse files
committed
feat(Query): LiveQuery
1 parent f41bb57 commit 6f9d1b2

File tree

19 files changed

+255
-123
lines changed

19 files changed

+255
-123
lines changed

demo/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ <h2><a href="https://leancloud.cn">为开发加速</a></h2>
1414
<input type="text" id='code'/>
1515
<button id="verify">verify</button>
1616
</div>
17-
<script src="../dist/av.js"></script>
17+
<script src="../dist/av-live-query.js"></script>
1818
<script src="./test.js"></script>
1919
</body>
2020
</html>

demo/test.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
var av = void 0;
1+
var AV;
22

33
// 检测是否在 Nodejs 环境下运行
44
if (typeof process !== 'undefined' && process.versions && process.versions.node) {
5-
av = require('../dist/node/av');
5+
AV = require('../live-query');
66
} else {
7-
av = window.AV;
7+
AV = window.AV;
88
}
99

1010
// 初始化
@@ -16,7 +16,7 @@ var region = 'cn';
1616
// const appKey = 'be2YmUduiuEnCB2VR9bLRnnV';
1717
// const region = 'us';
1818

19-
av.init({ appId: appId, appKey: appKey, region: region });
19+
AV.init({ appId: appId, appKey: appKey, region: region });
2020

2121
av.Captcha.request().then(captcha => {
2222
captcha.bind({
@@ -28,3 +28,9 @@ av.Captcha.request().then(captcha => {
2828
error: console.error,
2929
});
3030
});
31+
new AV.Query('Todo').subscribe().then(subscription => {
32+
subscription.on('create', console.log);
33+
subscription.on('update', console.log);
34+
subscription.on('delete', console.log);
35+
// subscription.unsubscribe();
36+
});

gulpfile.babel.js

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -35,59 +35,37 @@ const uploadCDN = (file) => {
3535
return file;
3636
};
3737

38-
gulp.task('clean-dist', () => {
38+
gulp.task('clean-dist', () =>
3939
gulp.src([
40-
'dist/*.js',
41-
'dist/*.map'
40+
'dist/**/*.*',
4241
]).pipe(clean({
43-
force: true}
44-
));
45-
});
42+
force: true
43+
}))
44+
);
4645

4746
// 编译浏览器版本
48-
gulp.task('bundle-browser', shell.task('npm run build:browser'));
49-
gulp.task('bundle-rn', shell.task('npm run build:rn'));
50-
gulp.task('bundle-weapp', shell.task('npm run build:weapp'));
51-
52-
gulp.task('uglify', ['bundle-browser', 'bundle-weapp'], shell.task([
53-
'npm run uglify:browser',
54-
'npm run uglify:weapp',
55-
]));
56-
57-
gulp.task('clean-node', () => {
58-
return gulp.src(['dist/node/**/*.*'])
59-
.pipe(clean({force: true}));
60-
});
47+
gulp.task('bundle-browser', ['clean-dist'], shell.task('npm run build:browser'));
48+
gulp.task('bundle-rn', ['clean-dist'], shell.task('npm run build:rn'));
49+
gulp.task('bundle-weapp', ['clean-dist'], shell.task('npm run build:weapp'));
6150

6251
// 编译出 Node 版本
63-
gulp.task('babel-node', ['clean-node'], () => {
52+
gulp.task('babel-node', ['clean-dist'], () => {
6453
return gulp.src('src/**/*.js')
6554
.pipe(babel())
6655
.pipe(gulp.dest('dist/node/'));
6756
});
6857

69-
gulp.task('clean-demo', () => {
70-
return gulp.src(['demo/test-es5.js'])
71-
.pipe(clean());
72-
});
73-
74-
// 编译 Demo 中的代码
75-
gulp.task('babel-demo', ['clean-demo'], () => {
76-
return gulp.src('demo/*.js')
77-
// .pipe(sourcemaps.init())
78-
.pipe(babel())
79-
.pipe(concat('test-es5.js'))
80-
// .pipe(sourcemaps.write("."))
81-
.pipe(gulp.dest('demo/'));
82-
});
83-
8458
// 上传到 CDN
8559
gulp.task('upload', () => {
8660
[
8761
'./dist/av-min.js',
8862
'./dist/av-weapp-min.js',
8963
'./dist/av.js',
9064
'./dist/av-weapp.js',
65+
'./dist/av-live-query-min.js',
66+
'./dist/av-live-query-weapp-min.js',
67+
'./dist/av-live-query.js',
68+
'./dist/av-live-query-weapp.js',
9169
].map(uploadCDN).map(file => `${file}.map`).map(uploadCDN);
9270
});
9371

@@ -97,7 +75,5 @@ gulp.task('build', [
9775
'bundle-browser',
9876
'bundle-rn',
9977
'bundle-weapp',
100-
'uglify',
101-
'clean-node',
10278
'babel-node'
10379
]);

live-query.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./dist/node/index-live-query');

package.json

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@
1414
"build:browser": "CLIENT_PLATFORM=Browser webpack --config webpack/browser.js",
1515
"build:rn": "CLIENT_PLATFORM=ReactNative webpack --config webpack/rn.js",
1616
"build:weapp": "CLIENT_PLATFORM=Weapp webpack --config webpack/weapp.js",
17-
"uglify:browser": "cd dist; uglifyjs av.js -m -c -o av-min.js --in-source-map av.js.map --source-map av-min.js.map; cd ..;",
18-
"uglify:weapp": "cd dist; uglifyjs av-weapp.js -m -c -o av-weapp-min.js --in-source-map av-weapp.js.map --source-map av-weapp-min.js.map; cd ..;",
19-
"build": "gulp build",
17+
"build": "gulp build && npm run build:live-query",
18+
"build:live-query": "export LIVE_QUERY=1 && npm run build:browser && npm run build:rn && npm run build:weapp",
2019
"prepublishOnly": "./script/check-version.js"
2120
},
2221
"dependencies": {
2322
"debug": "^2.2.0",
2423
"es6-promise": "^4.0.5",
24+
"eventemitter3": "^2.0.3",
25+
"leancloud-realtime": "^3.5.0-beta.0",
26+
"leancloud-realtime-plugin-live-query": "^1.0.0-beta.1",
2527
"localstorage-memory": "^1.0.1",
2628
"md5": "^2.0.0",
2729
"qiniu": "^6.1.11",
@@ -30,7 +32,7 @@
3032
},
3133
"devDependencies": {
3234
"babel-core": "^6.4.0",
33-
"babel-loader": "^6.2.8",
35+
"babel-loader": "^7.0.0",
3436
"babel-plugin-istanbul": "^2.0.0",
3537
"babel-preset-es2015": "^6.3.13",
3638
"babel-register": "^6.14.0",
@@ -49,7 +51,6 @@
4951
"mocha": "^3.0.0",
5052
"nyc": "^8.1.0",
5153
"should": "^11.1.0",
52-
"uglify-js": "git+https://github.com/Swaagie/UglifyJS2.git#fcb4f2f21584dc5b21af4c10e17733e1686135e4",
5354
"weapp-polyfill": "^1.1.0",
5455
"webpack": "^2.2.0-rc.3"
5556
},
@@ -63,11 +64,18 @@
6364
"./src/utils/localstorage.js": "./src/utils/localstorage-browser.js",
6465
"./src/utils/parse-base64.js": "./src/utils/parse-base64-browser.js",
6566
"./src/ua/comments.js": "./src/ua/comments-browser.js",
66-
"./dist/node/index.js": "./dist/av.js"
67+
"./dist/node/index.js": "./dist/av.js",
68+
"./dist/node/index-live-query.js": "./dist/av-live-query.js"
6769
},
6870
"react-native": {
6971
"./src/utils/localstorage.js": "./src/utils/localstorage-rn.js",
70-
"./dist/node/index.js": "./dist/av-rn.js"
72+
"./dist/node/index.js": "./dist/av-rn.js",
73+
"./dist/node/index-live-query.js": "./dist/av-live-query-rn.js"
74+
},
75+
"weapp": {
76+
"./src/polyfills/index.js": "./src/polyfills/index-weapp.js",
77+
"./dist/node/index.js": "./dist/av-weapp.js",
78+
"./dist/node/index-live-query.js": "./dist/av-live-query-weapp.js"
7179
},
7280
"typings": "./storage.d.ts",
7381
"types": "./storage.d.ts",

src/av.js

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@ const AV = global.AV || {};
99

1010
// All internal configuration items
1111
AV._config = {
12-
userAgent,
1312
serverURLs: {},
1413
useMasterKey: false,
1514
production: null,
15+
realtime: null,
16+
};
17+
18+
// configs shared by all AV instances
19+
AV._sharedConfig = {
20+
userAgent,
21+
liveQueryRealtime: null,
1622
};
1723

1824
/**
@@ -43,38 +49,54 @@ AV._getAVPath = function(path) {
4349
return "AV/" + AV.applicationId + "/" + path;
4450
};
4551

52+
const hexOctet = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
53+
const uuid = () => `${hexOctet()}${hexOctet()}-${hexOctet()}-${hexOctet()}-${hexOctet()}-${hexOctet()}${hexOctet()}${hexOctet()}`;
54+
4655
/**
4756
* Returns the unique string for this app on this machine.
4857
* Gets reset when localStorage is cleared.
4958
* @private
5059
*/
5160
AV._installationId = null;
52-
AV._getInstallationId = function() {
61+
AV._getInstallationId = () => {
5362
// See if it's cached in RAM.
5463
if (AV._installationId) {
5564
return AV.Promise.resolve(AV._installationId);
5665
}
5766

5867
// Try to get it from localStorage.
59-
var path = AV._getAVPath("installationId");
60-
return AV.localStorage.getItemAsync(path).then(function(_installationId){
68+
const path = AV._getAVPath('installationId');
69+
return AV.localStorage.getItemAsync(path).then((_installationId) => {
6170
AV._installationId = _installationId;
6271
if (!AV._installationId) {
6372
// It wasn't in localStorage, so create a new one.
64-
var hexOctet = function() {
65-
return Math.floor((1+Math.random())*0x10000).toString(16).substring(1);
66-
};
67-
AV._installationId = (
68-
hexOctet() + hexOctet() + "-" +
69-
hexOctet() + "-" +
70-
hexOctet() + "-" +
71-
hexOctet() + "-" +
72-
hexOctet() + hexOctet() + hexOctet());
73-
return AV.localStorage.setItemAsync(path, AV._installationId);
73+
AV._installationId = _installationId = uuid();
74+
return AV.localStorage.setItemAsync(path, _installationId).then(() => _installationId);
7475
}
75-
else {
76-
return _installationId;
76+
return _installationId;
77+
});
78+
};
79+
80+
AV._subscriptionId = null;
81+
AV._refreshSubscriptionId = (path = AV._getAVPath('subscriptionId')) => {
82+
const subscriptionId = AV._subscriptionId = uuid();
83+
return AV.localStorage.setItemAsync(path, subscriptionId).then(() => subscriptionId);
84+
};
85+
AV._getSubscriptionId = () => {
86+
// See if it's cached in RAM.
87+
if (AV._subscriptionId) {
88+
return AV.Promise.resolve(AV._subscriptionId);
89+
}
90+
91+
// Try to get it from localStorage.
92+
const path = AV._getAVPath('subscriptionId');
93+
return AV.localStorage.getItemAsync(path).then((_subscriptionId) => {
94+
AV._subscriptionId = _subscriptionId;
95+
if (!AV._subscriptionId) {
96+
// It wasn't in localStorage, so create a new one.
97+
_subscriptionId = AV._refreshSubscriptionId(path);
7798
}
99+
return _subscriptionId;
78100
});
79101
};
80102

src/index-live-query.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const AV = require('./index');
2+
3+
const { Realtime } = require('leancloud-realtime/core');
4+
const { LiveQueryPlugin } = require('leancloud-realtime-plugin-live-query');
5+
Realtime.__preRegisteredPlugins = [LiveQueryPlugin];
6+
AV._sharedConfig.liveQueryRealtime = Realtime;
7+
8+
module.exports = AV;

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Copyright 2016 LeanCloud.cn, Inc.
66
* The LeanCloud JavaScript SDK is freely distributable under the MIT license.
77
*/
8+
require('./polyfills');
89

910
const AV = require('./av');
1011

@@ -26,6 +27,7 @@ require('./object')(AV);
2627
require('./role')(AV);
2728
require('./user')(AV);
2829
require('./query')(AV);
30+
require('./live-query')(AV);
2931
require('./captcha')(AV);
3032
require('./cloudfunction')(AV);
3133
require('./push')(AV);

src/init.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ AV.init = function init(options, ...params) {
7878
serverURLs,
7979
disableCurrentUser = false,
8080
production,
81+
realtime,
8182
} = options;
8283
if (AV.applicationId) throw new Error('SDK is already initialized.');
8384
if (process.env.CLIENT_PLATFORM && masterKey) console.warn('MasterKey is not supposed to be used in browser.');
@@ -95,6 +96,14 @@ AV.init = function init(options, ...params) {
9596
AV._config.serverURLs,
9697
serverURLs
9798
), disableAppRouter);
99+
if (realtime) {
100+
AV._config.realtime = realtime;
101+
} else if (AV._sharedConfig.liveQueryRealtime) {
102+
AV._config.realtime = new AV._sharedConfig.liveQueryRealtime({
103+
appId,
104+
region,
105+
});
106+
}
98107
};
99108

100109
// If we're running in node.js, allow using the master key.

src/live-query.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
const EventEmitter = require('eventemitter3');
2+
const Promise = require('./promise');
3+
const { inherits } = require('./utils');
4+
const { request } = require('./request');
5+
6+
module.exports = (AV) => {
7+
const LiveQuery = AV.LiveQuery = inherits(EventEmitter, {
8+
constructor(id, client) {
9+
EventEmitter.apply(this);
10+
this.id = id;
11+
this._client = client;
12+
this._client.register(this);
13+
client.on('message', this._dispatch.bind(this));
14+
},
15+
_dispatch(message) {
16+
message.forEach(({
17+
op,
18+
object,
19+
query_id: queryId,
20+
updatedKeys,
21+
}) => {
22+
if (queryId !== this.id) return;
23+
const target = AV.parseJSON(Object.assign({
24+
__type: object.className === '_File' ? 'File' : 'Object',
25+
}, object));
26+
if (updatedKeys) {
27+
this.emit(op, target, updatedKeys);
28+
} else {
29+
this.emit(op, target);
30+
}
31+
});
32+
},
33+
unsubscribe() {
34+
this._client.deregister(this);
35+
return request({
36+
method: 'POST',
37+
path: '/LiveQuery/unsubscribe',
38+
data: {
39+
id: this._client.id,
40+
query_id: this.id,
41+
},
42+
});
43+
},
44+
}, {
45+
init: (query, {
46+
subscriptionId: userDefinedSubscriptionId = AV._getSubscriptionId(),
47+
} = {}) => {
48+
if (!AV._config.realtime) throw new Error('LiveQuery not supported. Please use the LiveQuery bundle. https://url.leanapp.cn/enable-live-query');
49+
if (!(query instanceof AV.Query)) throw new TypeError('LiveQuery must be inited with a Query');
50+
const { where, keys } = query.toJSON();
51+
return Promise.resolve(userDefinedSubscriptionId)
52+
.then(subscriptionId =>
53+
request({
54+
method: 'POST',
55+
path: '/LiveQuery/subscribe',
56+
data: {
57+
query: {
58+
where,
59+
keys,
60+
className: query.className,
61+
},
62+
id: subscriptionId,
63+
},
64+
}).then(({
65+
query_id: queryId,
66+
}) => AV._config.realtime.createLiveQueryClient(subscriptionId)
67+
.then(liveQueryClient => new LiveQuery(queryId, liveQueryClient))
68+
)
69+
);
70+
},
71+
});
72+
};

0 commit comments

Comments
 (0)