link: incorporate group filter component

This commit is contained in:
Matilde Park 2020-03-27 14:57:25 -04:00
parent 8335ae059c
commit 9cb90405c1
8 changed files with 309 additions and 50 deletions

View File

@ -247,6 +247,16 @@ class UrbitApi {
})
}
setSelected(selected) {
store.handleEvent({
data: {
local: {
selected: selected
}
}
})
}
}
export let api = new UrbitApi();

View File

@ -29,9 +29,6 @@ export class ChannelsSidebar extends Component {
let groupPath = !!props.associations.link[path] ?
props.associations.link[path]["group-path"] : "";
console.log(path);
console.log(groupPath);
if (groupPath.startsWith("/~/")) {
if (groupedChannels["/~/"]) {
let array = groupedChannels["/~/"];
@ -52,12 +49,23 @@ export class ChannelsSidebar extends Component {
}
});
let selectedGroups = !!props.selectedGroups ? props.selectedGroups : [];
let i = -1;
const groupedItems = Object.keys(associations).map((each) => {
const groupedItems = Object.keys(associations)
.filter((each) => {
if (selectedGroups.length === 0) {
return true;
};
let selectedPaths = selectedGroups.map((e) => {
return e[0];
});
return selectedPaths.includes(each);
})
.map((each) => {
let channels = groupedChannels[each];
if (!channels || channels.length === 0) return;
i++;
if (groupedChannels["/~/"] && groupedChannels["/~/"].length !== 0) {
if ((selectedGroups.length === 0) && groupedChannels["/~/"] && groupedChannels["/~/"].length !== 0) {
i++;
}
@ -73,7 +81,7 @@ export class ChannelsSidebar extends Component {
/>
)
});
if (groupedChannels["/~/"] && groupedChannels["/~/"].length !== 0) {
if ((selectedGroups.length === 0) && groupedChannels["/~/"] && groupedChannels["/~/"].length !== 0) {
groupedItems.unshift(
<GroupItem
key={"/~/"}

View File

@ -0,0 +1,231 @@
import React, { Component } from 'react'
export class GroupFilter extends Component {
constructor(props) {
super(props);
this.state = {
open: false,
selected: [],
groups: [],
searchTerm: "",
results: []
}
this.toggleOpen = this.toggleOpen.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
this.groupIndex = this.groupIndex.bind(this);
this.search = this.search.bind(this);
this.addGroup = this.addGroup.bind(this);
this.deleteGroup = this.deleteGroup.bind(this);
}
componentDidMount() {
document.addEventListener('mousedown', this.handleClickOutside);
this.groupIndex();
let selected = localStorage.getItem("urbit-selectedGroups");
if (selected) {
this.setState({selected: JSON.parse(selected)}, (() => {
window.api.setSelected(this.state.selected);
}))
}
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClickOutside);
}
componentDidUpdate(prevProps) {
if (prevProps !== this.props) {
this.groupIndex();
}
}
handleClickOutside(evt) {
if ((this.dropdown && !this.dropdown.contains(evt.target))
&& (this.toggleButton && !this.toggleButton.contains(evt.target))) {
this.setState({ open: false });
}
}
toggleOpen() {
this.setState({open: !this.state.open});
}
groupIndex() {
const { props, state } = this;
let index = [];
let associations = !!props.associations ? props.associations.contacts : {};
index = Object.keys(associations).map((each) => {
let eachGroup = [];
eachGroup.push(each);
let name = each;
if (associations[each].metadata) {
name = (associations[each].metadata.title !== "")
? associations[each].metadata.title : name;
}
eachGroup.push(name);
return eachGroup;
});
this.setState({groups: index})
}
search(evt) {
this.setState({searchTerm: evt.target.value});
let term = evt.target.value.toLowerCase();
if (term.length < 3) {
return this.setState({results: []})
}
let groupMatches = [];
groupMatches = this.state.groups.filter(e => {
return (e[0].includes(term) || e[1].includes(term));
});
this.setState({results: groupMatches});
}
addGroup(group) {
let selected = this.state.selected;
if (!(group in selected)) {
selected.push(group);
}
this.setState({
searchTerm: "",
selected: selected,
results: []
}, (() => {
window.api.setSelected(this.state.selected);
localStorage.setItem("urbit-selectedGroups", JSON.stringify(this.state.selected));
}))
}
deleteGroup(group) {
let selected = this.state.selected;
selected = selected.filter(e => {
return e !== group;
});
this.setState({selected: selected}, (() => {
window.api.setSelected(this.state.selected);
localStorage.setItem("urbit-selectedGroups", JSON.stringify(this.state.selected));
}))
}
render() {
const { props, state } = this;
let currentGroup = "All Groups";
if (state.selected.length > 0) {
let titles = state.selected.map((each) => {
return each[1];
})
currentGroup = titles.join(" + ");
}
let buttonOpened = (state.open)
? "bg-gray5 bg-gray1-d white-d" : "hover-bg-gray5 hover-bg-gray1-d white-d";
let dropdownClass = (state.open)
? "absolute db z-2 bg-white bg-gray0-d white-d ba b--gray3 b--gray1-d"
: "dn";
let inviteCount = (props.invites && props.invites.length > 0)
? <template className="dib fr">
<p className="dib bg-green2 bg-gray2-d white fw6 ph1 br1 v-mid" style={{ marginBottom: 2 }}>
{props.invites.length}
</p>
<span className="dib v-mid ml1">
<img
className="v-mid"
src="/~launch/img/Chevron.png"
style={{ height: 16, width: 16, paddingBottom: 1 }}
/>
</span>
</template>
: <template className="dib fr">
<span className="dib v-top ml1">
<img className="v-mid"
src="/~launch/img/Chevron.png"
style={{ height: 16, width: 16, paddingBottom: 1 }}
/>
</span>
</template>;
let selectedGroups = <div/>
let searchResults = <div/>
if (state.results.length > 0) {
let groupResults = state.results.map((group => {
return(
<li
key={group[0]}
className="tl list white-d f9 pv2 ph3 pointer hover-bg-gray4 hover-bg-gray1-d inter" onClick={() => this.addGroup(group)}>
<span className="mix-blend-diff white">{(group[1]) ? group[1] : group[0]}</span>
</li>
)
}))
searchResults = (
<div className={"tl absolute bg-white bg-gray0-d white-d pv3 z-1 w-100 ba b--gray4 b--white-d overflow-y-scroll"} style={{maxWidth: "15.67rem", maxHeight: "8rem"}}>
<p className="f9 tl gray2 ph3 pb2">Groups</p>
{groupResults}
</div>
)
}
if (state.selected.length > 0) {
let allSelected = this.state.selected.map((each) => {
let name = each[1];
return(
<span
key={each[0]}
className={"f9 inter black pa2 bg-gray5 bg-gray1-d " +
"ba b--gray4 b--gray2-d white-d dib mr2 mt2 c-default"}
>
{name}
<span
className="white-d ml3 mono pointer"
onClick={e => this.deleteGroup(each)}>
x
</span>
</span>
)
})
selectedGroups = (
<div className={
"f9 gray2 bb bl br b--gray3 b--gray2-d bg-gray0-d " +
"white-d pa3 db w-100 inter bg-gray5 lh-solid tl"
} style={{width: 253}}>
{allSelected}
</div>
)
}
return (
<div className="ml1 dib">
<div className={buttonOpened}
onClick={() => this.toggleOpen()}
ref={(el) => this.toggleButton = el}>
<p className="dib f9 pointer pa1 mw5 truncate v-mid">{currentGroup}</p>
</div>
<div className={dropdownClass}
style={{ maxHeight: "24rem", width: 285 }}
ref={(el) => { this.dropdown = el }}>
<p className="tc bb b--gray3 b--gray1-d gray3 pv4 f9">Group Select and Filter</p>
<a href="/~groups" className="ma4 bg-gray5 bg-gray1-d f9 tl pa1 br1 db no-underline" style={{paddingLeft: "6.5px", paddingRight: "6.5px"}}>Manage all Groups
{inviteCount}
</a>
<p className="pt4 gray3 f9 tl mh4">Filter Groups</p>
<div className="relative pb6 w-100 ph4 pt2">
<input className="ba b--gray3 white-d bg-gray0-d inter w-100 f9 pa2" style={{boxSizing: "border-box"}} placeholder="Group name..."
onChange={this.search}
value={state.searchTerm}
/>
{searchResults}
{selectedGroups}
</div>
</div>
</div>
)
}
}
export default GroupFilter;

View File

@ -1,57 +1,47 @@
import React, { Component } from "react";
import { IconHome } from "/components/lib/icons/icon-home";
import { GroupFilter } from "./group-filter";
import { Sigil } from "/components/lib/icons/sigil";
import { cite } from "../../lib/util";
export class HeaderBar extends Component {
constructor(props) {
super(props);
}
render() {
let popout = window.location.href.includes("popout/")
? "dn"
: "dn db-m db-l db-xl";
? "dn" : "dn db-m db-l db-xl";
let title = document.title === "Home" ? "" : document.title;
// let spinner = !!this.props.spinner
// ? this.props.spinner : false;
let spinner = !!this.props.spinner ? this.props.spinner : false;
// let spinnerClasses = "";
let spinnerClasses = "";
// if (spinner === true) {
// spinnerClasses = "spin-active";
// }
if (spinner === true) {
spinnerClasses = "spin-active";
}
let invites = (this.props.invites && this.props.invites.contacts)
? this.props.invites.contacts
: {};
return (
<div
className={"bg-white bg-gray0-d w-100 justify-between relative tc pt3 " + popout}
className={
"bg-white bg-gray0-d w-100 justify-between relative tc pt3 " + popout
}
style={{ height: 40 }}>
<a
className="dib gray2 f9 inter absolute left-0 white-d"
href="/"
style={{ top: 14 }}>
<IconHome classes={spinnerClasses} />
<span className="ml2 v-top lh-title" style={{ paddingTop: 3 }}>
Home
</span>
</a>
<span
className="f9 inter dib white-d"
style={{
verticalAlign: "text-top",
paddingTop: 3
}}>
{title}
</span>
<div className="absolute right-0 lh-copy" style={{ top: 8 }}>
<Sigil
ship={"~" + window.ship}
classes="mix-blend-diff v-mid"
size={16}
color={"#000000"}
/>
<span className="mono f9 ml2 white-d c-default">{cite(window.ship)}</span>
<div className="fl lh-copy absolute left-0" style={{ top: 8 }}>
<a href="/~groups/me" className="dib v-top">
<Sigil
ship={"~" + window.ship}
classes="v-mid mix-blend-diff"
size={16}
color={"#000000"}
/>
</a>
<GroupFilter invites={invites} associations={this.props.associations} />
<span className="dib f9 v-mid gray2 ml1 mr1 c-default inter">/</span>
<a
className="dib f9 v-mid inter ml1"
href="/"
style={{ top: 14 }}>
Links</a>
</div>
</div>
);

View File

@ -38,8 +38,10 @@ export class Root extends Component {
let comments = !!state.comments ? state.comments : {};
const seen = !!state.seen ? state.seen : {};
const invites = '/link' in state.invites ?
state.invites['/link'] : {};
const invites = state.invites ?
state.invites : {};
let selectedGroups = !!state.selected ? state.selected : [];
return (
<BrowserRouter><Switch>
@ -54,6 +56,7 @@ export class Root extends Component {
groups={groups}
rightPanelHide={true}
sidebarShown={state.sidebarShown}
selectedGroups={selectedGroups}
links={links}
listening={state.listening}>
<div className="h-100 w-100 overflow-x-hidden bg-white bg-gray0-d dn db-ns">
@ -76,6 +79,7 @@ export class Root extends Component {
groups={groups}
rightPanelHide={true}
sidebarShown={state.sidebarShown}
selectedGroups={selectedGroups}
links={links}
listening={state.listening}>
<NewScreen
@ -114,6 +118,7 @@ export class Root extends Component {
selected={resourcePath}
rightPanelHide={true}
sidebarShown={state.sidebarShown}
selectedGroups={selectedGroups}
links={links}
listening={state.listening}>
<MemberScreen
@ -151,6 +156,7 @@ export class Root extends Component {
selected={resourcePath}
rightPanelHide={true}
sidebarShown={state.sidebarShown}
selectedGroups={selectedGroups}
popout={popout}
links={links}
listening={state.listening}>
@ -203,6 +209,7 @@ export class Root extends Component {
groups={groups}
selected={resourcePath}
sidebarShown={state.sidebarShown}
selectedGroups={selectedGroups}
sidebarHideMobile={true}
popout={popout}
links={links}
@ -258,6 +265,7 @@ export class Root extends Component {
groups={groups}
selected={resourcePath}
sidebarShown={state.sidebarShown}
selectedGroups={selectedGroups}
sidebarHideMobile={true}
popout={popout}
links={links}

View File

@ -19,17 +19,21 @@ export class Skeleton extends Component {
let popoutBorder = (popout)
? "" : "ba-m ba-l ba-xl b--gray2 br1"
let linkInvites = ('/link' in this.props.invites)
? this.props.invites['/link'] : {};
return (
<div className={"absolute h-100 w-100 " + popoutWindow}>
<HeaderBar spinner={this.props.spinner} />
<HeaderBar spinner={this.props.spinner} invites={this.props.invites} associations={this.props.associations} />
<div className={`cf w-100 h-100 flex ` + popoutBorder}>
<ChannelsSidebar
active={this.props.active}
popout={popout}
associations={this.props.associations}
invites={this.props.invites}
invites={linkInvites}
groups={this.props.groups}
selected={this.props.selected}
selectedGroups={this.props.selectedGroups}
sidebarShown={this.props.sidebarShown}
links={this.props.links}
listening={this.props.listening}/>

View File

@ -6,6 +6,7 @@ export class LocalReducer {
if (data) {
this.sidebarToggle(data, state);
this.setSpinner(data, state);
this.setSelected(data, state);
}
}
@ -22,4 +23,10 @@ export class LocalReducer {
state.spinner = obj.spinner;
}
}
setSelected(obj, state) {
let data = _.has(obj, 'selected', false);
if (data) {
state.selected = obj.selected;
}
}
}

View File

@ -20,6 +20,7 @@ class Store {
contacts: {}
},
invites: {},
selected: [],
links: {},
listening: new Set(),
comments: {},