users: {
$uid
},
timeline: {
$postid
},
followers: {
$uid
},
userPosts: {
$uid/$postid
}
Last active
April 20, 2021 17:19
-
-
Save davideast/e68aa87ea6f0e7a4dc08 to your computer and use it in GitHub Desktop.
Firebase Social Network Client Fanout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class PostFanout { | |
constructor(rootRef) { | |
this._rootRef = rootRef; | |
this.user = rootRef.getAuth(); | |
this.followers = new SyncPath(this._rootRef, 'followers'); | |
this.userPosts = new SyncPath(this._rootRef, 'userPosts'); | |
} | |
createAll(path, data) { | |
var fannedOutData = {}; | |
// update own record | |
fannedOutData['/users/' + this.user.uid + '/' + path] = data; | |
// update own posts | |
this.userPosts.keys().forEach((postKey) => { | |
fannedOutData['/userPosts/' + this.user.uid + '/' + postKey + '/' + path] = data; | |
}); | |
// update follower's timeline posts | |
this.followers.keys().forEach((followerKey) => { | |
this.userPosts.keys().forEach((postKey) => { | |
fannedOutData['/timeline/' + followerKey + '/' + postKey + '/' + path] = data; | |
}); | |
}); | |
return fannedOutData; | |
} | |
createOne(path, data) { | |
var fannedOutData = {}; | |
// write to user's own post collection | |
fannedOutData['/userPosts/' + this.user.uid + '/' + path] = data; | |
// write to users own timeline | |
fannedOutData['/timeline/' + this.user.uid + '/' + path] = data; | |
// write to each follower's timeline | |
this.followers.keys().forEach((followerKey) => { | |
fannedOutData['/timeline/' + followerKey + '/' + path] = data; | |
}); | |
return fannedOutData; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* posts.js | |
* This is a demonstration of applying a "fan-out" update from the client using Firebase. | |
* | |
* In social networks it is common to update a post to every user's "timeline". With Firebase | |
* client side fan-out you can apply the entire update in one write. | |
* | |
*/ | |
import {SyncPath} from 'SyncPath'; | |
import {PostFanout} from 'PostFanout'; | |
/** | |
* Data Layer class for "fanning out" posts to a user's followers. | |
* | |
* @property {Firebase Reference} _rootRef | |
* @property {FirebaseAuthUser} user | |
* @property {PostFanout} fanout | |
*/ | |
class PostService { | |
constructor(rootRef) { | |
this._rootRef = rootRef; | |
this.user = rootRef.getAuth(); | |
this.fanout = new PostFanout(this._rootRef); | |
} | |
/** | |
* This function writes a post to the timeline of every follower of the posting user.* | |
* @param {string} text | |
* @returns void | |
*/ | |
postToFollowers(text) { | |
var post = { | |
text: text, | |
uid: this.user.uid, | |
key: this._rootRef.push().key(), // create a unique key | |
username: this.user.password.email | |
}; | |
var fannedOutData = this.fanout.createOne(post.key, post); | |
// Send the fan-out data-structure to the Firebase database | |
this._rootRef.update(fannedOutData); | |
} | |
} | |
// Example | |
var ref = new Firebase('https://fur.firebaseio.com/'); | |
var btnPost = document.getElementById('btnPost'); | |
var btnLogin = document.getElementById('btnLogin'); | |
var txtMessage = document.getElementById('txtMessage'); | |
// Post message on click | |
btnLogin.addEventListener('click', () => { | |
ref.authWithPassword({ | |
email: '[email protected]', | |
password: 'password' | |
}, (authData) => { | |
var postService = new PostService(ref); | |
btnPost.addEventListener('click', () => { | |
postService.postToFollowers(txtMessage.value) | |
txtMessage.value = ''; | |
}); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"rules": { | |
"users": { | |
"$uid": { | |
".read": "auth.uid == $uid", | |
".write": "auth.uid == $uid" | |
} | |
}, | |
"timeline": { | |
"$uid": { | |
"$postid": { | |
".read": "true", | |
".write": "auth.uid == $uid" | |
} | |
} | |
}, | |
"userPosts": { | |
"$uid": { | |
"$postid": { | |
".read": "true", | |
".write": "auth.uid == $uid" | |
} | |
} | |
}, | |
"followers": { | |
"$uid": { | |
".read": "true", | |
".write": "auth.uid == $uid" | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class SyncPath { | |
constructor(rootRef, path) { | |
this._rootRef = rootRef; | |
this.user = this._rootRef.getAuth(); | |
this._userDataRef = this._rootRef.child(path).child(this.user.uid); | |
this.data = {}; | |
this._userDataRef.on('value', (snap) => this.data = snap.val() || {}); | |
} | |
keys() { | |
return Object.keys(this.data); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* users.js | |
* This is a demonstration of applying a "fanout" update from the client using Firebase. | |
* | |
* In social media netowrks it's common for a user to change their username. When this happens we'll need | |
* to update every post the user has posted to the proper username. | |
* | |
*/ | |
import {SyncPath} from 'SyncPath'; | |
import {PostFanout} from 'PostFanout'; | |
class UserService { | |
constructor(rootRef) { | |
this._rootRef = rootRef; | |
this.fanout = new PostsFanout(this._rootRef); | |
} | |
changeUsername(username) { | |
var fannedOutData = this.fanout.createAll('username', username); | |
this._rootRef.update(fannedOutData); | |
} | |
deletePosts() { | |
var fannedOutData = this.fanout.createAll('', null); | |
this._rootRef.update(fannedOutData); | |
} | |
} | |
// Example | |
var ref = new Firebase('https://fur.firebaseio.com/'); | |
var btnChange = document.getElementById('btnChange'); | |
var btnLogin = document.getElementById('btnLogin'); | |
var btnDelete = document.getElementById('btnDelete'); | |
var txtUsername = document.getElementById('txtUsername'); | |
// Post message on click | |
btnLogin.addEventListener('click', () => { | |
ref.authWithPassword({ | |
email: '[email protected]', | |
password: 'password' | |
}, () => { | |
var userService = new UserService(ref); | |
btnChange.addEventListener('click', () => userService.changeUsername(txtUsername.value)); | |
btnDelete.addEventListener('click', () => userService.deletePosts()); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How would you manage replies to user posts?