mirror of
https://github.com/urbit/shrub.git
synced 2025-01-03 01:54:43 +03:00
Merge pull request #2220 from urbit/ixv/publish/notebook-page
OS1: publish implement notebook page, changes to post page
This commit is contained in:
commit
2df06777e6
@ -628,10 +628,12 @@
|
||||
++ form-note
|
||||
|= [note-name=@tas udon=@t]
|
||||
^- note
|
||||
=/ build=(each manx tang)
|
||||
%- mule |.
|
||||
^- manx
|
||||
elm:(static:cram (ream udon))
|
||||
=/ body=tape (slag (add 3 (need (find ";>" (trip udon)))) (trip udon))
|
||||
=/ snippet=@t (of-wain:format (scag 1 (to-wain:format (crip body))))
|
||||
:: =/ build=(each manx tang)
|
||||
:: %- mule |.
|
||||
:: ^- manx
|
||||
:: elm:(static:cram (ream udon))
|
||||
::
|
||||
=/ meta=(each (map term knot) tang)
|
||||
%- mule |.
|
||||
@ -658,7 +660,7 @@
|
||||
now.bol
|
||||
%.n
|
||||
udon
|
||||
build
|
||||
snippet
|
||||
~
|
||||
==
|
||||
::
|
||||
|
@ -103,6 +103,7 @@
|
||||
%- pairs
|
||||
:~ title+s+title.book
|
||||
date-created+(time date-created.book)
|
||||
about+s+description.book
|
||||
num-notes+(numb ~(wyt by notes.book))
|
||||
num-unread+(numb (count-unread notes.book))
|
||||
==
|
||||
@ -113,6 +114,7 @@
|
||||
=, enjs:format
|
||||
%- pairs
|
||||
:~ title+s+title.book
|
||||
about+s+description.book
|
||||
date-created+(time date-created.book)
|
||||
num-notes+(numb ~(wyt by notes.book))
|
||||
num-unread+(numb (count-unread notes.book))
|
||||
@ -160,7 +162,7 @@
|
||||
author+s+(scot %p author.note)
|
||||
title+s+title.note
|
||||
date-created+(time date-created.note)
|
||||
build+(note-build-to-json build.note)
|
||||
snippet+s+snippet.note
|
||||
file+s+file.note
|
||||
num-comments+(numb ~(wyt by comments.note))
|
||||
comments+(comments-page comments.note 0 50)
|
||||
@ -191,7 +193,7 @@
|
||||
date-created+(time date-created.note)
|
||||
num-comments+(numb ~(wyt by comments.note))
|
||||
read+b+read.note
|
||||
:: XX snippet
|
||||
snippet+s+snippet.note
|
||||
==
|
||||
::
|
||||
++ notes-page
|
||||
|
@ -40,7 +40,8 @@
|
||||
last-edit=@da
|
||||
read=?
|
||||
file=@t
|
||||
build=(each manx tang)
|
||||
snippet=@t
|
||||
:: build=(each manx tang)
|
||||
comments=(map @da comment)
|
||||
==
|
||||
::
|
||||
|
41
pkg/interface/publish/package-lock.json
generated
41
pkg/interface/publish/package-lock.json
generated
@ -1835,7 +1835,8 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -1856,12 +1857,14 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -1876,17 +1879,20 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -2003,7 +2009,8 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -2015,6 +2022,7 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -2029,6 +2037,7 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -2036,12 +2045,14 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -2060,6 +2071,7 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -2140,7 +2152,8 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -2152,6 +2165,7 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -2237,7 +2251,8 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -2273,6 +2288,7 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -2292,6 +2308,7 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -2335,12 +2352,14 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -76,6 +76,20 @@ a {
|
||||
}
|
||||
}
|
||||
|
||||
.bg-light-green {
|
||||
background: rgba(42, 167, 121, 0.1);
|
||||
}
|
||||
|
||||
.NotebookButton {
|
||||
padding: 8px 12px;
|
||||
border-radius:2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.NotebookTab {
|
||||
padding: 16px 8px;
|
||||
}
|
||||
|
||||
.NewPost {
|
||||
width: 100%;
|
||||
height: calc(100% - 103px);
|
||||
@ -87,7 +101,6 @@ a {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.CodeMirror {
|
||||
padding: 12px;
|
||||
height: 100% !important;
|
||||
@ -153,3 +166,13 @@ a {
|
||||
[contenteditable]:focus {
|
||||
outline: 0px solid transparent;
|
||||
}
|
||||
|
||||
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
@ -6,9 +6,9 @@ import React, { Component } from 'react';
|
||||
export class About extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<p className="f8 lh-solid">
|
||||
|
||||
</p>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ export class Comments extends Component {
|
||||
this.commentChange = this.commentChange.bind(this);
|
||||
}
|
||||
commentSubmit(evt){
|
||||
console.log ("this works")
|
||||
let comment = {
|
||||
"new-comment": {
|
||||
who: this.props.ship.slice(1),
|
||||
|
@ -8,7 +8,7 @@ export class NewPost extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
body: "# Hello",
|
||||
body: '',
|
||||
title: '',
|
||||
submit: false,
|
||||
awaiting: null,
|
||||
@ -23,7 +23,7 @@ export class NewPost extends Component {
|
||||
let newNote = {
|
||||
"new-note": {
|
||||
who: this.props.host.slice(1),
|
||||
book: this.props.notebookName,
|
||||
book: this.props.book,
|
||||
note: stringToSymbol(this.state.title),
|
||||
title: this.state.title,
|
||||
body: this.state.body,
|
||||
@ -37,10 +37,15 @@ export class NewPost extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
window.api.fetchNotebook(this.props.ship, this.props.book);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.props.notebook.notes[this.state.awaiting]) {
|
||||
let notebook = this.props.notebooks[this.props.ship][this.props.book];
|
||||
if (notebook.notes[this.state.awaiting]) {
|
||||
let redirect =
|
||||
`/~publish/note/${this.props.host}/${this.props.notebookName}/${this.state.awaiting}`;
|
||||
`/~publish/note/${this.props.host}/${this.props.book}/${this.state.awaiting}`;
|
||||
this.props.history.push(redirect);
|
||||
}
|
||||
}
|
||||
@ -56,6 +61,8 @@ export class NewPost extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let notebook = this.props.notebooks[this.props.ship][this.props.book];
|
||||
|
||||
const options = {
|
||||
mode: 'markdown',
|
||||
theme: 'tlon',
|
||||
@ -77,11 +84,11 @@ export class NewPost extends Component {
|
||||
<div className="w-100 tl">
|
||||
<button disabled={!this.state.submit} style={submitStyle}
|
||||
onClick={this.postSubmit}>
|
||||
Publish To {this.props.notebook.title}
|
||||
Publish To {notebook.title}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input type="text"
|
||||
<input autoFocus type="text"
|
||||
style={{paddingBottom: 8, paddingTop: 24}}
|
||||
className="w-100"
|
||||
onChange={this.titleChange}
|
||||
|
@ -6,7 +6,6 @@ import { Link } from 'react-router-dom';
|
||||
export class NoteNavigation extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
console.log(props)
|
||||
}
|
||||
render() {
|
||||
|
||||
@ -16,10 +15,8 @@ export class NoteNavigation extends Component {
|
||||
let prevUrl = ''
|
||||
|
||||
if (this.props.next && this.props.prev) {
|
||||
console.log("both")
|
||||
nextUrl = `/~publish/note/${this.props.ship}/${this.props.book}/${this.props.next.id}`;
|
||||
prevUrl = `/~publish/note/${this.props.ship}/${this.props.book}/${this.props.prev.id}`;
|
||||
console.log(this.props.prev.prevId);
|
||||
nextComponent =
|
||||
<Link to={nextUrl} className="di flex-column tr w-100 pv6 bt bb b--gray3">
|
||||
<div className="f9 gray2 mb2">Next</div>
|
||||
@ -35,8 +32,6 @@ export class NoteNavigation extends Component {
|
||||
</Link>
|
||||
|
||||
} else if (this.props.prev) {
|
||||
console.log("prev")
|
||||
console.log(this.props.prev.prevId);
|
||||
prevUrl = `/~publish/note/${this.props.ship}/${this.props.book}/${this.props.prev.id}`;
|
||||
prevComponent =
|
||||
<Link to={prevUrl} className="di flex-column w-100 pv6 bt bb b--gray3">
|
||||
@ -44,9 +39,7 @@ export class NoteNavigation extends Component {
|
||||
<div className="f9 mb1">{this.props.prev.title}</div>
|
||||
<div className="f9 gray2">{this.props.prev.date}</div>
|
||||
</Link>
|
||||
console.log(prevComponent)
|
||||
} else if (this.props.next) {
|
||||
console.log("next")
|
||||
nextUrl = `/~publish/note/${this.props.ship}/${this.props.book}/${this.props.next.id}`;
|
||||
nextComponent =
|
||||
<Link to={nextUrl} className="di flex-column tr w-100 pv6 bt bb b--gray3">
|
||||
@ -57,9 +50,6 @@ console.log(prevComponent)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="flex mt4">
|
||||
{prevComponent}
|
||||
|
@ -11,7 +11,6 @@ import ReactMarkdown from 'react-markdown'
|
||||
export class Note extends Component {
|
||||
constructor(props){
|
||||
super(props);
|
||||
console.log(this.props);
|
||||
moment.updateLocale('en', {
|
||||
relativeTime: {
|
||||
past: function(input) {
|
||||
@ -33,36 +32,99 @@ export class Note extends Component {
|
||||
yy : '%d years',
|
||||
}
|
||||
});
|
||||
this.scrollElement = React.createRef();
|
||||
this.onScroll = this.onScroll.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
window.api.fetchNote(this.props.ship, this.props.book, this.props.note);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!(this.props.notebooks[this.props.ship]) ||
|
||||
!(this.props.notebooks[this.props.ship][this.props.book]) ||
|
||||
!(this.props.notebooks[this.props.ship][this.props.book].notes[this.props.note]) ||
|
||||
!(this.props.notebooks[this.props.ship][this.props.book].notes[this.props.note].file))
|
||||
{
|
||||
window.api.fetchNote(this.props.ship, this.props.book, this.props.note);
|
||||
}
|
||||
}
|
||||
|
||||
onScroll() {
|
||||
let notebook = this.props.notebooks[this.props.ship][this.props.book];
|
||||
let note = notebook.notes[this.props.note];
|
||||
|
||||
if (!note.comments) {
|
||||
return;
|
||||
}
|
||||
|
||||
let scrollTop = this.scrollElement.scrollTop;
|
||||
let clientHeight = this.scrollElement.clientHeight;
|
||||
let scrollHeight = this.scrollElement.scrollHeight;
|
||||
|
||||
let atBottom = false;
|
||||
if (scrollHeight - scrollTop - clientHeight < 40) {
|
||||
atBottom = true;
|
||||
}
|
||||
|
||||
let loadedComments = note.comments.length;
|
||||
let allComments = note["num-comments"];
|
||||
|
||||
let fullyLoaded = (loadedComments === allComments);
|
||||
|
||||
if (atBottom && !fullyLoaded) {
|
||||
window.api.fetchCommentsPage(this.props.ship,
|
||||
this.props.book, this.props.note, loadedComments, 30);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (!(this.props.notebooks[this.props.ship]) ||
|
||||
!(this.props.notebooks[this.props.ship][this.props.book]) ||
|
||||
!(this.props.notebooks[this.props.ship][this.props.book].notes[this.props.note]) ||
|
||||
!(this.props.notebooks[this.props.ship][this.props.book].notes[this.props.note].file))
|
||||
{
|
||||
window.api.fetchNote(this.props.ship, this.props.book, this.props.note);
|
||||
}
|
||||
this.onScroll();
|
||||
}
|
||||
|
||||
render() {
|
||||
let comments = this.props.notebook.notes[this.props.note].comments;
|
||||
let title = this.props.notebook.notes[this.props.note].title;
|
||||
let author = this.props.notebook.notes[this.props.note].author;
|
||||
let file = this.props.notebook.notes[this.props.note].file;
|
||||
let date = moment(this.props.notebook.notes[this.props.note]["date-created"]).fromNow();
|
||||
let notebook = this.props.notebooks[this.props.ship][this.props.book];
|
||||
let comments = notebook.notes[this.props.note].comments;
|
||||
let title = notebook.notes[this.props.note].title;
|
||||
let author = notebook.notes[this.props.note].author;
|
||||
let file = notebook.notes[this.props.note].file;
|
||||
let date = moment(notebook.notes[this.props.note]["date-created"]).fromNow();
|
||||
|
||||
if (!file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let newfile = file.slice(file.indexOf(';>')+2);
|
||||
let prevId = this.props.notebook.notes[this.props.note]["prev-note"];
|
||||
let nextId = this.props.notebook.notes[this.props.note]["next-note"];
|
||||
let prevId = notebook.notes[this.props.note]["prev-note"];
|
||||
let nextId = notebook.notes[this.props.note]["next-note"];
|
||||
|
||||
let prev = (prevId === null)
|
||||
? null
|
||||
: {
|
||||
id: prevId,
|
||||
title: this.props.notebook.notes[prevId].title,
|
||||
date: moment(this.props.notebook.notes[prevId]["date-created"]).fromNow()
|
||||
title: notebook.notes[prevId].title,
|
||||
date: moment(notebook.notes[prevId]["date-created"]).fromNow()
|
||||
}
|
||||
let next = (nextId === null)
|
||||
? null
|
||||
: {
|
||||
id: nextId,
|
||||
title: this.props.notebook.notes[nextId].title,
|
||||
date: moment(this.props.notebook.notes[nextId]["date-created"]).fromNow()
|
||||
title: notebook.notes[nextId].title,
|
||||
date: moment(notebook.notes[nextId]["date-created"]).fromNow()
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="h-100 overflow-container">
|
||||
<div className="h-100 overflow-container no-scrollbar"
|
||||
onScroll={this.onScroll}
|
||||
ref={(el) => {this.scrollElement = el}}>
|
||||
<div className="flex justify-center mt4 ph4 pb4">
|
||||
<div className="w-100 mw6">
|
||||
<div className="flex flex-column">
|
||||
@ -79,8 +141,10 @@ export class Note extends Component {
|
||||
next={next}
|
||||
ship={this.props.ship}
|
||||
book={this.props.book}/>
|
||||
<Comments ship={this.props.ship} book={this.props.book} note={this.props.note} comments={comments}/>
|
||||
|
||||
<Comments ship={this.props.ship}
|
||||
book={this.props.book}
|
||||
note={this.props.note}
|
||||
comments={comments}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,74 @@
|
||||
import React, { Component } from 'react';
|
||||
import moment from 'moment';
|
||||
import { Link } from 'react-router-dom';
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
|
||||
export class NotebookPosts extends Component {
|
||||
constructor(props){
|
||||
super(props);
|
||||
moment.updateLocale('en', {
|
||||
relativeTime: {
|
||||
past: function(input) {
|
||||
return input === 'just now'
|
||||
? input
|
||||
: input + ' ago'
|
||||
},
|
||||
s : 'just now',
|
||||
future : 'in %s',
|
||||
m : '1m',
|
||||
mm : '%dm',
|
||||
h : '1h',
|
||||
hh : '%dh',
|
||||
d : '1d',
|
||||
dd : '%dd',
|
||||
M : '1 month',
|
||||
MM : '%d months',
|
||||
y : '1 year',
|
||||
yy : '%d years',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let notes = [];
|
||||
|
||||
for (var i=0; i<this.props.list.length; i++) {
|
||||
let noteId = this.props.list[i];
|
||||
let note = this.props.notes[noteId];
|
||||
if (!note) {
|
||||
break;
|
||||
}
|
||||
let comment = "No Comments";
|
||||
if (note["num-comments"] == 1) {
|
||||
comment = "1 Comment";
|
||||
} else if (note["num-comments"] > 1) {
|
||||
comment = `${note["num-comments"]} Comments`;
|
||||
}
|
||||
let date = moment(note["date-created"]).fromNow();
|
||||
let url = `/~publish/note/${this.props.host}/${this.props.notebookName}/${noteId}`
|
||||
|
||||
notes.push(
|
||||
<Link key={i} to={url}>
|
||||
<div className="mv6">
|
||||
<div className="mb1">{note.title}</div>
|
||||
<p className="mb1">{note.snippet}</p>
|
||||
<div className="flex">
|
||||
<div className="mono gray2 mr3">{note.author}</div>
|
||||
<div className="gray2 mr3">{date}</div>
|
||||
<div className="gray2">{comment}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-col">
|
||||
{notes}
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default NotebookPosts
|
@ -1,27 +1,152 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Link, Switch, Route } from 'react-router-dom';
|
||||
import { NoteList } from './note-list';
|
||||
import { NotebookPosts } from './notebook-posts';
|
||||
import { About } from './about';
|
||||
import { Subscribers } from './subscribers';
|
||||
import { Settings } from './settings';
|
||||
|
||||
//TODO subcomponents for posts, subscribers, settings
|
||||
//
|
||||
//TODO props.view switch for which component to render
|
||||
//pass props.notebook, contacts to each component
|
||||
|
||||
//TODO ask for notebook if we don't have it
|
||||
//
|
||||
//TODO initialise notebook obj if no props.notebook
|
||||
|
||||
//TODO component bar above the rendered component
|
||||
//don't render settings if it's ours
|
||||
//current component is black, others gray2 (see Chat's tab bar for an example)
|
||||
//TODO subcomponent logic for subscribers, settings
|
||||
|
||||
export class Notebook extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
constructor(props){
|
||||
super(props);
|
||||
|
||||
this.onScroll = this.onScroll.bind(this);
|
||||
this.unsubscribe = this.unsubscribe.bind(this);
|
||||
}
|
||||
|
||||
onScroll() {
|
||||
let notebook = this.props.notebooks[this.props.ship][this.props.book];
|
||||
let scrollTop = this.scrollElement.scrollTop;
|
||||
let clientHeight = this.scrollElement.clientHeight;
|
||||
let scrollHeight = this.scrollElement.scrollHeight;
|
||||
|
||||
let atBottom = false;
|
||||
if (scrollHeight - scrollTop - clientHeight < 40) {
|
||||
atBottom = true;
|
||||
}
|
||||
let loadedNotes = Object.keys(notebook.notes).length;
|
||||
let allNotes = notebook["notes-by-date"].length;
|
||||
|
||||
let fullyLoaded = (loadedNotes === allNotes);
|
||||
|
||||
if (atBottom && !fullyLoaded) {
|
||||
window.api.fetchNotesPage(this.props.ship, this.props.book, loadedNotes, 30);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount(){
|
||||
window.api.fetchNotebook(this.props.ship, this.props.book);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.notebooks[this.props.ship][this.props.book].notes) {
|
||||
this.onScroll();
|
||||
}
|
||||
}
|
||||
|
||||
unsubscribe() {
|
||||
let action = {
|
||||
unsubscribe: {
|
||||
who: this.props.ship.slice(1),
|
||||
book: this.props.book,
|
||||
}
|
||||
}
|
||||
window.api.action("publish", "publish-action", action);
|
||||
this.props.history.push("/~publish");
|
||||
}
|
||||
|
||||
render() {
|
||||
let notebook = this.props.notebooks[this.props.ship][this.props.book];
|
||||
let tabStyles = {
|
||||
posts: "bb b--gray4 gray2 NotebookTab",
|
||||
about: "bb b--gray4 gray2 NotebookTab",
|
||||
// subscribers: "bb b--gray4 gray2 NotebookTab",
|
||||
// settings: "bb b--gray4 pr2 gray2 NotebookTab",
|
||||
}
|
||||
tabStyles[this.props.view] = "bb b--black black NotebookTab";
|
||||
|
||||
let inner = null;
|
||||
switch (this.props.view) {
|
||||
case "posts":
|
||||
let notesList = notebook["notes-by-date"] || [];
|
||||
let notes = notebook.notes || null;
|
||||
inner = <NotebookPosts notes={notes}
|
||||
list={notesList}
|
||||
host={this.props.ship}
|
||||
notebookName={this.props.book}/>
|
||||
break;
|
||||
case "about":
|
||||
inner = <p className="f8 lh-solid">{notebook.about}</p>
|
||||
break;
|
||||
// case "subscribers":
|
||||
// inner = <Subscribers/>
|
||||
// break;
|
||||
// case "settings":
|
||||
// inner = <Settings/>
|
||||
// break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
let base = `/~publish/notebook/${this.props.ship}/${this.props.book}`;
|
||||
let about = base + '/about';
|
||||
let subs = base + '/subscribers';
|
||||
let settings = base + '/settings';
|
||||
let newUrl = base + '/new';
|
||||
|
||||
let newPost = !(window.ship === this.props.ship.slice(1))
|
||||
// XX TODO check based on authorized writers instead
|
||||
? null
|
||||
: <Link to={newUrl} className="NotebookButton bg-light-green green2">
|
||||
New Post
|
||||
</Link>
|
||||
|
||||
let unsub = (window.ship === this.props.ship.slice(1))
|
||||
? null
|
||||
: <button onClick={this.unsubscribe}
|
||||
className="NotebookButton bg-white black ba b--black ml3">
|
||||
Unsubscribe
|
||||
</button>
|
||||
|
||||
return (
|
||||
<div className="center mw6 f9 h-100"
|
||||
style={{paddingLeft:16, paddingRight:16}}>
|
||||
<div className="h-100 overflow-container no-scrollbar"
|
||||
onScroll={this.onScroll}
|
||||
ref={(el) => {this.scrollElement = el}}>
|
||||
<div className="flex justify-between"
|
||||
style={{marginTop: 56, marginBottom: 32}}>
|
||||
<div className="flex-col">
|
||||
<div className="mb1">{notebook.title}</div>
|
||||
<span><span className="gray3 mr1">by</span>
|
||||
<span className="mono">
|
||||
{this.props.ship}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
{newPost}
|
||||
{unsub}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex" style={{marginBottom:24}}>
|
||||
<Link to={base} className={tabStyles.posts}>
|
||||
All Posts
|
||||
</Link>
|
||||
<Link to={about} className={tabStyles.about}>
|
||||
About
|
||||
</Link>
|
||||
<div className="bb b--gray4 gray2 NotebookTab" style={{flexGrow:1}}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{height:"calc(100% - 188px)"}} className="f9 lh-solid">
|
||||
{inner}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -5,8 +5,36 @@ export class Settings extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div className="flex flex-column mb8">
|
||||
<label for="name" className="f9">Share</label>
|
||||
<small id="name-desc" className="f9 mb2 gray3">Share a link to this notebook</small>
|
||||
<div className="flex">
|
||||
<input style={{flex: "1"}} id="name" placeholder="dopzod.arvo.network/4f5hsS" className="input-reset bt bl bb pa3 gray4" type="text" aria-describedby="name-desc"/>
|
||||
<button className="bt br bb pa3 b--gray4">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-column mb8">
|
||||
<label for="name" className="f9">Rename</label>
|
||||
<small id="name-desc" className="f9 mb2 gray3">Change the name of this notebook</small>
|
||||
<div className="flex">
|
||||
<input style={{flex: "1"}} id="name" placeholder="Notebook Name" className="input-reset ba pa3 gray4" type="text" aria-describedby="name-desc"/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-column">
|
||||
<label for="name" className="f9">Export</label>
|
||||
<small id="name-desc" className="f9 mb2 gray3">Change the name of this notebook</small>
|
||||
<button className="bg-black white pa3">
|
||||
<div className="flex justify-between">
|
||||
<div>Export Notebook</div>
|
||||
<div>↓</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,47 @@ export class Subscribers extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div className="flex flex-column">
|
||||
<div className="f9 gray2">Host</div>
|
||||
<div className="flex justify-between mt3">
|
||||
<div className="flex">
|
||||
<div className="f9 mono mr2">~fabled-faster</div>
|
||||
<div className="f9 gray2">Last active</div>
|
||||
</div>
|
||||
<div className="f9">Options ⌃</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-column">
|
||||
<div className="f9 gray2 mt6">Participants (read and write access)</div>
|
||||
<div className="f9 mt3">There are no paticipants in this notebook.</div>
|
||||
<div className="flex justify-between mt3">
|
||||
<div className="flex">
|
||||
<div className="f9 mono mr2">~fabled-faster</div>
|
||||
<div className="f9 gray2">Last active</div>
|
||||
</div>
|
||||
<div className="f9">Options ⌃</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-column">
|
||||
<div className="f9 gray2 mt6 mb3">Subscribers (read access only)</div>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex">
|
||||
<div className="f9 mono mr2">~fabled-faster</div>
|
||||
<div className="f9 gray2">Last active</div>
|
||||
</div>
|
||||
<div className="f9">Options ⌃</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-column">
|
||||
<div className="f9 gray2 mt6 mb3">Banned</div>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex">
|
||||
<div className="f9 mono mr2">~fabled-faster</div>
|
||||
<div className="f9 gray2">Last active</div>
|
||||
</div>
|
||||
<div className="f9">Options ⌃</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -87,9 +87,9 @@ export class Root extends Component {
|
||||
sidebarShown={true}
|
||||
notebooks={state.notebooks}>
|
||||
<NewPost
|
||||
notebook={state.notebooks[ship][notebook]}
|
||||
notebooks={state.notebooks}
|
||||
host={ship}
|
||||
notebookName={notebook}
|
||||
book={notebook}
|
||||
{...props}
|
||||
/>
|
||||
</Skeleton>
|
||||
@ -104,10 +104,11 @@ export class Root extends Component {
|
||||
sidebarShown={true}
|
||||
notebooks={state.notebooks}>
|
||||
<Notebook
|
||||
notebooks={state.notebooks}
|
||||
view={view}
|
||||
ship={ship}
|
||||
book={notebook}/>
|
||||
notebooks={state.notebooks}
|
||||
view={view}
|
||||
ship={ship}
|
||||
book={notebook}
|
||||
{...props}/>
|
||||
</Skeleton>
|
||||
)
|
||||
}
|
||||
@ -126,10 +127,10 @@ export class Root extends Component {
|
||||
sidebarShown={true}
|
||||
notebooks={state.notebooks}>
|
||||
<Note
|
||||
notebook={state.notebooks[ship][notebook]}
|
||||
book={notebook}
|
||||
ship={ship}
|
||||
note={note}/>
|
||||
notebooks={state.notebooks}
|
||||
book={notebook}
|
||||
ship={ship}
|
||||
note={note}/>
|
||||
</Skeleton>
|
||||
)
|
||||
}}/>
|
||||
|
@ -2,7 +2,6 @@ import _ from 'lodash';
|
||||
|
||||
export class PrimaryReducer {
|
||||
reduce(json, state){
|
||||
console.log("primaryReducer", json);
|
||||
switch(Object.keys(json)[0]){
|
||||
case "add-book":
|
||||
this.addBook(json["add-book"], state);
|
||||
|
@ -2,7 +2,6 @@ import _ from 'lodash';
|
||||
|
||||
export class ResponseReducer {
|
||||
reduce(json, state) {
|
||||
console.log("responseReducer", json);
|
||||
switch(json.type) {
|
||||
case "notebooks":
|
||||
this.handleNotebooks(json, state);
|
||||
@ -155,8 +154,30 @@ export class ResponseReducer {
|
||||
state.notebooks[json.host][json.notebook].notes[json.note])
|
||||
{
|
||||
if (state.notebooks[json.host][json.notebook].notes[json.note].comments) {
|
||||
state.notebooks[json.host][json.notebook].notes[json.note].comments
|
||||
.concat(json.data);
|
||||
json.data.forEach((val, i) => {
|
||||
let newKey = Object.keys(val)[0];
|
||||
let newDate = val[newKey]["date-created"]
|
||||
let oldComments = state.notebooks[json.host][json.notebook].notes[json.note].comments;
|
||||
let insertIdx = -1;
|
||||
|
||||
for (var j=0; j<oldComments.length; j++) {
|
||||
let oldKey = Object.keys(oldComments[j])[0];
|
||||
let oldDate = oldComments[j][oldKey]["date-created"];
|
||||
|
||||
if (oldDate === newDate) {
|
||||
break;
|
||||
} else if (oldDate < newDate) {
|
||||
insertIdx = j;
|
||||
} else if ((oldDate > newDate) &&
|
||||
(j === oldComments.length-1)){
|
||||
insertIdx = j+1;
|
||||
}
|
||||
}
|
||||
if (insertIdx !== -1) {
|
||||
state.notebooks[json.host][json.notebook].notes[json.note].comments
|
||||
.splice(insertIdx, 0, val);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
state.notebooks[json.host][json.notebook].notes[json.note].comments =
|
||||
json.data;
|
||||
|
Loading…
Reference in New Issue
Block a user