Skip to content

Commit 518fb90

Browse files
committed
feat(demo): add live-query demo and a setting page
1 parent 3161542 commit 518fb90

File tree

4 files changed

+470
-2
lines changed

4 files changed

+470
-2
lines changed

demo/leaderboard/leaderboard.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1+
let configs = {};
2+
try {
3+
configs = JSON.parse(localStorage.getItem('js-sdk-demo/configs')) || {};
4+
} catch (e) {}
5+
6+
const {
7+
appId = 'FNHw86LIu6lnFToIEDemKCQl-gzGzoHsz',
8+
appKey = 'NJLcuqnsowO4GEPOwOn2O27C',
9+
} = configs;
10+
111
AV.init({
2-
appId: 'FNHw86LIu6lnFToIEDemKCQl-gzGzoHsz',
3-
appKey: 'DyvpOorH5HK1CVLDqDhb4gNT',
12+
appId,
13+
appKey,
414
});
515

616
const MAX_RESULTS_COUNT = 12;

demo/live-query/app.js

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
let configs = {};
2+
try {
3+
configs = JSON.parse(localStorage.getItem('js-sdk-demo/configs')) || {};
4+
} catch (e) {}
5+
6+
const {
7+
appId = 'FNHw86LIu6lnFToIEDemKCQl-gzGzoHsz',
8+
appKey = 'NJLcuqnsowO4GEPOwOn2O27C',
9+
} = configs;
10+
11+
AV.init({
12+
appId,
13+
appKey,
14+
});
15+
16+
var Todo = AV.Object.extend('Todo');
17+
18+
// visibility filters
19+
var filters = {
20+
all: function(todos) {
21+
return todos;
22+
},
23+
active: function(todos) {
24+
return todos.filter(function(todo) {
25+
return !todo.done;
26+
});
27+
},
28+
completed: function(todos) {
29+
return todos.filter(function(todo) {
30+
return todo.done;
31+
});
32+
},
33+
};
34+
35+
var bind = (subscription, initialStats, onChange) => {
36+
let stats = [...initialStats];
37+
const remove = value => {
38+
stats = stats.filter(target => target.id !== value.id);
39+
return onChange(stats);
40+
};
41+
const upsert = value => {
42+
let existed = false;
43+
stats = stats.map(
44+
target => (target.id === value.id ? ((existed = true), value) : target)
45+
);
46+
if (!existed) stats = [value, ...stats];
47+
return onChange(stats);
48+
};
49+
subscription.on('create', upsert);
50+
subscription.on('update', upsert);
51+
subscription.on('enter', upsert);
52+
subscription.on('leave', remove);
53+
subscription.on('delete', remove);
54+
return () => {
55+
subscription.off('create', upsert);
56+
subscription.off('update', upsert);
57+
subscription.off('enter', upsert);
58+
subscription.off('leave', remove);
59+
subscription.off('delete', remove);
60+
};
61+
};
62+
63+
// app Vue instance
64+
var app = new Vue({
65+
// app initial state
66+
data: {
67+
todos: [],
68+
newTodo: '',
69+
editedTodo: null,
70+
visibility: 'all',
71+
username: '',
72+
password: '',
73+
user: null,
74+
},
75+
76+
created: function() {
77+
var user = AV.User.current();
78+
if (user) {
79+
this.user = user.toJSON();
80+
}
81+
},
82+
83+
watch: {
84+
'user.objectId': {
85+
handler: function(id) {
86+
if (id) {
87+
this.fetchTodos(id);
88+
} else {
89+
this.todos = [];
90+
}
91+
},
92+
},
93+
},
94+
95+
// computed properties
96+
// https://vuejs.org/guide/computed.html
97+
computed: {
98+
filteredTodos: function() {
99+
return filters[this.visibility](this.todos);
100+
},
101+
remaining: function() {
102+
return filters.active(this.todos).length;
103+
},
104+
allDone: {
105+
get: function() {
106+
return this.remaining === 0;
107+
},
108+
set: function(done) {
109+
AV.Object.saveAll(
110+
filters[done ? 'active' : 'completed'](this.todos).map(function(
111+
todo
112+
) {
113+
todo.done = done;
114+
return AV.Object.createWithoutData('Todo', todo.objectId).set(
115+
'done',
116+
done
117+
);
118+
})
119+
);
120+
},
121+
},
122+
},
123+
124+
filters: {
125+
pluralize: function(n) {
126+
return n === 1 ? 'item' : 'items';
127+
},
128+
},
129+
130+
// methods that implement data logic.
131+
// note there's no DOM manipulation here at all.
132+
methods: {
133+
fetchTodos: function(id) {
134+
const query = new AV.Query(Todo)
135+
.equalTo('user', AV.Object.createWithoutData('User', id))
136+
.descending('createdAt');
137+
const updateTodos = this.updateTodos.bind(this);
138+
return AV.Promise.all([query.find().then(updateTodos), query.subscribe()])
139+
.then(
140+
function([todos, subscription]) {
141+
this.subscription = subscription;
142+
this.unbind = bind(subscription, todos, updateTodos);
143+
}.bind(this)
144+
)
145+
.catch(alert);
146+
},
147+
148+
login: function() {
149+
AV.User.logIn(this.username, this.password)
150+
.then(
151+
function(user) {
152+
this.user = user.toJSON();
153+
this.username = this.password = '';
154+
}.bind(this)
155+
)
156+
.catch(alert);
157+
},
158+
159+
signup: function() {
160+
AV.User.signUp(this.username, this.password)
161+
.then(
162+
function(user) {
163+
this.user = user.toJSON();
164+
this.username = this.password = '';
165+
}.bind(this)
166+
)
167+
.catch(alert);
168+
},
169+
170+
logout: function() {
171+
AV.User.logOut();
172+
this.user = null;
173+
this.subscription.unsubscribe();
174+
this.unbind();
175+
},
176+
177+
updateTodos: function(todos) {
178+
this.todos = todos.map(function(todo) {
179+
return todo.toJSON();
180+
});
181+
return todos;
182+
},
183+
184+
addTodo: function() {
185+
var value = this.newTodo && this.newTodo.trim();
186+
if (!value) {
187+
return;
188+
}
189+
var acl = new AV.ACL();
190+
acl.setPublicReadAccess(false);
191+
acl.setPublicWriteAccess(false);
192+
acl.setReadAccess(AV.User.current(), true);
193+
acl.setWriteAccess(AV.User.current(), true);
194+
new Todo({
195+
content: value,
196+
done: false,
197+
user: AV.User.current(),
198+
})
199+
.setACL(acl)
200+
.save()
201+
.then(
202+
function(todo) {
203+
this.todos.push(todo.toJSON());
204+
}.bind(this)
205+
)
206+
.catch(alert);
207+
this.newTodo = '';
208+
},
209+
210+
removeTodo: function(todo) {
211+
AV.Object.createWithoutData('Todo', todo.objectId)
212+
.destroy()
213+
.then(
214+
function() {
215+
this.todos.splice(this.todos.indexOf(todo), 1);
216+
}.bind(this)
217+
)
218+
.catch(alert);
219+
},
220+
221+
editTodo: function(todo) {
222+
this.beforeEditCache = todo.content;
223+
this.editedTodo = todo;
224+
},
225+
226+
doneEdit: function(todo) {
227+
this.editedTodo = null;
228+
todo.content = todo.content.trim();
229+
AV.Object.createWithoutData('Todo', todo.objectId)
230+
.save({
231+
content: todo.content,
232+
done: todo.done,
233+
})
234+
.catch(alert);
235+
if (!todo.content) {
236+
this.removeTodo(todo);
237+
}
238+
},
239+
240+
cancelEdit: function(todo) {
241+
this.editedTodo = null;
242+
todo.content = this.beforeEditCache;
243+
},
244+
245+
removeCompleted: function() {
246+
AV.Object.destroyAll(
247+
filters.completed(this.todos).map(function(todo) {
248+
return AV.Object.createWithoutData('Todo', todo.objectId);
249+
})
250+
)
251+
.then(
252+
function() {
253+
this.todos = filters.active(this.todos);
254+
}.bind(this)
255+
)
256+
.catch(alert);
257+
},
258+
},
259+
260+
// a custom directive to wait for the DOM to be updated
261+
// before focusing on the input field.
262+
// https://vuejs.org/guide/custom-directive.html
263+
directives: {
264+
'todo-focus': function(el, value) {
265+
if (value) {
266+
el.focus();
267+
}
268+
},
269+
},
270+
});
271+
272+
// handle routing
273+
function onHashChange() {
274+
var visibility = window.location.hash.replace(/#\/?/, '');
275+
if (filters[visibility]) {
276+
app.visibility = visibility;
277+
} else {
278+
window.location.hash = '';
279+
app.visibility = 'all';
280+
}
281+
}
282+
283+
window.addEventListener('hashchange', onHashChange);
284+
onHashChange();
285+
286+
// mount
287+
app.$mount('.todoapp');

0 commit comments

Comments
 (0)