Skip to content

Instantly share code, notes, and snippets.

@dgellow
Last active September 15, 2016 08:49
Show Gist options
  • Save dgellow/31a4638827b7ac15833d to your computer and use it in GitHub Desktop.
Save dgellow/31a4638827b7ac15833d to your computer and use it in GitHub Desktop.
Simple multi user text editor based on Meteor http://react-sync-texteditor.meteor.com/

Install Meteor

Follow instructions.

Prepare the project

# Create a new meteor project
meteor create <appName>
cd <appName>

# Delete default files
rm ./*

# Install meteor dependencies
meteor add reactjs:react accounts-ui accounts-password

# Copy files from this gist
git clone https://gist.github.com/31a4638827b7ac15833d.git gist-sync-editor
cp gist-sync-editor/* ./
rm -rf gist-sync-editor

Run local server

meteor
var App = React.createClass({
mixins: [ReactMeteor.Mixin],
startMeteorSubscriptions: function() {
Meteor.subscribe('documents');
},
getMeteorState: function() {
var doc = Documents.findOne({
title: 'Initial document'
});
return {
document: doc,
documentContent: doc ? doc.content: '',
users: Meteor.users.find({})
};
},
handleChange: function(event) {
var text = event.target.value;
this.setState({
documentContent: text,
cursor: event.target.selectionStart,
nbChars: event.target.value.length
});
Meteor.call('updateDocument',
this.state.document._id,
text);
},
componentDidUpdate: function() {
var domNode = this.refs.textarea.getDOMNode(),
newNbChars = domNode.value.length,
diffNbChars = newNbChars - this.state.nbChars,
newCursor = this.state.cursor + diffNbChars;
domNode.setSelectionRange(
newCursor, newCursor
);
},
render: function() {
var users = this.state.users.map(function(user) {
return (<span class="username">{user.username}</span>);
});
return (
<div>
<p>{users}</p>
<textarea ref="textarea" onChange={this.handleChange}
defaultValue={'I am empty'}
value={this.state.documentContent}>
</textarea>
</div>
);
}
});
if (Meteor.isClient) {
Meteor.startup(function() {
React.render(
<App />,
document.getElementById('react-root')
);
});
Accounts.ui.config({
passwordSignupFields: 'USERNAME_ONLY'
});
}
Documents = new Mongo.Collection('documents');
Meteor.methods({
addDocument: function(title) {
Documents.insert({
content: '',
createdAt: new Date(),
title: title
});
},
updateDocument: function(docId, content) {
var doc = Documents.findOne({_id: docId});
if (!doc) {
throw new Meteor.Error('not-found');
}
Documents.update(doc._id, {$set: {content: content}});
}
});
<head>
<title>sync-texteditor</title>
</head>
<body>
<h1>Multi User Text Editor</h1>
<div id="react-root"></div>
</body>
textarea {
margin: auto;
width: 90vw;
height: 80vh;
}
.username {
border: 1px solid black;
padding: 2px;
}
if (Meteor.isServer) {
Meteor.publish('documents', function() {
return Documents.find({});
});
Meteor.startup(function() {
var doc = Documents.findOne({title: 'Initial document'});
if (!doc) {
Documents.insert({
title: 'Initial document',
content: '',
createdAt: new Date()
});
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment