Created
September 14, 2018 01:29
-
-
Save flowstate/09661219199553339e966d0c1e7000bc to your computer and use it in GitHub Desktop.
MST / FireStore syncing approach
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
import { Person } from "./PersonStore"; | |
const Message = t.model("MessageModel", { | |
id: t.identifier, | |
author: t.late(() => t.reference(Person)), | |
body: t.string, | |
title: t.string, | |
toPeople: t.array(t.late(() => t.reference(Person))), | |
read: false | |
}) | |
const MessageStore = t.model("MessageStore", { | |
messages: t.map(Message), {}, | |
isLoading: true | |
}) | |
.views(self => ({ | |
get fs(){ | |
return getParent(self).firestore | |
} | |
})) | |
.actions(self => { | |
function afterAttach() { | |
self.fs().collection('messages').onSnapshot(self.handleCollectionSnapshot); | |
} | |
function handleCollectionSnapshot(snapshot) { | |
snapshot.docChanges().forEach((change) => { | |
const data = change.doc.data(); | |
data.id = change.doc.id; | |
switch (change.type) { | |
case 'added': | |
case 'modified': | |
self.messages.put(data); | |
break; | |
case 'removed': | |
const doomed = self.messages.get(data.id); | |
destroy(doomed); | |
break; | |
default: | |
break; | |
} | |
}); | |
} | |
function setLoading(loading) { | |
self.isLoading = loading | |
} | |
const addMessage = flow(function* addMessage(msg) { | |
try { | |
const messageRef = self.fs().collection('messages').doc(); | |
msg.id = messageRef.id; | |
const writeResult = yield self.fs().collection('people').doc(person.id).set(msg); | |
return { wasSuccessful: () => true, message: `message ${msg.title} was added to the platform.` }; | |
} catch (e) { | |
console.error(e); | |
return { wasSuccessful: () => false, message: `error adding message: ${e.message}` }; | |
} | |
}); | |
return { | |
setLoading, | |
loadMessages, | |
addMessage, | |
removeMessage, | |
} | |
})); | |
export function LateMessage(){return Message}; | |
export { Message, MessageStore }; |
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
import {Message} from './MessageStore' | |
const Person = types | |
.model('Person', { | |
id: types.identifier, | |
fullname: types.string, | |
messages: types.array(types.reference(types.late(() => Message))) | |
}) | |
.actions((self) => ({ | |
addMessage(msg) { | |
self.messages.push(msg); | |
// APPROACH 1: updating here seems really expensive and bad. | |
self.store.updatePerson(self) | |
}, | |
removeMessage(msg) { | |
self.messages.remove(msg); | |
// because I have to do it everywhere | |
self.store.updatePerson(self) | |
}, | |
})); | |
const PersonStore = types | |
.model('PersonStore', { | |
isLoading: true, | |
people: types.map(Person) | |
}) | |
.views(self => ({ | |
get fs(){ | |
return getParent(self).firestore | |
} | |
})) | |
.actions((self) => { | |
function afterAttach() { | |
// APPROACH 2: listener. This is where a ton of doubling comes from, but I don't know how else to get live updates | |
self.fs().collection('people').onSnapshot(self.handleCollectionSnapshot); | |
} | |
function setLoading(loading) { | |
self.isLoading = loading; | |
} | |
function handleCollectionSnapshot(snapshot) { | |
snapshot.docChanges().forEach((change) => { | |
const data = change.doc.data(); | |
data.id = change.doc.id; | |
switch (change.type) { | |
case 'added': | |
self.people.put(data); | |
break; | |
case 'modified': | |
// this is bad | |
// and writing a 'merge' seems ... brittle? | |
self.people.put(data); | |
break; | |
case 'removed': | |
const doomed = self.people.get(data.id); | |
destroy(doomed); | |
break; | |
default: | |
break; | |
} | |
}); | |
} | |
const loadFromFireStore = flow(function* loadFromFireStore() { | |
try { | |
setLoading(true); | |
const collectionSnap = yield self.fs().collection('people').get(); | |
collectionSnap.forEach((doc) => { | |
const data = doc.data(); | |
const id = doc.id; | |
data.id = id; | |
self.people.put(data); | |
}); | |
} catch (err) { | |
console.error('failed to load people', err); | |
} finally { | |
setLoading(false); | |
} | |
}); | |
// of course, if I call this, I also get a hit on the update from my listener | |
const addPerson = flow(function* addPerson(person) { | |
try { | |
const personRef = self.fs().collection('people').doc(); | |
person.id = personRef.id; | |
const writeResult = yield self.fs().collection('people').doc(person.id).set(person); | |
return { wasSuccessful: () => true, message: `person ${person.fullname} was added to the platform.` }; | |
} catch (e) { | |
console.error(e); | |
return { wasSuccessful: () => false, message: `error adding person: ${e.message}` }; | |
} | |
}); | |
const updatePerson = flow(function* updatePerson(person) { | |
try { | |
const out = yield self.fs().collection('people').doc(person.id).set(person, { merge: true }); | |
} catch (e) { | |
console.error(e); | |
} | |
}); | |
return { | |
afterAttach, | |
addPerson, | |
handleCollectionSnapshot, | |
loadFromFireStore, | |
updatePerson, | |
}; | |
}); | |
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
import { getEnv, types as t, flow } from "mobx-state-tree"; | |
import { MessageStore } from './MessageStore'; | |
import { PersonStore } from './PersonStore'; | |
const PhaseStore = t | |
.model("PhaseStore", { | |
personStore: t.optional(PersonStore, { people: {} }), | |
messageStore: t.optional(MessageStore, { posts: {} }), | |
}) | |
.actions(self => { | |
function afterCreate() { | |
self.loadAll() | |
} | |
// USAGE PROBLEM: this ends up blowing out stack depth due to the nested calls to update each person | |
function sendMessage(msg) { | |
const message = self.messageStore.addMessage(msg) | |
msg.author.addMessage(message) | |
message.isToPeople && message.toPeople.forEach(person => { | |
person.addMessage(message) | |
}) | |
return {wasSuccessful: () => true} | |
} | |
return { | |
afterCreate, | |
sendMessage | |
} | |
}); | |
export default PhaseStore; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You might be able to prevent the doubling issue by looking at
doc.metadata.hasPendingWrites
.https://firebase.google.com/docs/firestore/query-data/listen