markuapad 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/markuapad.js +33 -32
- data/app/assets/stylesheets/markuapad.css +1 -1
- data/build/app.css +1 -1
- data/build/app.js +33 -32
- data/build/dist.css +1 -1
- data/build/dist.js +33 -32
- data/build/index.js +1 -1
- data/build/web.css +1 -1
- data/build/web.js +33 -32
- data/lib/markuapad/version.rb +1 -1
- data/src/file_accessor.js +4 -0
- data/src/index.js +6 -0
- data/src/jsx/main.jsx +29 -23
- data/src/jsx/merge_conflict_modal.jsx +99 -0
- data/src/styles/_merge_conflicts.scss +51 -0
- data/src/styles/_workspace.scss +1 -1
- data/src/styles/app.scss +1 -0
- metadata +4 -2
data/src/jsx/main.jsx
CHANGED
|
@@ -8,6 +8,7 @@ import LivePreview from "./live_preview";
|
|
|
8
8
|
import FileAccessor from "../file_accessor";
|
|
9
9
|
import ImageModal from "./image_modal";
|
|
10
10
|
import _ from "underscore";
|
|
11
|
+
import MergeConflictModal from './merge_conflict_modal'
|
|
11
12
|
|
|
12
13
|
import { getCached } from "../util"
|
|
13
14
|
|
|
@@ -21,27 +22,18 @@ class Main extends React.Component {
|
|
|
21
22
|
imageFile: null,
|
|
22
23
|
previewState: 'closed',
|
|
23
24
|
inLiveMode: this.props.options.enablePreview,
|
|
24
|
-
previewHtml: ""
|
|
25
|
+
previewHtml: "",
|
|
26
|
+
isResolvingMergeConflicts: false,
|
|
27
|
+
mergeConflicts: null
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
// Autobind
|
|
28
|
-
this.onChangeFile = this.onChangeFile.bind(this);
|
|
29
|
-
this.onGeneratePreview = this.onGeneratePreview.bind(this);
|
|
30
|
-
this.onPreviewReady = this.onPreviewReady.bind(this);
|
|
31
|
-
this.onClosePreview = this.onClosePreview.bind(this);
|
|
32
|
-
this.toggleLiveMode = this.toggleLiveMode.bind(this);
|
|
33
|
-
this.onBookContentChanged = this.onBookContentChanged.bind(this);
|
|
34
|
-
this.getWorkspaceClass = this.getWorkspaceClass.bind(this);
|
|
35
|
-
this.onManuscriptChange = this.onManuscriptChange.bind(this);
|
|
36
|
-
this.onFileAdded = this.onFileAdded.bind(this);
|
|
37
|
-
this.onPreviewImage = this.onPreviewImage.bind(this);
|
|
38
|
-
|
|
39
30
|
// File access hooks
|
|
40
31
|
FileAccessor.onDelete(this.onManuscriptChange);
|
|
41
32
|
FileAccessor.onManuscriptChange(this.onManuscriptChange);
|
|
42
33
|
FileAccessor.onAdd(this.onFileAdded);
|
|
43
34
|
FileAccessor.onProgress(this.onProgress);
|
|
44
35
|
FileAccessor.onProgressStarted(this.onProgressStarted);
|
|
36
|
+
FileAccessor.onMergeConflicts(this.onMergeConflicts);
|
|
45
37
|
}
|
|
46
38
|
|
|
47
39
|
// Lifecycle methods
|
|
@@ -51,12 +43,25 @@ class Main extends React.Component {
|
|
|
51
43
|
this.onGeneratePreview();
|
|
52
44
|
}
|
|
53
45
|
|
|
46
|
+
// parameter conflicts is an array of tuples.
|
|
47
|
+
// { filename: String, serverVersion: String, clientVersion: String }
|
|
48
|
+
onMergeConflicts = (conflicts) => {
|
|
49
|
+
this.setState({
|
|
50
|
+
mergeConflicts: conflicts,
|
|
51
|
+
isResolvingMergeConflicts: true
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
doneResolvingConflicts = () => {
|
|
56
|
+
this.setState({ mergeConflicts: null, isResolvingMergeConflicts: false })
|
|
57
|
+
}
|
|
58
|
+
|
|
54
59
|
// File event operations
|
|
55
|
-
onFileAdded(file) {
|
|
60
|
+
onFileAdded = (file) => {
|
|
56
61
|
this.setState({ currentFile: file });
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
onManuscriptChange() {
|
|
64
|
+
onManuscriptChange = () => {
|
|
60
65
|
// Re-preview
|
|
61
66
|
if (this.state.inLiveMode)
|
|
62
67
|
this.onGeneratePreview();
|
|
@@ -76,16 +81,16 @@ class Main extends React.Component {
|
|
|
76
81
|
this.setState({ inProgress: true })
|
|
77
82
|
}
|
|
78
83
|
|
|
79
|
-
onPreviewImage(file) {
|
|
84
|
+
onPreviewImage = (file) => {
|
|
80
85
|
this.setState({ imageFile: file });
|
|
81
86
|
}
|
|
82
87
|
|
|
83
|
-
onChangeFile(file) {
|
|
88
|
+
onChangeFile = (file) => {
|
|
84
89
|
this.setState({ currentFile: file });
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
// Preview based methods
|
|
88
|
-
onPreviewReady(errors, html) {
|
|
93
|
+
onPreviewReady = (errors, html) => {
|
|
89
94
|
// If someone has already stopped the preview then just bail
|
|
90
95
|
if (this.state.previewState === "closed")
|
|
91
96
|
return
|
|
@@ -93,17 +98,17 @@ class Main extends React.Component {
|
|
|
93
98
|
this.setState({ previewHtml: html, previewState: "done", previewErrors: errors })
|
|
94
99
|
}
|
|
95
100
|
|
|
96
|
-
onGeneratePreview(e) {
|
|
101
|
+
onGeneratePreview = (e) => {
|
|
97
102
|
// Call out to the markua processor
|
|
98
103
|
this.setState({ previewState: "previewing" });
|
|
99
104
|
this.props.markua.run(this.onPreviewReady, { cursor: getCached("markuapad_cursor") })
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
onClosePreview(e) {
|
|
107
|
+
onClosePreview = (e) => {
|
|
103
108
|
this.setState({ previewState: "closed" })
|
|
104
109
|
}
|
|
105
110
|
|
|
106
|
-
toggleLiveMode() {
|
|
111
|
+
toggleLiveMode = () => {
|
|
107
112
|
// Clear the preview state here as well, so we don't accidentally open a preview
|
|
108
113
|
this.setState({ inLiveMode: !this.state.inLiveMode, previewState: "closed" }, () => {
|
|
109
114
|
window.dispatchEvent(new Event('resize'));
|
|
@@ -114,12 +119,12 @@ class Main extends React.Component {
|
|
|
114
119
|
});
|
|
115
120
|
}
|
|
116
121
|
|
|
117
|
-
onBookContentChanged() {
|
|
122
|
+
onBookContentChanged = () => {
|
|
118
123
|
if (this.state.inLiveMode)
|
|
119
124
|
this.onGeneratePreview();
|
|
120
125
|
}
|
|
121
126
|
|
|
122
|
-
getWorkspaceClass() {
|
|
127
|
+
getWorkspaceClass = () => {
|
|
123
128
|
let workspaceClass = `workspace`;
|
|
124
129
|
workspaceClass += this.state.inLiveMode ? ' live' : '';
|
|
125
130
|
return workspaceClass;
|
|
@@ -151,6 +156,7 @@ class Main extends React.Component {
|
|
|
151
156
|
</section>
|
|
152
157
|
{ this.state.inLiveMode ? <span /> : <Preview key='preview' inLiveMode={this.state.inLiveMode} onClosePreview={this.onClosePreview} html={this.state.previewHtml} previewState={this.state.previewState} previewErrors={this.state.previewErrors} /> }
|
|
153
158
|
{ this.state.imageFile ? <ImageModal file={this.state.imageFile} onClose={ _.partial(this.onPreviewImage, null) } /> : null }
|
|
159
|
+
{ this.state.isResolvingMergeConflicts ? <MergeConflictModal conflicts={this.state.mergeConflicts} onClose={this.doneResolvingConflicts} /> : null }
|
|
154
160
|
</section>
|
|
155
161
|
);
|
|
156
162
|
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import FileAccessor from "../file_accessor"
|
|
3
|
+
import _ from 'underscore'
|
|
4
|
+
|
|
5
|
+
const delimeter = (type, filename) => {
|
|
6
|
+
return `\n\n================================= ${type} version of ${filename} =================================\n\n`
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class MergeConflictModal extends React.Component {
|
|
10
|
+
static propTypes = {
|
|
11
|
+
conflicts: React.PropTypes.array.isRequired,
|
|
12
|
+
onClose: React.PropTypes.func.isRequired
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
state = {
|
|
16
|
+
currentConflictIndex: 0
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
resolveConflict = (method) => {
|
|
20
|
+
let conflict = this.props.conflicts[this.state.currentConflictIndex];
|
|
21
|
+
|
|
22
|
+
switch (method) {
|
|
23
|
+
case 'client':
|
|
24
|
+
break;
|
|
25
|
+
case 'server':
|
|
26
|
+
// Save the file on the server.
|
|
27
|
+
FileAccessor.save(conflict.filename, 'manuscript', conflict.serverVersion)
|
|
28
|
+
break;
|
|
29
|
+
case 'both':
|
|
30
|
+
// Concat both versions separated by a delimeter
|
|
31
|
+
FileAccessor.save(conflict.filename, 'manuscript', delimeter('Local', conflict.filename) +
|
|
32
|
+
conflict.clientVersion +
|
|
33
|
+
delimeter('Server', conflict.filename) +
|
|
34
|
+
conflict.serverVersion)
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (this.state.currentConflictIndex + 1 >= this.props.conflicts.length) {
|
|
39
|
+
// We are done.
|
|
40
|
+
this.props.onClose()
|
|
41
|
+
} else {
|
|
42
|
+
// Increase the index
|
|
43
|
+
this.setState({ currentConflictIndex: this.state.currentConflictIndex + 1 })
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
renderCurrentConflict() {
|
|
48
|
+
return (
|
|
49
|
+
<div className="conflict-wrapper">
|
|
50
|
+
<section className="conflict-content client-version">
|
|
51
|
+
<h6>Your local copy</h6>
|
|
52
|
+
<pre>
|
|
53
|
+
{ this.props.conflicts[this.state.currentConflictIndex].clientVersion }
|
|
54
|
+
</pre>
|
|
55
|
+
</section>
|
|
56
|
+
<section className="conflict-content server-version">
|
|
57
|
+
<h6>Server copy</h6>
|
|
58
|
+
<pre>
|
|
59
|
+
{ this.props.conflicts[this.state.currentConflictIndex].serverVersion }
|
|
60
|
+
</pre>
|
|
61
|
+
</section>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
render() {
|
|
67
|
+
return (
|
|
68
|
+
<div className="modal merge-conflicts-modal">
|
|
69
|
+
<div className="modal-fade-screen">
|
|
70
|
+
<div className="modal-inner">
|
|
71
|
+
<div className="modal-close" onClick={ this.props.onClose }></div>
|
|
72
|
+
<h4>Conflicting changes for { this.props.conflicts[this.state.currentConflictIndex].filename }</h4>
|
|
73
|
+
<p className="conflict-explanation">
|
|
74
|
+
Uh oh! You have conflicting changes for { this.props.conflicts[this.state.currentConflictIndex].filename }. This
|
|
75
|
+
probably happened because you were using the writing tools without
|
|
76
|
+
an active internet connection. To resolve this, review the differences here, and select the option
|
|
77
|
+
you want at the bottom. If you choose to 'keep both', then we will combine the two versions into one file so you
|
|
78
|
+
can manually merge them.
|
|
79
|
+
</p>
|
|
80
|
+
{ this.renderCurrentConflict() }
|
|
81
|
+
<ul className="conflict-actions">
|
|
82
|
+
<li>
|
|
83
|
+
<a onClick={_.partial(this.resolveConflict, 'client')}>Keep local version</a>
|
|
84
|
+
</li>
|
|
85
|
+
<li>
|
|
86
|
+
<a onClick={_.partial(this.resolveConflict, 'server')}>Keep remote version</a>
|
|
87
|
+
</li>
|
|
88
|
+
<li>
|
|
89
|
+
<a onClick={_.partial(this.resolveConflict, 'both')}>Keep both versions</a>
|
|
90
|
+
</li>
|
|
91
|
+
</ul>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export default MergeConflictModal;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
.markuapad .merge-conflicts-modal {
|
|
2
|
+
.modal-fade-screen {
|
|
3
|
+
padding-top: 4em;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.modal-inner {
|
|
7
|
+
max-height: 95%;
|
|
8
|
+
width: 80%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.conflict-explanation {
|
|
12
|
+
padding: 1em;
|
|
13
|
+
background: #fcc;
|
|
14
|
+
border: thin solid #faa;
|
|
15
|
+
text-shadow: 0 1px 0 #fff;
|
|
16
|
+
border-radius: 4px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.conflict-wrapper {
|
|
20
|
+
@include display(flex);
|
|
21
|
+
@include flex(1 1 100%);
|
|
22
|
+
@include flex-flow(row nowrap);
|
|
23
|
+
padding: 20px 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.conflict-content {
|
|
27
|
+
@include flex(1);
|
|
28
|
+
padding: 1em;
|
|
29
|
+
|
|
30
|
+
&.client-version {
|
|
31
|
+
border-right: thin dashed #ddd;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
h6 {
|
|
35
|
+
border-bottom: thin dashed #ddd;
|
|
36
|
+
font-weight: 700;
|
|
37
|
+
padding-bottom: 1em;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.conflict-actions {
|
|
42
|
+
text-align: center;
|
|
43
|
+
li {
|
|
44
|
+
display: inline-block;
|
|
45
|
+
@extend %nav-button;
|
|
46
|
+
> a {
|
|
47
|
+
border-color: #eee;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
data/src/styles/_workspace.scss
CHANGED
data/src/styles/app.scss
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: markuapad
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Braden Simpson
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-08-
|
|
11
|
+
date: 2015-08-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -85,6 +85,7 @@ files:
|
|
|
85
85
|
- src/jsx/image_modal.jsx
|
|
86
86
|
- src/jsx/live_preview.jsx
|
|
87
87
|
- src/jsx/main.jsx
|
|
88
|
+
- src/jsx/merge_conflict_modal.jsx
|
|
88
89
|
- src/jsx/preview.jsx
|
|
89
90
|
- src/jsx/toolbar.jsx
|
|
90
91
|
- src/markuapad.js
|
|
@@ -93,6 +94,7 @@ files:
|
|
|
93
94
|
- src/styles/_file_browser.scss
|
|
94
95
|
- src/styles/_grid-settings.scss
|
|
95
96
|
- src/styles/_layout.scss
|
|
97
|
+
- src/styles/_merge_conflicts.scss
|
|
96
98
|
- src/styles/_modal.scss
|
|
97
99
|
- src/styles/_preview.scss
|
|
98
100
|
- src/styles/_toolbar.scss
|