chat: wip on settings

This commit is contained in:
Logan Allen 2020-08-10 12:01:03 -07:00
parent b90beaf969
commit 9778939aae
9 changed files with 467 additions and 418 deletions

View File

@ -277,44 +277,6 @@ export default class ChatApp extends React.Component<ChatAppProps, {}> {
);
}}
/>
<Route
exact
path="/~chat/(popout)?/members/(~)?/:ship/:station+"
render={(props) => {
let station = `/${props.match.params.ship}/${props.match.params.station}`;
const popout = props.match.url.includes('/popout/');
const association =
station in associations['chat'] ? associations.chat[station] : {};
const groupPath = association['group-path'];
const group = groups[groupPath] || {};
return (
<Skeleton
associations={associations}
invites={invites}
sidebarHideOnMobile={true}
sidebarShown={sidebarShown}
popout={popout}
sidebar={renderChannelSidebar(props, station)}
>
<MemberScreen
{...props}
api={api}
group={group}
groups={groups}
associations={associations}
station={station}
association={association}
contacts={contacts}
popout={popout}
sidebarShown={sidebarShown}
/>
</Skeleton>
);
}}
/>
<Route
exact
path="/~chat/(popout)?/settings/(~)?/:ship/:station+"

View File

@ -28,18 +28,6 @@ export class ChatTabBar extends Component {
return (
<div className="dib flex-shrink-0 flex-grow-1">
{props.isOwner ? (
<div className={'dib pt2 f9 pl6 lh-solid'}>
<Link
className={'no-underline ' + memColor}
to={'/~chat/' + popout + 'members' + props.station}
>
Members
</Link>
</div>
) : (
<div className="dib" style={{ width: 0 }}></div>
)}
<div className={'dib pt2 f9 pl6 pr6 lh-solid'}>
<Link
className={'no-underline ' + setColor}

View File

@ -0,0 +1,40 @@
import React, { Component } from 'react';
export const DeleteButton = (props) => {
const { isOwner, deleteChat } = props;
const leaveButtonClasses = (!isOwner) ? 'pointer' : 'c-default';
const deleteButtonClasses = (isOwner) ?
'b--red2 red2 pointer bg-gray0-d' :
'b--gray3 gray3 bg-gray0-d c-default';
return (
<div>
<div className={'w-100 fl mt3 ' + ((isOwner) ? 'o-30' : '')}>
<p className="f8 mt3 lh-copy db">Leave Chat</p>
<p className="f9 gray2 db mb4">
Remove this chat from your chat list.{' '}
You will need to request for access again.
</p>
<a onClick={(!isOwner) ? deleteChat : null}
className={
'dib f9 black gray4-d bg-gray0-d ba pa2 b--black b--gray1-d ' +
leaveButtonClasses
}>
Leave this chat
</a>
</div>
<div className={'w-100 fl mt3 ' + ((!isOwner) ? 'o-30' : '')}>
<p className="f8 mt3 lh-copy db">Delete Chat</p>
<p className="f9 gray2 db mb4">
Permanently delete this chat.{' '}
All current members will no longer see this chat.
</p>
<a onClick={(isOwner) ? deleteChat : null}
className={'dib f9 ba pa2 ' + deleteButtonClasses}
>Delete this chat</a>
</div>
</div>
);
};

View File

@ -0,0 +1,96 @@
import React, { Component } from 'react';
export class GroupifyButton extends Component {
constructor(props) {
super(props);
this.state = {
inclusive: false,
targetGroup: null
};
}
changeTargetGroup(target) {
if (target.groups.length === 1) {
this.setState({ targetGroup: target.groups[0] });
} else {
this.setState({ targetGroup: null });
}
}
changeInclusive(event) {
this.setState({ inclusive: Boolean(event.target.checked) });
}
renderInclusiveToggle() {
return this.state.targetGroup ? (
<div className="mt4">
<Toggle
boolean={inclusive}
change={this.changeInclusive.bind(this)}
/>
<span className="dib f9 white-d inter ml3">
Add all members to group
</span>
<p className="f9 gray2 pt1" style={{ paddingLeft: 40 }}>
Add chat members to the group if they aren't in it yet
</p>
</div>
) : <div />;
}
render() {
const { inclusive, targetGroup } = this.state;
const {
isOwner,
association,
contacts,
groups,
groupifyChat,
} = this.props;
const groupPath = association['group-path'];
const ownedUnmanagedVillage =
isOwner &&
!contacts[groupPath];
if (!ownedUnmanagedVillage) {
return null;
}
return (
<div>
<div className={'w-100 fl mt3'} style={{ maxWidth: '29rem' }}>
<p className="f8 mt3 lh-copy db">Convert Chat</p>
<p className="f9 gray2 db mb4">
Convert this chat into a group with associated chat, or select a
group to add this chat to.
</p>
<InviteSearch
groups={groups}
contacts={contacts}
associations={associations}
groupResults={true}
shipResults={false}
invites={{
groups: targetGroup ? [targetGroup] : [],
ships: []
}}
setInvite={this.changeTargetGroup.bind(this)}
/>
{this.renderInclusive()}
<a onClick={() => {
groupifyChat(targetGroup, inclusive);
}}
className={
'dib f9 black gray4-d bg-gray0-d ba pa2 mt4 b--black ' +
'b--gray1-d pointer'
}>Convert to group</a>
</div>
</div>
);
}
}

View File

@ -0,0 +1,100 @@
import React, { Component } from 'react';
import { uxToHex } from '../../../../lib/util';
export class MetadataColor extends Component {
constructor(props) {
super(props);
this.state = {
value: props.initialValue
};
this.inputRef = React.createRef();
}
componentDidUpdate(prevProps) {
const { props } = this;
if (prevProps.initialValue !== props.initialValue) {
this.setState({ value: props.initialValue }, () => {
if (this.inputRef.current) {
this.inputRef.current.value =
`#${uxToHex(props.initialValue)}`;
}
});
}
}
changeColor() {
this.setState({ color: event.target.value });
}
submitColor() {
const { props, state } = this;
let color = state.color;
if (color.startsWith('#')) {
color = state.color.substr(1);
}
const hexExp = /([0-9A-Fa-f]{6})/;
const hexTest = hexExp.exec(color);
let currentColor = '000000';
if (props.association && 'metadata' in props.association) {
currentColor = uxToHex(props.association.metadata.color);
}
if (hexTest && (hexTest[1] !== currentColor)) {
const chatOwner = (deSig(props.match.params.ship) === window.ship);
const association =
(props.association) && ('metadata' in props.association)
? props.association : {};
if (chatOwner) {
this.setState({ awaiting: true, type: 'Editing chat...' }, (() => {
props.api.metadata.metadataAdd(
'chat',
association['app-path'],
association['group-path'],
association.metadata.title,
association.metadata.description,
association.metadata['date-created'],
color
).then(() => {
this.setState({ awaiting: false });
});
}));
}
}
}
render() {
return (
<div>
<p className="f8 mt3 lh-copy">Change color</p>
<p className="f9 gray2 db mb4">Give this chat a color when viewing group channels</p>
<div className="relative w-100 flex"
style={{ maxWidth: '10rem' }}
>
<div className="absolute"
style={{
height: 16,
width: 16,
backgroundColor: state.color,
top: 13,
left: 11
}}
/>
<input
className={'pl7 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={state.color}
disabled={!chatOwner}
onChange={this.changeColor}
onBlur={this.submitColor}
/>
</div>
</div>
);
}
}

View File

@ -0,0 +1,52 @@
export class MetadataInput extends Component {
constructor(props) {
super(props);
this.state = {
value: props.initialValue
};
this.inputRef = React.createRef();
}
componentDidUpdate(prevProps) {
const { props } = this;
if (prevProps.initialValue !== props.initialValue) {
this.setState({ value: props.initialValue }, () => {
if (this.inputRef.current) {
this.inputRef.current.value = props.initialValue;
}
});
}
}
render() {
const {
isDisabled,
changeValue,
title,
description
} = this.props;
<div className={'w-100 pb6 fl mt3 ' + ((isOwner) ? '' : 'o-30')}>
<p className="f8 mt3 lh-copy">{title}</p>
<p className="f9 gray2 db mb4">{description}</p>
<div className="relative w-100 flex" style={{ maxWidth: '29rem' }}>
<input
ref={this.inputRef}
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={state.value}
disabled={isDisabled}
onBlur={() => {
if (!isDisabled) {
changeValue(state.value);
}
}}
/>
</div>
</div>
}
}

View File

@ -0,0 +1,105 @@
import MetadataInput from './metadata-input';
const MetadataSettings = (props) => {
const {
isOwner,
association:
(props.association) && ('metadata' in props.association) ?
props.association : {},
changeTitle
} = props;
return (
<div>
<MetadataInput
title={'Rename'}
description={'Change the name of this chat'}
isDisabled={!isOwner}
isDisabled,
changeValue,
title,
description
/>
<div className={'w-100 pb6 fl mt3 ' + ((isOwner) ? '' : 'o-30')}>
<p className="f8 mt3 lh-copy">Rename</p>
<p className="f9 gray2 db mb4">Change the name of this chat</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={state.title}
disabled={!isOwner}
onBlur={() => {
if (isOwner) {
// get title
changeTitle();
}
}}
/>
</div>
<p className="f8 mt3 lh-copy">Change description</p>
<p className="f9 gray2 db mb4">Change the description of this chat</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={state.description}
disabled={!isOwner}
onChange={this.changeDescription}
onBlur={() => {
if (isOwner) {
this.setState({ awaiting: true, type: 'Editing chat...' }, (() => {
props.api.metadata.metadataAdd(
'chat',
association['app-path'],
association['group-path'],
association.metadata.title,
state.description,
association.metadata['date-created'],
uxToHex(association.metadata.color)
).then(() => {
this.setState({ awaiting: false });
});
}));
}
}}
/>
</div>
<p className="f8 mt3 lh-copy">Change color</p>
<p className="f9 gray2 db mb4">Give this chat a color when viewing group channels</p>
<div className="relative w-100 flex"
style={{ maxWidth: '10rem' }}
>
<div className="absolute"
style={{
height: 16,
width: 16,
backgroundColor: state.color,
top: 13,
left: 11
}}
/>
<input
className={'pl7 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={state.color}
disabled={!isOwner}
onChange={this.changeColor}
onBlur={this.submitColor}
/>
</div>
</div>
</div>
);
};

View File

@ -1,94 +0,0 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { deSig } from '../../../lib/util';
import { ChatTabBar } from './lib/chat-tabbar';
import { MemberElement } from './lib/member-element';
import { InviteElement } from './lib/invite-element';
import { SidebarSwitcher } from '../../../components/SidebarSwitch';
import { GroupView } from '../../../components/Group';
import { PatpNoSig } from '../../../types/noun';
export class MemberScreen extends Component {
constructor(props) {
super(props);
this.inviteShips = this.inviteShips.bind(this);
}
inviteShips(ships) {
const { props } = this;
return props.api.chat.invite(props.station, ships.map(s => `~${s}`));
}
render() {
const { props } = this;
const isinPopout = this.props.popout ? 'popout/' : '';
let title = props.station.substr(1);
if (props.association && 'metadata' in props.association) {
title =
props.association.metadata.title !== ''
? props.association.metadata.title
: props.station.substr(1);
}
return (
<div className='h-100 w-100 overflow-x-hidden flex flex-column white-d'>
<div
className='w-100 dn-m dn-l dn-xl inter pt4 pb6 pl3 f8'
style={{ height: '1rem' }}
>
<Link to='/~chat/'>{'⟵ All Chats'}</Link>
</div>
<div
className={`pl4 pt2 bb b--gray4 b--gray1-d bg-gray0-d flex relative
overflow-x-scroll overflow-x-auto-l overflow-x-auto-xl flex-shrink-0`}
style={{ height: 48 }}
>
<SidebarSwitcher
sidebarShown={this.props.sidebarShown}
popout={this.props.popout}
api={this.props.api}
/>
<Link
to={'/~chat/' + isinPopout + 'room' + props.station}
className='pt2 white-d'
>
<h2
className={
'dib f9 fw4 lh-solid v-top ' +
(title === props.station.substr(1) ? 'mono' : '')
}
style={{ width: 'max-content' }}
>
{title}
</h2>
</Link>
<ChatTabBar
{...props}
station={props.station}
numPeers={5}
isOwner={deSig(props.match.params.ship) === window.ship}
popout={this.props.popout}
api={props.api}
/>
</div>
<div className='w-100 pl3 mt0 mt4-m mt4-l mt4-xl cf pr6'>
{ props.association['group-path'] && (
<GroupView
permissions
group={props.group}
resourcePath={props.association['group-path'] || ''}
associations={props.associations}
groups={props.groups}
inviteShips={this.inviteShips}
contacts={props.contacts}
/> )}
</div>
</div>
);
}
}

View File

@ -2,6 +2,10 @@ import React, { Component } from 'react';
import { deSig, uxToHex, writeText } from '../../../lib/util';
import { Link } from 'react-router-dom';
import { MetadataColor } from './lib/metadata-color';
import { MetadataInput } from './lib/metadata-input';
import { DeleteButton } from './lib/delete-button';
import { GroupifyButton } from './lib/groupify-button';
import { Spinner } from '../../../components/Spinner';
import { ChatTabBar } from './lib/chat-tabbar';
import { InviteSearch } from '../../../components/InviteSearch';
@ -14,34 +18,9 @@ export class SettingsScreen extends Component {
this.state = {
isLoading: false,
title: '',
description: '',
color: '',
// groupify settings
targetGroup: null,
inclusive: false,
awaiting: false,
type: 'Editing chat...'
};
this.renderDelete = this.renderDelete.bind(this);
this.changeTargetGroup = this.changeTargetGroup.bind(this);
this.changeInclusive = this.changeInclusive.bind(this);
this.changeTitle = this.changeTitle.bind(this);
this.changeDescription = this.changeDescription.bind(this);
this.changeColor = this.changeColor.bind(this);
this.submitColor = this.submitColor.bind(this);
}
componentDidMount() {
const { props } = this;
if (props.association && 'metadata' in props.association) {
this.setState({
title: props.association.metadata.title,
description: props.association.metadata.description,
color: `#${uxToHex(props.association.metadata.color)}`
});
}
}
componentDidUpdate(prevProps) {
@ -53,76 +32,6 @@ export class SettingsScreen extends Component {
props.history.push('/~chat');
});
}
if ((state.title === '') && (prevProps !== props)) {
if (props.association && 'metadata' in props.association)
this.setState({
title: props.association.metadata.title,
description: props.association.metadata.description,
color: `#${uxToHex(props.association.metadata.color)}`
});
}
}
changeTargetGroup(target) {
if (target.groups.length === 1) {
this.setState({ targetGroup: target.groups[0] });
} else {
this.setState({ targetGroup: null });
}
}
changeInclusive(event) {
this.setState({ inclusive: Boolean(event.target.checked) });
}
changeTitle() {
this.setState({ title: event.target.value });
}
changeDescription() {
this.setState({ description: event.target.value });
}
changeColor() {
this.setState({ color: event.target.value });
}
submitColor() {
const { props, state } = this;
let color = state.color;
if (color.startsWith('#')) {
color = state.color.substr(1);
}
const hexExp = /([0-9A-Fa-f]{6})/;
const hexTest = hexExp.exec(color);
let currentColor = '000000';
if (props.association && 'metadata' in props.association) {
currentColor = uxToHex(props.association.metadata.color);
}
if (hexTest && (hexTest[1] !== currentColor)) {
const chatOwner = (deSig(props.match.params.ship) === window.ship);
const association =
(props.association) && ('metadata' in props.association)
? props.association : {};
if (chatOwner) {
this.setState({ awaiting: true, type: 'Editing chat...' }, (() => {
props.api.metadata.metadataAdd(
'chat',
association['app-path'],
association['group-path'],
association.metadata.title,
association.metadata.description,
association.metadata['date-created'],
color
).then(() => {
this.setState({ awaiting: false });
});
}));
}
}
}
deleteChat() {
@ -139,7 +48,7 @@ export class SettingsScreen extends Component {
}));
}
groupifyChat() {
groupifyChat(targetGroup, inclusive) {
const { props, state } = this;
this.setState({
@ -148,197 +57,79 @@ export class SettingsScreen extends Component {
type: 'Converting chat...'
}, (() => {
props.api.chat.groupify(
props.station, state.targetGroup, state.inclusive
props.station, targetGroup, inclusive
).then(() => this.setState({ awaiting: false }));
}));
}
renderDelete() {
const { props } = this;
renderMembers() {
const chatOwner = (deSig(props.match.params.ship) === window.ship);
const deleteButtonClasses = (chatOwner) ? 'b--red2 red2 pointer bg-gray0-d' : 'b--gray3 gray3 bg-gray0-d c-default';
const leaveButtonClasses = (!chatOwner) ? 'pointer' : 'c-default';
return (
<div>
<div className={'w-100 fl mt3 ' + ((chatOwner) ? 'o-30' : '')}>
<p className="f8 mt3 lh-copy db">Leave Chat</p>
<p className="f9 gray2 db mb4">Remove this chat from your chat list. You will need to request for access again.</p>
<a onClick={(!chatOwner) ? this.deleteChat.bind(this) : null}
className={'dib f9 black gray4-d bg-gray0-d ba pa2 b--black b--gray1-d ' + leaveButtonClasses}
>Leave this chat</a>
</div>
<div className={'w-100 fl mt3 ' + ((!chatOwner) ? 'o-30' : '')}>
<p className="f8 mt3 lh-copy db">Delete Chat</p>
<p className="f9 gray2 db mb4">Permanently delete this chat. All current members will no longer see this chat.</p>
<a onClick={(chatOwner) ? this.deleteChat.bind(this) : null}
className={'dib f9 ba pa2 ' + deleteButtonClasses}
>Delete this chat</a>
</div>
</div>
);
}
renderGroupify() {
const { props, state } = this;
const chatOwner = (deSig(props.match.params.ship) === window.ship);
const groupPath = props.association['group-path'];
const ownedUnmanagedVillage =
chatOwner &&
!props.contacts[groupPath];
if (!ownedUnmanagedVillage) {
return null;
} else {
let inclusiveToggle = <div />;
if (state.targetGroup) {
inclusiveToggle = (
<div className="mt4">
<Toggle
boolean={state.inclusive}
change={this.changeInclusive}
/>
<span className="dib f9 white-d inter ml3">
Add all members to group
</span>
<p className="f9 gray2 pt1" style={{ paddingLeft: 40 }}>
Add chat members to the group if they aren't in it yet
</p>
</div>
);
}
if (chatOwner) {
return (
<div>
<div className={'w-100 fl mt3'} style={{ maxWidth: '29rem' }}>
<p className="f8 mt3 lh-copy db">Convert Chat</p>
<p className="f9 gray2 db mb4">
Convert this chat into a group with associated chat, or select a
group to add this chat to.
</p>
<InviteSearch
groups={props.groups}
contacts={props.contacts}
associations={props.associations}
groupResults={true}
shipResults={false}
invites={{
groups: state.targetGroup ? [state.targetGroup] : [],
ships: []
}}
setInvite={this.changeTargetGroup}
/>
{inclusiveToggle}
<a onClick={this.groupifyChat.bind(this)}
className={'dib f9 black gray4-d bg-gray0-d ba pa2 mt4 b--black b--gray1-d pointer'}
>
Convert to group
</a>
</div>
<div className={'dib pt2 f9 pl6 lh-solid'}>
<Link
className={'no-underline ' + memColor}
to={"/~groups/ship/:ship/:group/"}>
Members
</Link>
</div>
);
}
return (
<div className="dib" style={{ width: 0 }}></div>
);
}
renderMetadataSettings() {
const { props, state } = this;
const chatOwner = (deSig(props.match.params.ship) === window.ship);
const isOwner = (deSig(props.match.params.ship) === window.ship);
const association = (props.association) && ('metadata' in props.association)
? props.association : {};
return(
return (
<div>
<div className={'w-100 pb6 fl mt3 ' + ((chatOwner) ? '' : 'o-30')}>
<p className="f8 mt3 lh-copy">Rename</p>
<p className="f9 gray2 db mb4">Change the name of this chat</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={state.title}
disabled={!chatOwner}
onChange={this.changeTitle}
onBlur={() => {
if (chatOwner) {
this.setState({ awaiting: true, type: 'Editing chat...' }, (() => {
props.api.metadata.metadataAdd(
'chat',
association['app-path'],
association['group-path'],
state.title,
association.metadata.description,
association.metadata['date-created'],
uxToHex(association.metadata.color)
).then(() => {
this.setState({ awaiting: false });
});
}));
}
}}
/>
</div>
<p className="f8 mt3 lh-copy">Change description</p>
<p className="f9 gray2 db mb4">Change the description of this chat</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={state.description}
disabled={!chatOwner}
onChange={this.changeDescription}
onBlur={() => {
if (chatOwner) {
this.setState({ awaiting: true, type: 'Editing chat...' }, (() => {
props.api.metadata.metadataAdd(
'chat',
association['app-path'],
association['group-path'],
association.metadata.title,
state.description,
association.metadata['date-created'],
uxToHex(association.metadata.color)
).then(() => {
this.setState({ awaiting: false });
});
}));
}
}}
/>
</div>
<p className="f8 mt3 lh-copy">Change color</p>
<p className="f9 gray2 db mb4">Give this chat a color when viewing group channels</p>
<div className="relative w-100 flex"
style={{ maxWidth: '10rem' }}
>
<div className="absolute"
style={{
height: 16,
width: 16,
backgroundColor: state.color,
top: 13,
left: 11
}}
/>
<input
className={'pl7 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={state.color}
disabled={!chatOwner}
onChange={this.changeColor}
onBlur={this.submitColor}
/>
</div>
</div>
<MetadataInput
title={'Rename'}
description={'Change the name of this chat'}
isDisabled={!isOwner}
setValue={() => {
this.setState({ awaiting: true, type: 'Editing chat...' }, () => {
props.api.metadata.metadataAdd(
'chat',
association['app-path'],
association['group-path'],
state.title,
association.metadata.description,
association.metadata['date-created'],
uxToHex(association.metadata.color)
).then(() => {
this.setState({ awaiting: false });
});
});
}} />
<MetadataInput
title={'Change description'}
description={'Change the description of this chat'}
isDisabled={!isOwner}
setValue={() => {
this.setState({ awaiting: true, type: 'Editing chat...' }, (() => {
props.api.metadata.metadataAdd(
'chat',
association['app-path'],
association['group-path'],
association.metadata.title,
state.description,
association.metadata['date-created'],
uxToHex(association.metadata.color)
).then(() => {
this.setState({ awaiting: false });
});
}));
}} />
<MetadataColor initialValue={props.association.metadata.color} />
</div>
);
}
@ -347,8 +138,6 @@ export class SettingsScreen extends Component {
const { props, state } = this;
const isinPopout = this.props.popout ? 'popout/' : '';
const permission = Array.from(props.group.members.values());
if (state.isLoading) {
let title = props.station.substr(1);
@ -387,7 +176,6 @@ export class SettingsScreen extends Component {
<ChatTabBar
{...props}
station={props.station}
numPeers={permission.length}
host={props.match.params.ship}
api={props.api}
/>
@ -408,6 +196,8 @@ export class SettingsScreen extends Component {
? props.association.metadata.title : props.station.substr(1);
}
const isOwner = deSig(props.match.params.ship) === window.ship;
return (
<div className="h-100 w-100 overflow-x-hidden flex flex-column white-d">
<div
@ -439,15 +229,25 @@ export class SettingsScreen extends Component {
<ChatTabBar
{...props}
station={props.station}
numPeers={permission.length}
isOwner={deSig(props.match.params.ship) === window.ship}
isOwner={isOwner}
popout={this.props.popout}
/>
</div>
<div className="w-100 pl3 mt4 cf">
<h2 className="f8 pb2">Chat Settings</h2>
{this.renderGroupify()}
{this.renderDelete()}
{this.renderMembers()}
<GroupifyButton
isOwner={isOwner}
association={association}
contacts={contacts}
groups={groups}
groupifyChat={this.groupifyChat.bind(this)}
changeTargetGroup={this.changeTargetGroup.bind(this)}
changeInclusive={this.changeInclusive.bind(this)}
/>
<DeleteButton
isOwner={isOwner}
deleteChat={this.deleteChat.bind(this)} />
{this.renderMetadataSettings()}
<Spinner awaiting={state.awaiting}
classes="absolute right-2 bottom-2 ba pa2 b--gray1-d"