mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-01 11:33:41 +03:00
publish: sidebar redesign with grouped notebooks
This commit is contained in:
parent
afa38b1d2d
commit
e7a29dc628
@ -69,8 +69,8 @@ a {
|
||||
.db-ns {
|
||||
display: block;
|
||||
}
|
||||
.flex-basis-300-ns {
|
||||
flex-basis: 300px;
|
||||
.flex-basis-250-ns {
|
||||
flex-basis: 250px;
|
||||
}
|
||||
.h-100-m-40-ns {
|
||||
height: calc(100% - 40px);
|
||||
|
44
pkg/interface/publish/src/js/components/lib/group-item.js
Normal file
44
pkg/interface/publish/src/js/components/lib/group-item.js
Normal file
@ -0,0 +1,44 @@
|
||||
import React, { Component } from 'react';
|
||||
import { NotebookItem } from './notebook-item';
|
||||
|
||||
export class GroupItem extends Component {
|
||||
render() {
|
||||
const { props, state } = this;
|
||||
let association = !!props.association ? props.association : {};
|
||||
|
||||
let title = association["app-path"] ? association["app-path"] : "Unmanaged Notebooks";
|
||||
if (association.metadata && association.metadata.title) {
|
||||
title = association.metadata.title !== ""
|
||||
? association.metadata.title : title;
|
||||
}
|
||||
|
||||
let groupedBooks = !!props.groupedBooks ? props.groupedBooks : [];
|
||||
let first = (props.index === 0) ? "pt1" : "pt4";
|
||||
|
||||
let notebookItems = groupedBooks.map((each, i) => {
|
||||
let unreads = props.notebooks[each]["num-unread"] || 0;
|
||||
let title = each.substr(1);
|
||||
if (props.notebooks[each].title) {
|
||||
title = (props.notebooks[each].title !== "")
|
||||
? props.notebooks[each].title : title;
|
||||
}
|
||||
return (
|
||||
<NotebookItem
|
||||
key={i}
|
||||
unreadCount={unreads}
|
||||
title={title}
|
||||
path={each}
|
||||
selected={(props.path === each)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
return (
|
||||
<div className={first}>
|
||||
<p className="f9 ph4 fw6 gray3">{title}</p>
|
||||
{notebookItems}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default GroupItem;
|
@ -1,51 +1,23 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Route, Link } from 'react-router-dom';
|
||||
import { cite } from '../../lib/util';
|
||||
|
||||
export class NotebookItem extends Component {
|
||||
render() {
|
||||
let { props } = this;
|
||||
|
||||
let selectedClass = (props.selected) ? "bg-gray5 bg-gray1-d b--gray4 b--gray2-d" : "b--gray4 b--gray2-d";
|
||||
|
||||
let postCount = (props.total === 1)
|
||||
? `${props.total} post` : `${props.total} posts`;
|
||||
let selectedClass = (props.selected) ? "bg-gray5 bg-gray1-d c-default" : "pointer hover-bg-gray5 hover-bg-gray1-d";
|
||||
|
||||
let unread = (props.unreadCount > 0)
|
||||
? `${props.unreadCount} unread` : "";
|
||||
|
||||
let notebookContacts = (props.contactsPath in props.contacts)
|
||||
? props.contacts[props.contactsPath] : {};
|
||||
let contact = !!(props.author.substr(1) in notebookContacts)
|
||||
? notebookContacts[props.author.substr(1)] : false;
|
||||
|
||||
let name = props.author;
|
||||
if (contact) {
|
||||
name = (contact.nickname.length > 0)
|
||||
? contact.nickname : props.author;
|
||||
}
|
||||
|
||||
if (name === props.author) {
|
||||
name = cite(props.author);
|
||||
}
|
||||
? <p className="dib f9 fr"><span className="dib white bg-gray3 bg-gray2-d fw6 br1" style={{ padding: "1px 5px" }}>
|
||||
{props.unreadCount}
|
||||
</span></p> : <span/>;
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={"/~publish/notebook/" + props.path}>
|
||||
<div className={"w-100 v-mid f9 pl4 bb " + selectedClass}>
|
||||
<p className="f9 pt1">{props.title}</p>
|
||||
<p className="f9 gray2">by
|
||||
<span className={"pl1 " + (contact.nickname ? null : "mono")}
|
||||
title={props.author}>
|
||||
{name}
|
||||
</span>
|
||||
</p>
|
||||
<p className="f9 pb1">
|
||||
{postCount}
|
||||
<span className="green2 ml3">
|
||||
{unread}
|
||||
</span>
|
||||
</p>
|
||||
<div className={"w-100 v-mid f9 ph4 pv1 " + selectedClass}>
|
||||
<p className="dib f9">{props.title}</p>
|
||||
{unread}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
|
@ -1,95 +1,11 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Route, Link } from 'react-router-dom';
|
||||
import { Dropdown } from './dropdown';
|
||||
import { NotebookItem } from './notebook-item';
|
||||
import { SidebarInvite } from './sidebar-invite';
|
||||
import { Welcome } from './welcome';
|
||||
import { GroupItem } from './group-item';
|
||||
import { alphabetiseAssociations } from '../../lib/util';
|
||||
|
||||
export class Sidebar extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
sort: "oldest",
|
||||
sortedBooks: new Map()
|
||||
}
|
||||
this.sort = this.sort.bind(this);
|
||||
this.sortChange = this.sortChange.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.sort();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if ((prevState.sort !== this.state.sort) || (prevProps !== this.props)) {
|
||||
this.sort();
|
||||
}
|
||||
}
|
||||
|
||||
sort() {
|
||||
let { props, state } = this;
|
||||
let notebooks = new Map();
|
||||
Object.keys(props.notebooks).map(host => {
|
||||
Object.keys(props.notebooks[host]).map(notebook => {
|
||||
let title = `${host}/${notebook}`;
|
||||
notebooks.set(title, props.notebooks[host][notebook])
|
||||
})
|
||||
});
|
||||
switch (state.sort) {
|
||||
case "oldest":
|
||||
notebooks = new Map(
|
||||
[...notebooks.entries()].sort(
|
||||
(a, b) => {
|
||||
if ((a[1]) && (b[1])) {
|
||||
return a[1]["date-created"] - b[1]["date-created"]
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
break;
|
||||
case "newest":
|
||||
notebooks = new Map(
|
||||
[...notebooks.entries()].sort(
|
||||
(a, b) => {
|
||||
if ((a[1]) && (b[1])) {
|
||||
return b[1]["date-created"] - a[1]["date-created"]
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
break;
|
||||
case "alphabetical":
|
||||
notebooks = new Map(
|
||||
[...notebooks.entries()].sort((a, b) => {
|
||||
if ((a[1]) && (b[1])) {
|
||||
return a[1]["title"].toLowerCase().localeCompare(
|
||||
b[1]["title"].toLowerCase()
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
break;
|
||||
case "reverseAlphabetical":
|
||||
notebooks = new Map(
|
||||
[...notebooks.entries()].sort((a, b) => {
|
||||
if ((a[1]) && (b[1])) {
|
||||
return b[1]["title"].toLowerCase().localeCompare(
|
||||
a[1]["title"].toLowerCase()
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.setState({ sortedBooks: notebooks });
|
||||
}
|
||||
|
||||
sortChange(event) {
|
||||
this.setState({sort: event.target.value});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { props, state } = this;
|
||||
let activeClasses = (props.active === "sidebar") ? " " : "dn-s ";
|
||||
@ -111,24 +27,70 @@ export class Sidebar extends Component {
|
||||
key={i} />
|
||||
)
|
||||
});
|
||||
let associations = !!props.associations ? alphabetiseAssociations(props.associations.contacts) : {};
|
||||
|
||||
let notebookItems = [...state.sortedBooks].map(([path, book]) => {
|
||||
let selected = (props.path === path);
|
||||
let author = path.split("/")[0];
|
||||
return (
|
||||
<NotebookItem
|
||||
key={book.title}
|
||||
title={book.title}
|
||||
author={author}
|
||||
contacts={props.contacts}
|
||||
contactsPath={book["subscribers-group-path"]}
|
||||
path={path}
|
||||
total={book["num-notes"]}
|
||||
unreadCount={book["num-unread"]}
|
||||
selected={selected}
|
||||
/>
|
||||
);
|
||||
})
|
||||
let notebooks = {};
|
||||
Object.keys(props.notebooks).map(host => {
|
||||
Object.keys(props.notebooks[host]).map(notebook => {
|
||||
let title = `${host}/${notebook}`;
|
||||
notebooks[title] = props.notebooks[host][notebook];
|
||||
})
|
||||
});
|
||||
|
||||
let groupedNotebooks = {};
|
||||
Object.keys(notebooks).map(book => {
|
||||
if (notebooks[book]["subscribers-group-path"].startsWith("/~/")) {
|
||||
if (groupedNotebooks["/~/"]) {
|
||||
let array = groupedNotebooks["/~/"];
|
||||
array.push(book);
|
||||
groupedNotebooks["/~/"] = array;
|
||||
} else {
|
||||
groupedNotebooks["/~/"] = [book];
|
||||
};
|
||||
};
|
||||
let path = !!notebooks[book]["subscribers-group-path"]
|
||||
? notebooks[book]["subscribers-group-path"] : book;
|
||||
if (path in associations) {
|
||||
if (groupedNotebooks[path]) {
|
||||
let array = groupedNotebooks[path];
|
||||
array.push[book];
|
||||
groupedNotebooks[path] = array;
|
||||
} else {
|
||||
groupedNotebooks[path] = [book];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let groupedItems = Object.keys(associations)
|
||||
.map((each, i) => {
|
||||
let books = groupedNotebooks[each];
|
||||
if (books.length === 0) return;
|
||||
if (groupedNotebooks["/~/"] && groupedNotebooks["/~/"].length !== 0) {
|
||||
i = i + 1;
|
||||
}
|
||||
return(
|
||||
<GroupItem
|
||||
key={i}
|
||||
index={i}
|
||||
association={associations[each]}
|
||||
groupedBooks={books}
|
||||
notebooks={notebooks}
|
||||
path={props.path}
|
||||
/>
|
||||
)
|
||||
})
|
||||
if (groupedNotebooks["/~/"] && groupedNotebooks["/~/"].length !== 0) {
|
||||
groupedItems.unshift(
|
||||
<GroupItem
|
||||
key={"/~/"}
|
||||
index={0}
|
||||
association={"/~/"}
|
||||
groupedBooks={groupedNotebooks["/~/"]}
|
||||
notebooks={notebooks}
|
||||
path={props.path}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -136,53 +98,24 @@ export class Sidebar extends Component {
|
||||
"bn br-m br-l br-xl b--gray4 b--gray2-d lh-copy h-100 " +
|
||||
"flex-shrink-0 pt3 pt0-m pt0-l pt0-xl relative " +
|
||||
"overflow-y-hidden " + activeClasses +
|
||||
(hiddenClasses ? "flex-basis-100-s flex-basis-300-ns" : "dn")
|
||||
(hiddenClasses ? "flex-basis-100-s flex-basis-250-ns" : "dn")
|
||||
}>
|
||||
<a className="db dn-m dn-l dn-xl f9 pb3 pl3" href="/">
|
||||
⟵ Landscape
|
||||
</a>
|
||||
<div className="w-100 f9">
|
||||
<Link to="/~publish/new" className="green2 mr4 f9 pl4 pt4 dib">
|
||||
<Link to="/~publish/new" className="green2 pa4 f9 dib">
|
||||
New Notebook
|
||||
</Link>
|
||||
<Link to="/~publish/join" className="f9 gray2">
|
||||
Join Notebook
|
||||
</Link>
|
||||
<div className="pl2 pv2 bb b--gray4 b--gray2-d">
|
||||
<Dropdown
|
||||
width="16rem"
|
||||
align="left"
|
||||
options={[
|
||||
{
|
||||
cls: "white-d w-100 tl pointer db ph2 pv3 hover-bg-gray4 hover-bg-gray1-d bg-transparent",
|
||||
txt: "Oldest",
|
||||
action: () => {this.setState({sort: "oldest"})}
|
||||
},
|
||||
{
|
||||
cls: "white-d w-100 tl pointer db ph2 pv3 hover-bg-gray4 hover-bg-gray1-d bg-transparent",
|
||||
txt: "Newest",
|
||||
action: () => {this.setState({sort: "newest"})}
|
||||
},
|
||||
{
|
||||
cls: "white-d w-100 tl pointer db ph2 pv3 hover-bg-gray4 hover-bg-gray1-d bg-transparent",
|
||||
txt: "A -> Z",
|
||||
action: () => {this.setState({sort: "alphabetical"})}
|
||||
},
|
||||
{
|
||||
cls: "white-d w-100 tl pointer db ph2 pv3 hover-bg-gray4 hover-bg-gray1-d bg-transparent",
|
||||
txt: "Z -> A",
|
||||
action: () => {this.setState({sort: "reverseAlphabetical"})}
|
||||
}
|
||||
]}
|
||||
buttonText="Sort By"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="overflow-y-auto pb1"
|
||||
style={{height: "calc(100% - 82px)"}}>
|
||||
<Welcome notebooks={props.notebooks}/>
|
||||
{sidebarInvites}
|
||||
{notebookItems}
|
||||
{groupedItems}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -38,6 +38,7 @@ export class Root extends Component {
|
||||
spinner={state.spinner}
|
||||
invites={state.invites}
|
||||
notebooks={state.notebooks}
|
||||
associations={associations}
|
||||
contacts={contacts}>
|
||||
<div className={`h-100 w-100 overflow-x-hidden flex flex-column
|
||||
bg-white bg-gray0-d dn db-ns`}>
|
||||
@ -62,6 +63,7 @@ export class Root extends Component {
|
||||
spinner={state.spinner}
|
||||
invites={state.invites}
|
||||
notebooks={state.notebooks}
|
||||
associations={associations}
|
||||
contacts={contacts}>
|
||||
<NewScreen
|
||||
associations={associations.contacts}
|
||||
@ -87,6 +89,7 @@ export class Root extends Component {
|
||||
spinner={state.spinner}
|
||||
invites={state.invites}
|
||||
notebooks={state.notebooks}
|
||||
associations={associations}
|
||||
contacts={contacts}>
|
||||
<JoinScreen
|
||||
notebooks={state.notebooks}
|
||||
@ -124,6 +127,7 @@ export class Root extends Component {
|
||||
spinner={state.spinner}
|
||||
invites={state.invites}
|
||||
notebooks={state.notebooks}
|
||||
associations={associations}
|
||||
contacts={contacts}
|
||||
path={path}>
|
||||
<NewPost
|
||||
@ -147,6 +151,7 @@ export class Root extends Component {
|
||||
spinner={state.spinner}
|
||||
invites={state.invites}
|
||||
notebooks={state.notebooks}
|
||||
associations={associations}
|
||||
contacts={contacts}
|
||||
path={path}>
|
||||
<Notebook
|
||||
@ -191,6 +196,7 @@ export class Root extends Component {
|
||||
spinner={state.spinner}
|
||||
invites={state.invites}
|
||||
notebooks={state.notebooks}
|
||||
associations={associations}
|
||||
contacts={contacts}
|
||||
path={path}>
|
||||
<EditPost
|
||||
@ -214,6 +220,7 @@ export class Root extends Component {
|
||||
spinner={state.spinner}
|
||||
invites={state.invites}
|
||||
notebooks={state.notebooks}
|
||||
associations={associations}
|
||||
contacts={contacts}
|
||||
path={path}>
|
||||
<Note
|
||||
@ -236,4 +243,4 @@ export class Root extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default Root
|
||||
export default Root;
|
@ -30,6 +30,7 @@ export class Skeleton extends Component {
|
||||
contacts={props.contacts}
|
||||
path={props.path}
|
||||
invites={props.invites}
|
||||
associations={props.associations}
|
||||
/>
|
||||
<div className={"h-100 w-100 relative white-d flex-auto " + rightPanelHide} style={{
|
||||
flexGrow: 1,
|
||||
|
@ -90,4 +90,26 @@ export function cite(ship) {
|
||||
return shortened;
|
||||
}
|
||||
return `~${patp}`;
|
||||
}
|
||||
|
||||
export function alphabetiseAssociations(associations) {
|
||||
let result = {};
|
||||
Object.keys(associations).sort((a, b) => {
|
||||
let aName = a.substr(1);
|
||||
let bName = b.substr(1);
|
||||
if (a.metadata && a.metadata.title) {
|
||||
aName = a.metadata.title !== ""
|
||||
? a.metadata.title
|
||||
: a.substr(1);
|
||||
}
|
||||
if (b.metadata && b.metadata.title) {
|
||||
bName = b.metadata.title !== ""
|
||||
? b.metadata.title
|
||||
: b.substr(1);
|
||||
}
|
||||
return aName.toLowerCase().localeCompare(bName.toLowerCase());
|
||||
}).map((each) => {
|
||||
result[each] = associations[each];
|
||||
})
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue
Block a user