Merge pull request #2284 from urbit/mp/os1/publish/popout-note

publish: collapsable sidebar logic, popout for all notebook/note views
This commit is contained in:
matildepark 2020-02-13 12:40:10 -05:00 committed by GitHub
commit d40a37b7cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 184 additions and 147 deletions

View File

@ -96,7 +96,7 @@ a {
.NewPost { .NewPost {
width: 100%; width: 100%;
height: calc(100% - 103px); height: calc(100% - 115px);
display: flex; display: flex;
padding-top: 8px; padding-top: 8px;
} }

View File

@ -1,7 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';
import _ from 'lodash'; import _ from 'lodash';
import { uuid } from '/lib/util'; import { store } from './store'
class UrbitApi { class UrbitApi {
setAuthTokens(authTokens) { setAuthTokens(authTokens) {
@ -125,11 +123,10 @@ class UrbitApi {
sidebarBoolean = false; sidebarBoolean = false;
} }
store.handleEvent({ store.handleEvent({
type: { type: "local",
local: { data: {
'sidebarToggle': sidebarBoolean 'sidebarToggle': sidebarBoolean
} }
}
}); });
} }

View File

@ -1,16 +0,0 @@
import React, { Component } from 'react';
//TODO "About" subcomponent of Notebook.js
//Fill in with "description" from props.notebook
export class About extends Component {
render() {
return (
<p className="f8 lh-solid">
</p>
)
}
}
export default About

View File

@ -9,7 +9,7 @@ export class SidebarSwitcher extends Component {
: "dib-m dib-l dib-xl"; : "dib-m dib-l dib-xl";
return ( return (
<div className="pt2"> <div className={"absolute left-1 top-1 " + popoutSwitcher}>
<a <a
className="pointer flex-shrink-0" className="pointer flex-shrink-0"
onClick={() => { onClick={() => {

View File

@ -1,4 +1,6 @@
import React, { Component } from 'react' import React, { Component } from 'react'
import { SidebarSwitcher } from './icons/icon-sidebar-switch';
import { Route, Link } from 'react-router-dom';
import { Controlled as CodeMirror } from 'react-codemirror2' import { Controlled as CodeMirror } from 'react-codemirror2'
import { dateToDa, stringToSymbol } from '/lib/util'; import { dateToDa, stringToSymbol } from '/lib/util';
@ -44,8 +46,9 @@ export class NewPost extends Component {
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState) {
let notebook = this.props.notebooks[this.props.ship][this.props.book]; let notebook = this.props.notebooks[this.props.ship][this.props.book];
if (notebook.notes[this.state.awaiting]) { if (notebook.notes[this.state.awaiting]) {
let popout = (this.props.popout) ? "popout/" : "";
let redirect = let redirect =
`/~publish/note/${this.props.ship}/${this.props.book}/${this.state.awaiting}`; `/~publish/${popout}note/${this.props.ship}/${this.props.book}/${this.state.awaiting}`;
this.props.history.push(redirect); this.props.history.push(redirect);
} }
} }
@ -61,7 +64,9 @@ export class NewPost extends Component {
} }
render() { render() {
let notebook = this.props.notebooks[this.props.ship][this.props.book]; const { props, state } = this;
let notebook = props.notebooks[props.ship][props.book];
const options = { const options = {
mode: 'markdown', mode: 'markdown',
@ -74,39 +79,66 @@ export class NewPost extends Component {
let date = dateToDa(new Date()).slice(1, -10); let date = dateToDa(new Date()).slice(1, -10);
let submitStyle = (this.state.submit) let submitStyle = (state.submit)
? { color: '#2AA779', cursor: "pointer" } ? { color: '#2AA779', cursor: "pointer" }
: { color: '#B1B2B3', cursor: "auto" }; : { color: '#B1B2B3', cursor: "auto" };
let hrefIndex = props.location.pathname.indexOf("/notebook/");
let publishsubStr = props.location.pathname.substr(hrefIndex)
let popoutHref = `/~publish/popout${publishsubStr}`;
let hiddenOnPopout = (props.popout)
? "" : "dib-m dib-l dib-xl";
return ( return (
<div className="center mw7 f9 h-100"> <div className="f9 h-100 relative">
<div style={{padding: 16}} className="flex-col"> <div className="w-100 tl pv4 flex justify-center">
<div className="w-100 tl"> <SidebarSwitcher
<button disabled={!this.state.submit} style={submitStyle} sidebarShown={props.sidebarShown}
popout={props.popout}
/>
<button
className="v-mid w-100 mw7 tl pl4 h1"
disabled={!state.submit}
style={submitStyle}
onClick={this.postSubmit}> onClick={this.postSubmit}>
Publish To {notebook.title} Publish To {notebook.title}
</button> </button>
<Link
className={"dn absolute right-1 top-1 " + hiddenOnPopout}
to={popoutHref}
target="_blank">
<img src="/~publish/popout.png"
height={16}
width={16}
/>
</Link>
</div> </div>
<div className="overflow-container mw7 center">
<input autoFocus type="text" <div style={{ padding: 16 }}>
style={{paddingBottom: 8, paddingTop: 24}} <input
autoFocus
type="text"
style={{ paddingBottom: 8 }}
className="w-100" className="w-100"
onChange={this.titleChange} onChange={this.titleChange}
placeholder="New Post" /> placeholder="New Post"
/>
<div style={{color:'#7F7F7F'}}>{date}</div> <div style={{ color: "#7F7F7F" }}>{date}</div>
</div> </div>
<div className="NewPost"> <div className="NewPost">
<CodeMirror <CodeMirror
value={this.state.body} value={state.body}
options={options} options={options}
onBeforeChange={(e, d, v) => this.bodyChange(e, d, v)} onBeforeChange={(e, d, v) => this.bodyChange(e, d, v)}
onChange={(editor, data, value) => {}} onChange={(editor, data, value) => {}}
/> />
</div> </div>
</div> </div>
) </div>
);
} }
} }

View File

@ -1,16 +0,0 @@
import React, { Component } from 'react'
//TODO render props from notebook.js as a div of individual notes in the
//notebook
export class NoteItem extends Component {
render() {
return (
<div>
</div>
)
}
}
export default NoteItem

View File

@ -1,15 +0,0 @@
import React, { Component } from 'react';
import { NoteItem } from './note-item';
//TODO map a list of NoteItems
export class NoteList extends Component {
render() {
return (
<div>
</div>
)
}
}
export default NoteList

View File

@ -14,9 +14,11 @@ export class NoteNavigation extends Component {
let nextUrl = '' let nextUrl = ''
let prevUrl = '' let prevUrl = ''
let popout = (this.props.popout) ? "popout/" : "";
if (this.props.next && this.props.prev) { if (this.props.next && this.props.prev) {
nextUrl = `/~publish/note/${this.props.ship}/${this.props.book}/${this.props.next.id}`; nextUrl = `/~publish/${popout}note/${this.props.ship}/${this.props.book}/${this.props.next.id}`;
prevUrl = `/~publish/note/${this.props.ship}/${this.props.book}/${this.props.prev.id}`; prevUrl = `/~publish/${popout}note/${this.props.ship}/${this.props.book}/${this.props.prev.id}`;
nextComponent = nextComponent =
<Link to={nextUrl} className="di flex-column tr w-100 pv6 bt bb b--gray3"> <Link to={nextUrl} className="di flex-column tr w-100 pv6 bt bb b--gray3">
<div className="f9 gray2 mb2">Next</div> <div className="f9 gray2 mb2">Next</div>
@ -32,7 +34,7 @@ export class NoteNavigation extends Component {
</Link> </Link>
} else if (this.props.prev) { } else if (this.props.prev) {
prevUrl = `/~publish/note/${this.props.ship}/${this.props.book}/${this.props.prev.id}`; prevUrl = `/~publish/${popout}note/${this.props.ship}/${this.props.book}/${this.props.prev.id}`;
prevComponent = prevComponent =
<Link to={prevUrl} className="di flex-column w-100 pv6 bt bb b--gray3"> <Link to={prevUrl} className="di flex-column w-100 pv6 bt bb b--gray3">
<div className="f9 gray2 mb2">Previous</div> <div className="f9 gray2 mb2">Previous</div>
@ -40,7 +42,7 @@ export class NoteNavigation extends Component {
<div className="f9 gray2">{this.props.prev.date}</div> <div className="f9 gray2">{this.props.prev.date}</div>
</Link> </Link>
} else if (this.props.next) { } else if (this.props.next) {
nextUrl = `/~publish/note/${this.props.ship}/${this.props.book}/${this.props.next.id}`; nextUrl = `/~publish/${popout}note/${this.props.ship}/${this.props.book}/${this.props.next.id}`;
nextComponent = nextComponent =
<Link to={nextUrl} className="di flex-column tr w-100 pv6 bt bb b--gray3"> <Link to={nextUrl} className="di flex-column tr w-100 pv6 bt bb b--gray3">
<div className="f9 gray2 mb2">Next</div> <div className="f9 gray2 mb2">Next</div>

View File

@ -1,4 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Route, Link } from 'react-router-dom';
import { SidebarSwitcher } from './icons/icon-sidebar-switch';
import { Comments } from './comments'; import { Comments } from './comments';
import { NoteNavigation } from './note-navigation'; import { NoteNavigation } from './note-navigation';
import moment from 'moment'; import moment from 'moment';
@ -129,18 +131,49 @@ export class Note extends Component {
date: moment(notebook.notes[nextId]["date-created"]).fromNow() date: moment(notebook.notes[nextId]["date-created"]).fromNow()
} }
let popout = (props.popout) ? "popout/" : "";
let hrefIndex = props.location.pathname.indexOf("/note/");
let publishsubStr = props.location.pathname.substr(hrefIndex);
let popoutHref = `/~publish/popout${publishsubStr}`;
let hiddenOnPopout = props.popout ? "" : "dib-m dib-l dib-xl";
let baseUrl = `/~publish/${popout}notebook/${props.ship}/${props.book}`;
return ( return (
<div className="h-100 overflow-container no-scrollbar" <div
className="h-100 no-scrollbar"
onScroll={this.onScroll} onScroll={this.onScroll}
ref={(el) => {this.scrollElement = el}}> ref={el => {
<div className="flex justify-center mt4 ph4 pb4"> this.scrollElement = el;
<div className="w-100 mw6"> }}>
<div className="h-100 flex flex-column items-center mt4 ph4 pb4">
<div className="w-100 flex justify-center pb6">
<SidebarSwitcher
popout={props.popout}
sidebarShown={props.sidebarShown}
/>
<Link className="f9 w-100 mw6 tl" to={baseUrl}>
{"<- Notebook index"}
</Link>
<Link
to={popoutHref}
className={"dn absolute right-1 top-1 " + hiddenOnPopout}
target="_blank">
<img src="/~publish/popout.png"
height={16}
width={16}
/>
</Link>
</div>
<div className="w-100 mw6 overflow-container">
<div className="flex flex-column"> <div className="flex flex-column">
<div className="f9 mb1">{title}</div> <div className="f9 mb1">{title}</div>
<div className="flex mb6"> <div className="flex mb6">
<div className={"di f9 gray2 mr2 " + <div
((name === author) ? "mono" : "")}> className={
"di f9 gray2 mr2 " + (name === author ? "mono" : "")
}>
{name} {name}
</div> </div>
<div className="di f9 gray2">{date}</div> <div className="di f9 gray2">{date}</div>
@ -150,11 +183,14 @@ export class Note extends Component {
<ReactMarkdown source={newfile} /> <ReactMarkdown source={newfile} />
</div> </div>
<NoteNavigation <NoteNavigation
popout={props.popout}
prev={prev} prev={prev}
next={next} next={next}
ship={props.ship} ship={props.ship}
book={props.book}/> book={props.book}
<Comments ship={props.ship} />
<Comments
ship={props.ship}
book={props.book} book={props.book}
note={props.note} note={props.note}
comments={comments} comments={comments}
@ -163,7 +199,7 @@ export class Note extends Component {
</div> </div>
</div> </div>
</div> </div>
) );
} }
} }

View File

@ -55,7 +55,8 @@ export class NotebookPosts extends Component {
comment = `${note["num-comments"]} Comments`; comment = `${note["num-comments"]} Comments`;
} }
let date = moment(note["date-created"]).fromNow(); let date = moment(note["date-created"]).fromNow();
let url = `/~publish/note/${props.host}/${props.notebookName}/${noteId}` let popout = (props.popout) ? "popout/" : "";
let url = `/~publish/${popout}note/${props.host}/${props.notebookName}/${noteId}`
notes.push( notes.push(
<Link key={i} to={url}> <Link key={i} to={url}>

View File

@ -1,10 +1,10 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Link, Switch, Route } from 'react-router-dom'; import { Link, Switch, Route } from 'react-router-dom';
import { NoteList } from './note-list'; import { SidebarSwitcher } from './icons/icon-sidebar-switch';
import { NotebookPosts } from './notebook-posts'; import { NotebookPosts } from './notebook-posts';
import { About } from './about';
import { Subscribers } from './subscribers'; import { Subscribers } from './subscribers';
import { Settings } from './settings'; import { Settings } from './settings';
import Sidebar from './sidebar';
//TODO subcomponent logic for subscribers, settings //TODO subcomponent logic for subscribers, settings
@ -71,6 +71,13 @@ export class Notebook extends Component {
render() { render() {
const { props } = this; const { props } = this;
// popout logic
let hrefIndex = props.location.pathname.indexOf("/notebook/");
let publishsubStr = props.location.pathname.substr(hrefIndex);
let popoutHref = `/~publish/popout${publishsubStr}`;
let hiddenOnPopout = props.popout ? "" : "dib-m dib-l dib-xl";
let notebook = props.notebooks[props.ship][props.book]; let notebook = props.notebooks[props.ship][props.book];
let tabStyles = { let tabStyles = {
@ -87,6 +94,7 @@ export class Notebook extends Component {
let notesList = notebook["notes-by-date"] || []; let notesList = notebook["notes-by-date"] || [];
let notes = notebook.notes || null; let notes = notebook.notes || null;
inner = <NotebookPosts notes={notes} inner = <NotebookPosts notes={notes}
popout={props.popout}
list={notesList} list={notesList}
host={props.ship} host={props.ship}
notebookName={props.book} notebookName={props.book}
@ -106,6 +114,7 @@ export class Notebook extends Component {
break; break;
} }
// displaying nicknames, sigil colors for contacts
let contact = !!(props.ship.substr(1) in props.contacts) let contact = !!(props.ship.substr(1) in props.contacts)
? props.contacts[props.ship.substr(1)] : false; ? props.contacts[props.ship.substr(1)] : false;
let name = props.ship; let name = props.ship;
@ -114,7 +123,8 @@ export class Notebook extends Component {
? contact.nickname : props.ship; ? contact.nickname : props.ship;
} }
let base = `/~publish/notebook/${props.ship}/${props.book}`; let popout = (props.popout) ? "popout/" : "";
let base = `/~publish/${popout}notebook/${props.ship}/${props.book}`;
let about = base + '/about'; let about = base + '/about';
let subs = base + '/subscribers'; let subs = base + '/subscribers';
let settings = base + '/settings'; let settings = base + '/settings';
@ -142,20 +152,37 @@ export class Notebook extends Component {
<div <div
className="center mw6 f9 h-100" className="center mw6 f9 h-100"
style={{ paddingLeft: 16, paddingRight: 16 }}> style={{ paddingLeft: 16, paddingRight: 16 }}>
<SidebarSwitcher
popout={props.popout}
sidebarShown={props.sidebarShown}
/>
<div className="w-100 dn-m dn-l dn-xl inter pt4 pb6 f9">
<Link to="/~publish">{"<- All Notebooks"}</Link>
</div>
<Link
className={"dn absolute right-1 top-1 " + hiddenOnPopout}
to={popoutHref}
target="_blank"
>
<img src="/~publish/popout.png"
height={16}
width={16}
/>
</Link>
<div <div
className="h-100 overflow-container no-scrollbar" className="h-100 pt0 pt8-m pt8-l pt8-xl overflow-container no-scrollbar"
onScroll={this.onScroll} onScroll={this.onScroll}
ref={el => { ref={el => {
this.scrollElement = el; this.scrollElement = el;
}}> }}>
<div <div
className="flex justify-between" className="flex justify-between"
style={{ marginTop: 56, marginBottom: 32 }}> style={{ marginBottom: 32 }}>
<div className="flex-col"> <div className="flex-col">
<div className="mb1">{notebook.title}</div> <div className="mb1">{notebook.title}</div>
<span> <span>
<span className="gray3 mr1">by</span> <span className="gray3 mr1">by</span>
<span className={(props.ship === name) ? "mono" : ""}> <span className={props.ship === name ? "mono" : ""}>
{name} {name}
</span> </span>
</span> </span>

View File

@ -146,7 +146,7 @@ export class Sidebar extends Component {
"overflow-y-hidden " + activeClasses + "overflow-y-hidden " + activeClasses +
(hiddenClasses ? "flex-basis-100-s flex-basis-30-ns" : "dn") (hiddenClasses ? "flex-basis-100-s flex-basis-30-ns" : "dn")
}> }>
<a className="db dn-m dn-l dn-xl f8 pb3 pl3" href="/"> <a className="db dn-m dn-l dn-xl f9 pb3 pl3" href="/">
Landscape Landscape
</a> </a>
<div className="w-100"> <div className="w-100">
@ -156,11 +156,6 @@ export class Sidebar extends Component {
<Link to="/~publish/join" className="f9 gray2"> <Link to="/~publish/join" className="f9 gray2">
Join Notebook Join Notebook
</Link> </Link>
<h2
className={`f8 pt3 pr4 pb3 pl3 black c-default bb b--gray4 mb2
dn-m dn-l dn-xl`}>
Your Notebooks
</h2>
<div className="dropdown relative bb b--gray4"> <div className="dropdown relative bb b--gray4">
<select <select
style={{ WebkitAppearance: "none" }} style={{ WebkitAppearance: "none" }}

View File

@ -55,7 +55,7 @@ export class Root extends Component {
popout={false} popout={false}
active={"rightPanel"} active={"rightPanel"}
rightPanelHide={false} rightPanelHide={false}
sidebarShown={true} sidebarShown={state.sidebarShown}
invites={state.invites} invites={state.invites}
notebooks={state.notebooks} notebooks={state.notebooks}
contacts={contacts}> contacts={contacts}>
@ -75,7 +75,7 @@ export class Root extends Component {
popout={false} popout={false}
active={"rightPanel"} active={"rightPanel"}
rightPanelHide={false} rightPanelHide={false}
sidebarShown={true} sidebarShown={state.sidebarShown}
invites={state.invites} invites={state.invites}
notebooks={state.notebooks} notebooks={state.notebooks}
contacts={contacts}> contacts={contacts}>
@ -83,12 +83,14 @@ export class Root extends Component {
</Skeleton> </Skeleton>
) )
}}/> }}/>
<Route exact path="/~publish/(popout)?/notebook/:ship/:notebook/:view?" <Route exact path="/~publish/:popout?/notebook/:ship/:notebook/:view?"
render={ (props) => { render={ (props) => {
let view = (props.match.params.view) let view = (props.match.params.view)
? props.match.params.view ? props.match.params.view
: "posts"; : "posts";
let popout = !!props.match.params.popout || false;
let ship = props.match.params.ship || ""; let ship = props.match.params.ship || "";
let notebook = props.match.params.notebook || ""; let notebook = props.match.params.notebook || "";
@ -102,10 +104,10 @@ export class Root extends Component {
if (view === "new") { if (view === "new") {
return ( return (
<Skeleton <Skeleton
popout={false} popout={popout}
active={"rightPanel"} active={"rightPanel"}
rightPanelHide={false} rightPanelHide={false}
sidebarShown={true} sidebarShown={state.sidebarShown}
invites={state.invites} invites={state.invites}
notebooks={state.notebooks} notebooks={state.notebooks}
contacts={contacts} contacts={contacts}
@ -114,6 +116,8 @@ export class Root extends Component {
notebooks={state.notebooks} notebooks={state.notebooks}
ship={ship} ship={ship}
book={notebook} book={notebook}
sidebarShown={state.sidebarShown}
popout={popout}
{...props} {...props}
/> />
</Skeleton> </Skeleton>
@ -122,10 +126,10 @@ export class Root extends Component {
else { else {
return ( return (
<Skeleton <Skeleton
popout={false} popout={popout}
active={"rightPanel"} active={"rightPanel"}
rightPanelHide={false} rightPanelHide={false}
sidebarShown={true} sidebarShown={state.sidebarShown}
invites={state.invites} invites={state.invites}
notebooks={state.notebooks} notebooks={state.notebooks}
contacts={contacts} contacts={contacts}
@ -137,19 +141,23 @@ export class Root extends Component {
book={notebook} book={notebook}
groups={state.groups} groups={state.groups}
contacts={notebookContacts} contacts={notebookContacts}
sidebarShown={state.sidebarShown}
popout={popout}
{...props} {...props}
/> />
</Skeleton> </Skeleton>
); );
} }
}}/> }}/>
<Route exact path="/~publish/(popout)?/note/:ship/:notebook/:note" <Route exact path="/~publish/:popout?/note/:ship/:notebook/:note"
render={ (props) => { render={ (props) => {
let ship = props.match.params.ship || ""; let ship = props.match.params.ship || "";
let notebook = props.match.params.notebook || ""; let notebook = props.match.params.notebook || "";
let path = `${ship}/${notebook}` let path = `${ship}/${notebook}`
let note = props.match.params.note || ""; let note = props.match.params.note || "";
let popout = !!props.match.params.popout || false;
let bookGroupPath = let bookGroupPath =
state.notebooks[ship][notebook]["subscribers-group-path"]; state.notebooks[ship][notebook]["subscribers-group-path"];
let notebookContacts = (bookGroupPath in state.contacts) let notebookContacts = (bookGroupPath in state.contacts)
@ -157,10 +165,10 @@ export class Root extends Component {
return ( return (
<Skeleton <Skeleton
popout={false} popout={popout}
active={"rightPanel"} active={"rightPanel"}
rightPanelHide={false} rightPanelHide={false}
sidebarShown={true} sidebarShown={state.sidebarShown}
invites={state.invites} invites={state.invites}
notebooks={state.notebooks} notebooks={state.notebooks}
contacts={contacts} contacts={contacts}
@ -171,6 +179,9 @@ export class Root extends Component {
contacts={notebookContacts} contacts={notebookContacts}
ship={ship} ship={ship}
note={note} note={note}
sidebarShown={state.sidebarShown}
popout={popout}
{...props}
/> />
</Skeleton> </Skeleton>
); );

View File

@ -33,7 +33,7 @@ export class Skeleton extends Component {
path={props.path} path={props.path}
invites={props.invites} invites={props.invites}
/> />
<div className={"h-100 w-100 overflow-container " + rightPanelHide} style={{ <div className={"h-100 w-100 relative " + rightPanelHide} style={{
flexGrow: 1, flexGrow: 1,
}}> }}>
{props.children} {props.children}

View File

@ -1,17 +0,0 @@
import _ from 'lodash';
export class LocalReducer {
reduce(json, state) {
let data = _.get(json, 'local', false);
if (data) {
this.sidebarToggle(data, state);
}
}
sidebarToggle(obj, state) {
let data = _.has(obj, 'sidebarToggle', false);
if (data) {
state.sidebarShown = obj.sidebarToggle;
}
}
}

View File

@ -188,9 +188,9 @@ export class ResponseReducer {
} }
sidebarToggle(json, state) { sidebarToggle(json, state) {
let data = _.has(json, 'sidebarToggle', false); let data = _.has(json.data, 'sidebarToggle', false);
if (data) { if (data) {
state.sidebarShown = json.type.local.sidebarToggle; state.sidebarShown = json.data.sidebarToggle;
} }
} }

View File

@ -13,7 +13,7 @@ class Store {
permissions: {}, permissions: {},
invites: {}, invites: {},
spinner: false, spinner: false,
sidebarShown: false, sidebarShown: true
} }
this.initialReducer = new InitialReducer(); this.initialReducer = new InitialReducer();