mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-01 11:33:41 +03:00
groups: add descriptions, group settings
This commit is contained in:
parent
831b93dc4e
commit
d73d60b73b
@ -68,13 +68,13 @@ class UrbitApi {
|
||||
return this.action("contact-view", "json", data);
|
||||
}
|
||||
|
||||
contactCreate(path, ships = [], title) {
|
||||
contactCreate(path, ships = [], title, description) {
|
||||
return this.contactViewAction({
|
||||
create: {
|
||||
path,
|
||||
ships,
|
||||
title,
|
||||
description: ''
|
||||
description
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -149,6 +149,31 @@ class UrbitApi {
|
||||
});
|
||||
}
|
||||
|
||||
metadataAction(data) {
|
||||
console.log(data);
|
||||
return this.action("metadata-hook", "metadata-action", data);
|
||||
}
|
||||
|
||||
metadataAdd(appPath, groupPath, title, description, dateCreated, color) {
|
||||
let creator = `~${window.ship}`;
|
||||
return this.metadataAction({
|
||||
add: {
|
||||
"group-path": groupPath,
|
||||
resource: {
|
||||
"app-path": appPath,
|
||||
"app-name": "contacts"
|
||||
},
|
||||
metadata: {
|
||||
title,
|
||||
description,
|
||||
color,
|
||||
'date-created': dateCreated,
|
||||
creator
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setSpinner(boolean) {
|
||||
store.handleEvent({
|
||||
data: {
|
||||
|
@ -1,27 +1,64 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Route, Link } from 'react-router-dom';
|
||||
import { uxToHex } from '/lib/util.js';
|
||||
import { deSig, uxToHex } from '/lib/util.js';
|
||||
|
||||
export class GroupDetail extends Component {
|
||||
render() {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
title: "",
|
||||
description: "",
|
||||
}
|
||||
this.changeTitle = this.changeTitle.bind(this);
|
||||
this.changeDescription = this.changeDescription.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { props } = this;
|
||||
let channelPath = `${props.path}/contacts${props.path}`;
|
||||
if ((props.association) && (props.association[channelPath])) {
|
||||
this.setState({
|
||||
title: props.association[channelPath].metadata.title,
|
||||
description: props.association[channelPath].metadata.description
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { props } = this;
|
||||
if (prevProps !== this.props) {
|
||||
let channelPath = `${props.path}/contacts${props.path}`;
|
||||
if ((props.association) && (props.association[channelPath])) {
|
||||
this.setState({
|
||||
title: props.association[channelPath].metadata.title,
|
||||
description: props.association[channelPath].metadata.description
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changeTitle(event) {
|
||||
this.setState({title: event.target.value})
|
||||
}
|
||||
|
||||
changeDescription(event) {
|
||||
this.setState({description: event.target.value})
|
||||
}
|
||||
|
||||
renderDetail() {
|
||||
const { props } = this;
|
||||
|
||||
let responsiveClass =
|
||||
props.activeDrawer === "detail" ? "db" : "dn db-ns";
|
||||
props.activeDrawer === "detail" ? "db " : "dn db-ns ";
|
||||
|
||||
let groupPath = props.path || "";
|
||||
let channelsForGroup = (groupPath in props.associations) ?
|
||||
props.associations[groupPath] : {};
|
||||
|
||||
|
||||
let isEmpty = (Object.keys(channelsForGroup).length === 0) ||
|
||||
((Object.keys(channelsForGroup).length === 1) &&
|
||||
(Object.keys(channelsForGroup)[0].includes("contacts")));
|
||||
let isEmpty = (Object.keys(props.association).length === 0) ||
|
||||
((Object.keys(props.association).length === 1) &&
|
||||
(Object.keys(props.association)[0].includes("contacts")));
|
||||
|
||||
let channelList = (<div />);
|
||||
|
||||
channelList = Object.keys(channelsForGroup).map((key) => {
|
||||
let channel = channelsForGroup[key];
|
||||
channelList = Object.keys(props.association).map((key) => {
|
||||
let channel = props.association[key];
|
||||
if (!('metadata' in channel)) {
|
||||
return <div key={channel} />;
|
||||
}
|
||||
@ -37,18 +74,18 @@ export class GroupDetail extends Component {
|
||||
let link = `/~${app}/join${channelPath}`
|
||||
app = app.charAt(0).toUpperCase() + app.slice(1)
|
||||
|
||||
return(
|
||||
<li key={channel} className="f9 list flex pv2 w-100">
|
||||
<div className="dib"
|
||||
style={{backgroundColor: `#${color}`, height: 32, width: 32}}
|
||||
></div>
|
||||
<div className="flex flex-column flex-auto">
|
||||
<p className="f9 inter ml2 w-100">{title}</p>
|
||||
<p className="f9 inter ml2 w-100"
|
||||
style={{marginTop: "0.35rem"}}>
|
||||
<span className="f9 di mr2 inter">{app}</span>
|
||||
<a className="f9 di green2" href={link}>Open</a>
|
||||
</p>
|
||||
return (
|
||||
<li key={channelPath} className="f9 list flex pv2 w-100">
|
||||
<div className="dib"
|
||||
style={{ backgroundColor: `#${color}`, height: 32, width: 32 }}
|
||||
></div>
|
||||
<div className="flex flex-column flex-auto">
|
||||
<p className="f9 inter ml2 w-100">{title}</p>
|
||||
<p className="f9 inter ml2 w-100"
|
||||
style={{ marginTop: "0.35rem" }}>
|
||||
<span className="f9 di mr2 inter">{app}</span>
|
||||
<a className="f9 di green2" href={link}>Open</a>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
@ -60,23 +97,137 @@ export class GroupDetail extends Component {
|
||||
let emptyGroup = (
|
||||
<div className={isEmpty ? "dt w-100 h-100" : "dn"}>
|
||||
<p className="gray2 f9 tc v-mid dtc">
|
||||
This group has no channels. To add a channel, invite this group using any application.
|
||||
This group has no channels. To add a channel, invite this group using any application.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
let title = props.path.substr(1);
|
||||
let description = "";
|
||||
let channel = `${props.path}/contacts${props.path}`;
|
||||
if ((props.association) && (props.association[channel])) {
|
||||
title = (props.association[channel].metadata.title !== "")
|
||||
? props.association[channel].metadata.title
|
||||
: props.path.substr(1);
|
||||
description = (props.association[channel].metadata.description !== "")
|
||||
? props.association[channel].metadata.description
|
||||
: "";
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={"h-100 w-100 overflow-x-hidden bg-white bg-gray0-d white-d pa4 "
|
||||
+ responsiveClass}>
|
||||
<div className="pb5 f8 db dn-m dn-l dn-xl">
|
||||
<div className={"relative h-100 w-100 bg-white bg-gray0-d white-d pa4 "
|
||||
+ responsiveClass +
|
||||
((isEmpty) ? "overflow-hidden" : "overflow-x-hidden")}>
|
||||
<div className="pb4 f8 db dn-m dn-l dn-xl">
|
||||
<Link to={backLink}>⟵ Contacts</Link>
|
||||
</div>
|
||||
<p className={"gray2 f9 mb2 pt2 pt0-m pt0-l pt0-xl " + (isEmpty ? "dn" : "")}>Group Channels</p>
|
||||
{emptyGroup}
|
||||
{channelList}
|
||||
</div>
|
||||
<div className="w-100 lh-copy">
|
||||
<Link
|
||||
className="absolute right-1 f9"
|
||||
to={"/~groups/settings" + props.path}>Group Settings</Link>
|
||||
<p className="f9">{title}</p>
|
||||
<p className="f9 gray2">{description}</p>
|
||||
<p className="f9">
|
||||
{props.group.size + " participant" +
|
||||
((props.group.size === 1) ? "" : "s")}
|
||||
</p>
|
||||
</div>
|
||||
<p className={"gray2 f9 mb2 pt6 " + (isEmpty ? "dn" : "")}>Group Channels</p>
|
||||
{emptyGroup}
|
||||
{channelList}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderSettings() {
|
||||
const { props } = this;
|
||||
|
||||
let groupOwner = (deSig(props.match.params.ship) === window.ship);
|
||||
|
||||
let channelPath = `${props.path}/contacts${props.path}`;
|
||||
|
||||
let association = ((props.association) && (props.association[channelPath]))
|
||||
? props.association[channelPath] : {};
|
||||
|
||||
return (
|
||||
<div className="pa4 w-100 h-100 white-d">
|
||||
<div className="f9 w-100">
|
||||
<Link to={"/~groups/detail" + props.path}>{"⟵ Channels"}</Link>
|
||||
</div>
|
||||
<div className={(groupOwner) ? "" : "o-30"}>
|
||||
<p className="f8 mt3 lh-copy">Rename</p>
|
||||
<p className="f9 gray2 mb4">Change the name of this group</p>
|
||||
<div className="relative w-100 flex"
|
||||
style={{maxWidth: "29rem"}}>
|
||||
<input
|
||||
className={"f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
|
||||
"focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"}
|
||||
value={this.state.title}
|
||||
disabled={!groupOwner}
|
||||
onChange={this.changeTitle}
|
||||
/>
|
||||
<span className={"f8 absolute pa3 inter " + ((groupOwner) ? "pointer" : "")}
|
||||
style={{right: 12, top: 1}}
|
||||
ref="rename"
|
||||
onClick={() => {
|
||||
if (groupOwner) {
|
||||
props.api.setSpinner(true);
|
||||
props.api.metadataAdd(
|
||||
association['app-path'],
|
||||
association['group-path'],
|
||||
this.state.title,
|
||||
association.metadata.description,
|
||||
association.metadata['date-created'],
|
||||
uxToHex(association.metadata.color)
|
||||
).then(() => {
|
||||
this.refs.rename.innerText = "Saved";
|
||||
props.api.setSpinner(false);
|
||||
})
|
||||
}
|
||||
}}>Save</span>
|
||||
</div>
|
||||
<p className="f8 mt3 lh-copy">Change description</p>
|
||||
<p className="f9 gray2 mb4">Change the description of this group</p>
|
||||
<div className="relative w-100 flex"
|
||||
style={{ maxWidth: "29rem" }}>
|
||||
<input
|
||||
className={"f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
|
||||
"focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"}
|
||||
value={this.state.description}
|
||||
disabled={!groupOwner}
|
||||
onChange={this.changeDescription}
|
||||
/>
|
||||
<span className={"f8 absolute pa3 inter " + ((groupOwner) ? "pointer" : "")}
|
||||
style={{ right: 12, top: 1 }}
|
||||
ref="description"
|
||||
onClick={() => {
|
||||
if (groupOwner) {
|
||||
props.api.setSpinner(true);
|
||||
props.api.metadataAdd(
|
||||
association['app-path'],
|
||||
association['group-path'],
|
||||
association.metadata.title,
|
||||
this.state.description,
|
||||
association.metadata['date-created'],
|
||||
uxToHex(association.metadata.color)
|
||||
).then(() => {
|
||||
this.refs.description.innerText = "Saved";
|
||||
props.api.setSpinner(false);
|
||||
})
|
||||
}
|
||||
}}>Save</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
let render = (this.props.settings)
|
||||
? this.renderSettings() : this.renderDetail();
|
||||
|
||||
return render;
|
||||
}
|
||||
}
|
||||
|
||||
export default GroupDetail
|
||||
|
@ -12,6 +12,7 @@ export class NewScreen extends Component {
|
||||
this.state = {
|
||||
groupName: '',
|
||||
title: '',
|
||||
description: '',
|
||||
invites: {
|
||||
groups: [],
|
||||
ships: []
|
||||
@ -21,6 +22,7 @@ export class NewScreen extends Component {
|
||||
};
|
||||
|
||||
this.groupNameChange = this.groupNameChange.bind(this);
|
||||
this.descriptionChange = this.descriptionChange.bind(this);
|
||||
this.invChange = this.invChange.bind(this);
|
||||
}
|
||||
|
||||
@ -33,6 +35,10 @@ export class NewScreen extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
descriptionChange(event) {
|
||||
this.setState({description: event.target.value});
|
||||
}
|
||||
|
||||
invChange(value) {
|
||||
this.setState({
|
||||
invites: value
|
||||
@ -61,8 +67,12 @@ export class NewScreen extends Component {
|
||||
invites: ''
|
||||
}, () => {
|
||||
props.api.setSpinner(true);
|
||||
let submit = props.api.contactView.create(group, aud, this.state.title);
|
||||
submit.then(() => {
|
||||
props.api.contactView.create(
|
||||
group,
|
||||
aud,
|
||||
this.state.title,
|
||||
this.state.description
|
||||
).then(() => {
|
||||
props.api.setSpinner(false);
|
||||
props.history.push(`/~groups${group}`);
|
||||
})
|
||||
@ -102,6 +112,21 @@ export class NewScreen extends Component {
|
||||
onChange={this.groupNameChange}
|
||||
/>
|
||||
{groupNameErrElem}
|
||||
<h2 className="f8 pt6">Description <span className="gray2">(Optional)</span></h2>
|
||||
<textarea
|
||||
className={
|
||||
"f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 db w-100 mt2 " +
|
||||
"focus-b--black focus-b--white-d"
|
||||
}
|
||||
rows={1}
|
||||
placeholder="Two trumpeteers and a microphone"
|
||||
style={{
|
||||
resize: "none",
|
||||
height: 48,
|
||||
paddingTop: 14
|
||||
}}
|
||||
onChange={this.descriptionChange}
|
||||
/>
|
||||
<h2 className="f8 pt6">Add Group Members</h2>
|
||||
<p className="f9 gray2 lh-copy">Invite ships to your group</p>
|
||||
<div className="relative pb6 mt2">
|
||||
|
@ -82,14 +82,18 @@ export class Root extends Component {
|
||||
</Skeleton>
|
||||
);
|
||||
}} />
|
||||
<Route exact path="/~groups/(detail)?/:ship/:group/"
|
||||
<Route exact path="/~groups/:detail?/:ship/:group/"
|
||||
render={ (props) => {
|
||||
let groupPath =
|
||||
`/${props.match.params.ship}/${props.match.params.group}`;
|
||||
let groupContacts = contacts[groupPath] || {};
|
||||
let group = groups[groupPath] || new Set([]);
|
||||
let detail = !!props.match.url.includes("/detail");
|
||||
let detail = !!(props.match.url.includes("/detail"));
|
||||
let settings = !!(props.match.url.includes("/settings"));
|
||||
|
||||
let association = (associations[groupPath])
|
||||
? associations[groupPath]
|
||||
: {};
|
||||
|
||||
return (
|
||||
<Skeleton
|
||||
@ -99,21 +103,24 @@ export class Root extends Component {
|
||||
contacts={contacts}
|
||||
invites={invites}
|
||||
groups={groups}
|
||||
activeDrawer={detail ? "detail" : "contacts"}
|
||||
activeDrawer={(detail || settings) ? "detail" : "contacts"}
|
||||
selected={groupPath}
|
||||
associations={associations}>
|
||||
<ContactSidebar
|
||||
contacts={groupContacts}
|
||||
defaultContacts={defaultContacts}
|
||||
group={group}
|
||||
activeDrawer={detail ? "detail" : "contacts"}
|
||||
activeDrawer={(detail || settings) ? "detail" : "contacts"}
|
||||
path={groupPath}
|
||||
{...props}
|
||||
/>
|
||||
<GroupDetail
|
||||
associations={associations}
|
||||
association={association}
|
||||
path={groupPath}
|
||||
activeDrawer={detail ? "detail" : "contacts"}
|
||||
group={group}
|
||||
activeDrawer={(detail || settings) ? "detail" : "contacts"}
|
||||
settings={settings}
|
||||
api={api}
|
||||
{...props}
|
||||
/>
|
||||
</Skeleton>
|
||||
|
@ -40,6 +40,7 @@ export class MetadataReducer {
|
||||
state.associations = metadata;
|
||||
}
|
||||
}
|
||||
|
||||
update(json, state) {
|
||||
let data = _.get(json, 'update-metadata', false);
|
||||
if (data) {
|
||||
|
Loading…
Reference in New Issue
Block a user