collaboratively edit any file

large txt files requires a custom fork of etherpad-lite-client
This commit is contained in:
Philip Schatz 2016-04-29 04:00:14 -04:00
parent e704733b60
commit e3b86a351b
4 changed files with 51 additions and 5 deletions

View File

@ -38,7 +38,7 @@
"classnames": "^2.2.1",
"d3": "^3.5.12",
"dexie": "^1.3.1",
"etherpad-lite-client": "^0.8.0",
"etherpad-lite-client": "philschatz/etherpad-lite-client-js#master",
"gfm-linkify": "^0.1.0",
"jsonquery-engine": "^1.0.2",
"level-js": "philschatz/level.js#master",

View File

@ -0,0 +1,43 @@
import React from 'react';
import Client from '../github-client';
import IssueStore from '../issue-store';
import Etherpad from './etherpad';
const EtherpadIssueShell = React.createClass({
render() {
const {repoOwner, repoName, branch, splat} = this.props.params;
const path = splat;
const title = `Editing ${repoOwner}/${repoName} (${branch}) ${path}`;
const padName = `file_github.com_${repoOwner}_${repoName}_${path.replace('/', '_')}`;
let fileText = null;
let fileSha = null;
const getBody = () => {
return fileText || ''; // return '' so it can be trimmed when comparing
};
const saveBody = (text) => {
const message = 'Saved using gh-board';
return Client.getOcto().repos(repoOwner, repoName).contents(path).add({message, content: btoa(text), sha: fileSha, branch}).then(({content}) => {
fileSha = content.sha;
});
};
const loadBody = () => {
return Client.getOcto().repos(repoOwner, repoName).contents(path).fetch({ref: branch}).then(({content, sha, encoding}) => {
fileText = atob(content);
fileSha = sha;
return fileText;
});
};
const promise = loadBody();
return (
<Etherpad promise={promise} title={title} padName={padName} getBody={getBody} saveBody={saveBody} loadBody={loadBody} repoOwner={repoOwner} repoName={repoName}/>
);
}
});
export default EtherpadIssueShell;

View File

@ -13,7 +13,7 @@ const EtherpadIssueShell = React.createClass({
const title = `Editing ${repoOwner}/${repoName}#${number}`;
const padName = `issue_github.com_${repoOwner}_${repoName}_${number}`;
const getIssueBody = () => {
const getBody = () => {
const card = IssueStore.issueNumberToCard(repoOwner, repoName, number);
if (card.issue) {
return card.issue.body;
@ -21,10 +21,10 @@ const EtherpadIssueShell = React.createClass({
return ''; // return '' so it can be trimmed when comparing
}
};
const saveIssueBody = (text) => {
const saveBody = (text) => {
return Client.getOcto().repos(repoOwner, repoName).issues(number).update({body: text});
};
const loadIssueBody = () => {
const loadBody = () => {
const card = IssueStore.issueNumberToCard(repoOwner, repoName, number);
// refetch the Issue body (esp if it hasn't been loaded yet)
@ -34,7 +34,7 @@ const EtherpadIssueShell = React.createClass({
};
return (
<Etherpad title={title} padName={padName} getBody={getIssueBody} saveBody={saveIssueBody} loadBody={loadIssueBody} repoOwner={repoOwner} repoName={repoName}/>
<Etherpad title={title} padName={padName} getBody={getBody} saveBody={saveBody} loadBody={loadBody} repoOwner={repoOwner} repoName={repoName}/>
);
}
});

View File

@ -13,6 +13,7 @@ import MergedSince from './components/merged-since';
import {MergedSinceFormShell} from './components/merged-since';
import DiffEnvs from './components/diff-envs';
import EtherpadIssueShell from './components/etherpad-issue';
import EtherpadFileShell from './components/etherpad-file';
import BatchLabelsShell from './components/batch-labels';
import {parseRoute, buildRoute} from './route-utils';
@ -34,6 +35,7 @@ const routes = [
childRoutes: [
{ path: '/diff-envs/:startHost/:endHost', component: DiffEnvs},
{ path: '/p-issue/:repoOwner/:repoName/:number', component: EtherpadIssueShell},
{ path: '/p-file/:repoOwner/:repoName/:branch/**', component: EtherpadFileShell},
{ path: '/r/:repoStr(/m/:milestonesStr)(/t/:tagsStr)(/u/:userName)(/x/:columnRegExpStr)',
indexRoute: {component: RepoKanban},
// If you change these children (or the parents) make sure you edit RELEVANT_PATH_SEGMENT in another file.
@ -52,6 +54,7 @@ const routes = [
// Redirect to the gantt URL
{ path: 'milestone-review', onEnter: (state, replace) => replace(null, buildRoute('gantt', parseRoute(state))) },
{ path: 'p-issue/:repoOwner/:repoName/:number', component: EtherpadIssueShell},
{ path: 'p-file/:repoOwner/:repoName/:branch/**', component: EtherpadFileShell},
{ path: 'gantt',
// Keep the review page as a separate chunk because it contains d3
getComponent(location, callback) {