publish: componentise spinner as part of layout

This commit pulls the spinner out of the header bar -- and
reincorporates it as a component that hooks into local state when
awaiting a new prop, or disabling an input.
This commit is contained in:
Matilde Park 2020-03-31 19:59:11 -04:00 committed by Matilde Park
parent a21294f02e
commit 48c9bdc702
16 changed files with 72 additions and 81 deletions

View File

Before

Width:  |  Height:  |  Size: 679 B

After

Width:  |  Height:  |  Size: 679 B

View File

@ -130,15 +130,6 @@ class UrbitApi {
});
}
setSpinner(boolean) {
store.handleEvent({
type: "local",
data: {
'spinner': boolean
}
});
}
setSelected(selected) {
store.handleEvent({
type: "local",

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react'
import { CommentItem } from './comment-item';
import { dateToDa } from '/lib/util';
import { Spinner } from './icons/icon-spinner';
export class Comments extends Component {
constructor(props){
@ -49,11 +50,9 @@ export class Comments extends Component {
this.setState({pending: pendingState});
this.textArea.value = '';
window.api.setSpinner(true);
this.setState({commentBody: ""});
this.setState({commentBody: "", disabled: true});
let submit = window.api.action("publish", "publish-action", comment);
submit.then(() => {
window.api.setSpinner(false);
this.setState({ disabled: false });
})
}
@ -105,7 +104,7 @@ export class Comments extends Component {
return (
<div>
<div className="mv8">
<div className="mv8 relative">
<div>
<textarea style={{resize:'vertical'}}
ref={(el) => {this.textArea = el}}
@ -130,6 +129,7 @@ export class Comments extends Component {
className={commentClass}>
Add comment
</button>
<Spinner text="Posting comment..." awaiting={this.state.disabled} classes="absolute bottom-0 right-0 pb2"/>
</div>
{pendingArray}
{commentArray}

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { SidebarSwitcher } from './icons/icon-sidebar-switch';
import { Spinner } from './icons/icon-spinner';
import { Route, Link } from 'react-router-dom';
import { Controlled as CodeMirror } from 'react-codemirror2';
import { dateToDa } from '/lib/util';
@ -11,7 +12,8 @@ export class EditPost extends Component {
super(props);
this.state = {
body: '',
submit: false
submit: false,
awaiting: false
}
this.postSubmit = this.postSubmit.bind(this);
this.bodyChange = this.bodyChange.bind(this);
@ -48,11 +50,11 @@ export class EditPost extends Component {
body: state.body
}
}
window.api.setSpinner(true);
this.setState({awaiting: true});
window.api.action("publish", "publish-action", editNote).then(() => {
let editIndex = props.location.pathname.indexOf("/edit");
let noteHref = props.location.pathname.slice(0, editIndex);
window.api.setSpinner(false);
this.setState({awaiting: false});
props.history.push(noteHref);
});
}
@ -125,6 +127,7 @@ export class EditPost extends Component {
onBeforeChange={(e, d, v) => this.bodyChange(e, d, v)}
onChange={(editor, data, value) => {}}
/>
<Spinner text="Editing post..." awaiting={this.state.awaiting} classes="absolute bottom-1 right-1 ba b--gray1-d pa2"/>
</div>
</div>
</div>

View File

@ -7,15 +7,6 @@ export class HeaderBar extends Component {
let popout = window.location.href.includes("popout/")
? "dn" : "dn db-m db-l db-xl";
// let spinner = !!this.props.spinner
// ? this.props.spinner : false;
// let spinnerClasses = "";
// if (spinner === true) {
// spinnerClasses = "spin-active";
// }
let invites = (this.props.invites && this.props.invites.contacts)
? this.props.invites.contacts
: {};

View File

@ -1,15 +0,0 @@
import React, { Component } from 'react';
export class IconHome extends Component {
render() {
let classes = !!this.props.classes ? this.props.classes : "";
return (
<img className={"invert-d " + classes}
src="/~publish/Home.png"
width={16}
height={16} />
);
}
}

View File

@ -0,0 +1,25 @@
import React, { Component } from 'react';
export class Spinner extends Component {
render() {
let classes = !!this.props.classes ? this.props.classes : "";
let text = !!this.props.text ? this.props.text : "";
let awaiting = !!this.props.awaiting ? this.props.awaiting : false;
if (awaiting) {
return (
<div className={classes + " z-2 bg-white bg-gray0-d white-d"}>
<img className="invert-d spin-active v-mid"
src="/~publish/Spinner.png"
width={16}
height={16} />
<p className="dib f9 ml2 v-mid">{text}</p>
</div>
);
}
else {
return null;
}
}
}

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react'
import classnames from 'classnames';
import { Route, Link } from 'react-router-dom';
import { Spinner } from './icons/icon-spinner';
import urbitOb from 'urbit-ob';
export class JoinScreen extends Component {
@ -8,7 +9,7 @@ export class JoinScreen extends Component {
super(props);
this.state = {
book: '/',
book: '',
error: false,
awaiting: null,
disable: false
@ -36,6 +37,7 @@ export class JoinScreen extends Component {
let notebook = book[1];
if ((ship in this.props.notebooks) &&
(notebook in this.props.notebooks[ship])) {
this.setState({disable: false, book: "/"});
this.props.history.push(`/~publish/notebook/${ship}/${notebook}`)
}
}
@ -91,13 +93,11 @@ export class JoinScreen extends Component {
}
// TODO: askHistory setting
window.api.setSpinner(true);
this.setState({disable: true});
window.api.action("publish","publish-action", actionData).catch((err) => {
console.log(err)
}).then(() => {
this.setState({awaiting: text, disable: false, book: ""})
window.api.setSpinner(false);
this.setState({awaiting: text})
});
}
@ -152,7 +152,9 @@ export class JoinScreen extends Component {
style={{
resize: 'none',
}}
onChange={this.bookChange} />
onChange={this.bookChange}
value={this.state.book}
/>
{errElem}
<br />
<button
@ -160,10 +162,11 @@ export class JoinScreen extends Component {
onClick={this.onClickJoin.bind(this)}
className={joinClasses}
>Join Notebook</button>
<Spinner awaiting={this.state.disable} classes="mt4" text="Joining notebook..." />
</div>
</div>
);
}
}
export default JoinScreen
export default JoinScreen;

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react'
import { SidebarSwitcher } from './icons/icon-sidebar-switch';
import { Spinner } from './icons/icon-spinner';
import { Route, Link } from 'react-router-dom';
import { Controlled as CodeMirror } from 'react-codemirror2'
import { dateToDa, stringToSymbol } from '/lib/util';
@ -35,7 +36,6 @@ export class NewPost extends Component {
}
}
window.api.setSpinner(true);
this.setState({ disabled: true });
window.api.action("publish", "publish-action", newNote).then(() => {
this.setState({ awaiting: newNote["new-note"].note, disabled: false });
@ -57,7 +57,6 @@ export class NewPost extends Component {
componentDidUpdate(prevProps, prevState) {
let notebook = this.props.notebooks[this.props.ship][this.props.book];
if (notebook.notes[this.state.awaiting]) {
window.api.setSpinner(false);
let popout = (this.props.popout) ? "popout/" : "";
let redirect =
`/~publish/${popout}note/${this.props.ship}/${this.props.book}/${this.state.awaiting}`;
@ -151,6 +150,7 @@ export class NewPost extends Component {
onBeforeChange={(e, d, v) => this.bodyChange(e, d, v)}
onChange={(editor, data, value) => {}}
/>
<Spinner text="Creating post..." awaiting={this.state.disabled} classes="absolute bottom-1 right-1 ba b--gray1-d pa2" />
</div>
</div>
</div>

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { InviteSearch } from './invite-search';
import { Spinner } from './icons/icon-spinner';
import { Route, Link } from 'react-router-dom';
import { uuid, isPatTa, deSig, stringToSymbol } from "/lib/util";
import urbitOb from 'urbit-ob';
@ -30,7 +31,6 @@ export class NewScreen extends Component {
const { props, state } = this;
if (props.notebooks && (("~" + window.ship) in props.notebooks)) {
if (state.awaiting in props.notebooks["~" + window.ship]) {
props.api.setSpinner(false);
let notebook = `/~${window.ship}/${state.awaiting}`;
props.history.push("/~publish/notebook" + notebook);
}
@ -93,10 +93,8 @@ export class NewScreen extends Component {
group: groupInfo
}
}
props.api.setSpinner(true);
this.setState({awaiting: bookId, disabled: true}, () => {
props.api.action("publish", "publish-action", action).then(() => {
props.api.setSpinner(false);
});
});
}
@ -135,7 +133,7 @@ export class NewScreen extends Component {
Notebook must have a valid name.
</span>
);
}
}
return (
<div
@ -202,6 +200,7 @@ export class NewScreen extends Component {
className={createClasses}>
Create Notebook
</button>
<Spinner awaiting={this.state.awaiting} classes="mt3" text="Creating notebook..."/>
</div>
</div>
);

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react';
import { Route, Link } from 'react-router-dom';
import { SidebarSwitcher } from './icons/icon-sidebar-switch';
import { Spinner } from './icons/icon-spinner';
import { Comments } from './comments';
import { NoteNavigation } from './note-navigation';
import moment from 'moment';
@ -8,8 +9,11 @@ import ReactMarkdown from 'react-markdown';
import { cite } from '../../lib/util';
export class Note extends Component {
constructor(props){
constructor(props) {
super(props);
this.state = {
deleting: false
}
moment.updateLocale('en', {
relativeTime: {
past: function(input) {
@ -119,10 +123,9 @@ export class Note extends Component {
}
let popout = (props.popout) ? "popout/" : "";
let baseUrl = `/~publish/${popout}notebook/${props.ship}/${props.book}`;
window.api.setSpinner(true);
this.setState({deleting: true});
window.api.action("publish", "publish-action", deleteAction)
.then(() => {
window.api.setSpinner(false);
props.history.push(baseUrl);
});
}
@ -251,6 +254,7 @@ export class Note extends Component {
comments={comments}
contacts={props.contacts}
/>
<Spinner text="Deleting post..." awaiting={this.state.deleting} classes="absolute bottom-1 right-1 ba b--gray1-d pa2" />
</div>
</div>
</div>

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { writeText } from '../../lib/util';
import { Spinner } from './icons/icon-spinner';
export class Settings extends Component {
constructor(props){
@ -8,7 +9,8 @@ export class Settings extends Component {
title: "",
description: "",
comments: false,
disabled: false
disabled: false,
type: "Editing"
}
this.deleteNotebook = this.deleteNotebook.bind(this);
this.changeTitle = this.changeTitle.bind(this);
@ -55,8 +57,7 @@ export class Settings extends Component {
}
changeComments() {
this.setState({comments: !this.state.comments}, (() => {
window.api.setSpinner(true);
this.setState({comments: !this.state.comments, disabled: true}, (() => {
window.api.action("publish", "publish-action", {
"edit-book": {
book: this.props.book,
@ -66,7 +67,7 @@ export class Settings extends Component {
group: null
}
}).then(() => {
window.api.setSpinner(false);
this.setState({disabled: false});
})
}));
}
@ -77,9 +78,8 @@ export class Settings extends Component {
book: this.props.book
}
}
window.api.setSpinner(true);
this.setState({ disabled: true, type: "Deleting" });
window.api.action("publish", "publish-action", action).then(() => {
window.api.setSpinner(false);
this.props.history.push("/~publish");
});
}
@ -138,7 +138,6 @@ export class Settings extends Component {
disabled={this.state.disabled}
onBlur={() => {
this.setState({ disabled: true });
window.api.setSpinner(true);
window.api
.action("publish", "publish-action", {
"edit-book": {
@ -151,7 +150,6 @@ export class Settings extends Component {
})
.then(() => {
this.setState({ disabled: false })
window.api.setSpinner(false);
});
}}
/>
@ -168,7 +166,6 @@ export class Settings extends Component {
onChange={this.changeDescription}
onBlur={() => {
this.setState({ disabled: true });
window.api.setSpinner(true);
window.api
.action("publish", "publish-action", {
"edit-book": {
@ -181,7 +178,6 @@ export class Settings extends Component {
})
.then(() => {
this.setState({ disabled: false });
window.api.setSpinner(false);
});
}}
/>
@ -198,6 +194,11 @@ export class Settings extends Component {
Subscribers may comment when enabled
</p>
</div>
<Spinner
awaiting={this.state.disabled}
classes="absolute right-1 bottom-1 pa2 ba b--black b--gray0-d white-d"
text={`${this.state.type} notebook...`}
/>
</div>
);
} else {

View File

@ -19,6 +19,11 @@ export class Root extends Component {
store.setStateHandler(this.setState.bind(this));
}
componentDidMount() {
//preload spinner asset
new Image().src = "/~publish/Spinner.png";
}
render() {
const { props, state } = this;
@ -36,7 +41,6 @@ export class Root extends Component {
active={"sidebar"}
rightPanelHide={true}
sidebarShown={true}
spinner={state.spinner}
invites={state.invites}
notebooks={state.notebooks}
associations={associations}
@ -62,7 +66,6 @@ export class Root extends Component {
active={"rightPanel"}
rightPanelHide={false}
sidebarShown={state.sidebarShown}
spinner={state.spinner}
invites={state.invites}
notebooks={state.notebooks}
associations={associations}
@ -89,7 +92,6 @@ export class Root extends Component {
active={"rightPanel"}
rightPanelHide={false}
sidebarShown={state.sidebarShown}
spinner={state.spinner}
invites={state.invites}
notebooks={state.notebooks}
associations={associations}
@ -128,7 +130,6 @@ export class Root extends Component {
active={"rightPanel"}
rightPanelHide={false}
sidebarShown={state.sidebarShown}
spinner={state.spinner}
invites={state.invites}
notebooks={state.notebooks}
associations={associations}
@ -153,7 +154,6 @@ export class Root extends Component {
active={"rightPanel"}
rightPanelHide={false}
sidebarShown={state.sidebarShown}
spinner={state.spinner}
invites={state.invites}
notebooks={state.notebooks}
associations={associations}
@ -199,7 +199,6 @@ export class Root extends Component {
active={"rightPanel"}
rightPanelHide={false}
sidebarShown={state.sidebarShown}
spinner={state.spinner}
invites={state.invites}
notebooks={state.notebooks}
selectedGroups={selectedGroups}
@ -224,7 +223,6 @@ export class Root extends Component {
active={"rightPanel"}
rightPanelHide={false}
sidebarShown={state.sidebarShown}
spinner={state.spinner}
invites={state.invites}
notebooks={state.notebooks}
associations={associations}

View File

@ -21,7 +21,6 @@ export class Skeleton extends Component {
return (
<div className={"absolute h-100 w-100 " + popoutWindow}>
<HeaderBar
spinner={props.spinner}
invites={props.invites}
associations={props.associations} />
<div className={`cf w-100 h-100 flex ` + popoutBorder}>

View File

@ -20,7 +20,6 @@ export class ResponseReducer {
break;
case "local":
this.sidebarToggle(json, state);
this.setSpinner(json, state);
this.setSelected(json, state);
break;
default:
@ -205,12 +204,6 @@ export class ResponseReducer {
}
}
setSpinner(json, state) {
let data = _.has(json.data, 'spinner', false);
if (data) {
state.spinner = json.data.spinner;
}
}
setSelected(json, state) {
let data = _.has(json.data, 'selected', false);
if (data) {

View File

@ -18,7 +18,6 @@ class Store {
permissions: {},
invites: {},
selectedGroups: [],
spinner: false,
sidebarShown: true
}