Skip to content
This repository was archived by the owner on Sep 23, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,15 @@ We wanted it to be easy to contribute for **non-technical people** and include e
* [Firebase](https://firebase.google.com/)

## Changelog
* 12.10.2019 - Upvoting an article
* 15.09.2017 - "Open as .png" option
* 13.09.2017 - Initial release

## Authors
[Bartosz Olchówka](https://twitter.com/bolchowka/) / CTO @ [LiveChat](https://www.livechatinc.com/)

## Contributors
[Kamil Rudnicki](https://twitter.com/krudnicki/) / [TimeCamp](https://www.timecamp.com/)

## License
This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details
17 changes: 17 additions & 0 deletions src/components/page/Page.css
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,20 @@
.content__editor__buttons__guide {
float: right;
}

.vote {
display: inline-block;
border: 1px solid #c4c4c4;
border-radius: 2px;
padding: 7px 5px;
color: #666;
font-size: 0.8em;
margin-top: 20px;
margin-bottom: 50px;
cursor: pointer;
margin-right: 10px;
}
.vote:hover {
background: #fff278;
color: #666;
}
61 changes: 50 additions & 11 deletions src/components/page/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,30 @@ export default class Page extends Component {
this.setEditMode(false);
}

upVote = (e) => {
if (this.props.onVote) {
this.props.onVote("upvotes");
}
}

hardVote = (e) => {
if (this.props.onVote) {
this.props.onVote("hard");
}
}

readVote = (e) => {
if (this.props.onVote) {
this.props.onVote("read");
}
}

obsoleteVote = (e) => {
if (this.props.onVote) {
this.props.onVote("read");
}
}

render() {
const contentClass = ['content', 'content--page'];
if (this.state.editMode) {
Expand All @@ -152,40 +176,54 @@ export default class Page extends Component {
<div className={contentClass.join(' ')}>
<div className="content__meta">
{this.props.canEdit &&
<p>{meta.reduce((acc, x) => acc === null ? [x] : [acc, ' | ', x], null)}</p>
<p>{meta.reduce((acc, x) => acc === null ? [x] : [acc, ' | ', x], null)}</p>
}
{this.props.currentlyViewing.length > 0 &&
<p className="content__meta__currently-viewing">
Reading now: {this.props.currentlyViewing.map(user => <Person data={user} key={user.email} />)}
</p>
<p className="content__meta__currently-viewing">
Reading now: {this.props.currentlyViewing.map(user => <Person data={user} key={user.email} />)}
</p>
}
</div>

<div className="content__body">
<Markdown className="content__markdown content--markdown">
{this.state.body}
{this.state.body}
</Markdown>

{this.state.editMode &&
<div className="content__editor">
<div className="content__editor">
<textarea
className="content__editor__textarea"
defaultValue={this.state.body}
onKeyDown={this.handleEditorKeyDown}
onChange={this.handleBodyChange}
ref={(ref) => ref && ref.focus()}
/>
<p className="content__editor__buttons">
<button onClick={this.saveChanges}>Save changes</button> or <a href="" className="content__editor__buttons__cancel" onClick={this.cancelChanges}>cancel</a>
<span className="content__editor__buttons__guide">
<p className="content__editor__buttons">
<button onClick={this.saveChanges}>Save changes</button> or <a href="" className="content__editor__buttons__cancel" onClick={this.cancelChanges}>cancel</a>
<span className="content__editor__buttons__guide">
<a href="https://guides.github.com/features/mastering-markdown/" target="_blank" rel="noopener noreferrer">Formatting help</a>
<span> | </span>
<a href="/diagrams-help" target="_blank" rel="noopener noreferrer">Diagrams help</a>
</span>
</p>
</div>
</p>
</div>
}
</div>

{!this.state.editMode &&
<a className="vote" onClick={this.upVote}><span role="img" aria-label="">👍 it helped</span> <b>{this.props.votes && this.props.votes['upvotes'] ? Object.keys(this.props.votes['upvotes']).length : "0"}</b></a>
}
{!this.state.editMode &&
<a className="vote" onClick={this.hardVote}><span role="img" aria-label="">🤔 not clear</span> <b>{this.props.votes && this.props.votes['hard'] ? Object.keys(this.props.votes['hard']).length : "0"}</b></a>
}
{!this.state.editMode &&
<a className="vote" onClick={this.readVote}><span role="img" aria-label="">✅ read</span> <b>{this.props.votes && this.props.votes['read'] ? Object.keys(this.props.votes['read']).length : "0"}</b></a>
}
{!this.state.editMode &&
<a className="vote" onClick={this.obsoleteVote}><span role="img" aria-label="">🗑 needs update</span> <b>{this.props.votes && this.props.votes['obsolete'] ? Object.keys(this.props.votes['obsolete']).length : "0"}</b></a>
}

</div>
);
}
Expand All @@ -197,6 +235,7 @@ Page.propTypes = {
onUnsavedChanges: PropTypes.func,
onChangesSaved: PropTypes.func,
canEdit: PropTypes.bool,
onVote: PropTypes.func,
currentlyViewing: PropTypes.array,
lastChangeTimestamp: PropTypes.number,
lastChangeAutor: PropTypes.string,
Expand Down
44 changes: 44 additions & 0 deletions src/containers/page/PageContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default class PageContainer extends Component {
currentlyViewing: [],
fetched: false,
body: '',
votes: {},
};

getCurrentPath() {
Expand Down Expand Up @@ -59,6 +60,7 @@ export default class PageContainer extends Component {
fetched: true,
e404: false,
body: data.body,
votes: data.votes,
lastChangeTimestamp: data.lastChangeTimestamp,
lastChangeAutor: data.lastChangeAutor
});
Expand Down Expand Up @@ -98,6 +100,16 @@ export default class PageContainer extends Component {
this.setState({
body: newBody
});

//mark as unread
let votes = this.state.votes;
if(votes && votes["read"]) {
firebase.database().ref(`pages/${this.getCurrentPath()}/votes/read`).remove();
delete votes["read"];
this.setState({
votes: votes,
});
}
}

createPage = () => {
Expand All @@ -118,6 +130,36 @@ export default class PageContainer extends Component {
}
}

onVote = (voteType) => {
let votes = this.state.votes;
if(votes && votes[voteType] && votes[voteType][Firebase.getUser().uid]) {
firebase.database().ref(`pages/${this.getCurrentPath()}/votes/${voteType}/${Firebase.getUser().uid}`).remove();

delete votes[voteType][Firebase.getUser().uid];

this.setState({
votes: votes,
});
} else {
if(!votes) {
votes = {}
}
if(!votes[voteType]) {
votes[voteType] = {}
}
votes[voteType][Firebase.getUser().uid] = {
timestamp: Utils.getTimestamp(),
user: Firebase.getUser().email
}
firebase.database().ref(`pages/${this.getCurrentPath()}`).update({
votes: votes
});
this.setState({
votes: votes,
});
}
}

render() {
if (!this.state.fetched) {
if (this.props.spinner === false) {
Expand All @@ -143,6 +185,8 @@ export default class PageContainer extends Component {
lastChangeTimestamp={this.state.lastChangeTimestamp}
lastChangeAutor={this.state.lastChangeAutor}
body={this.state.body}
votes={this.state.votes}
onVote={this.onVote}
onEditModeChange={this.props.onEditModeChange}
onChangesSaved={this.onChangesSaved}
onUnsavedChanges={this.props.onUnsavedChanges}
Expand Down