mirror of
https://github.com/urbit/shrub.git
synced 2024-12-01 06:35:32 +03:00
Merge branch 'mp/sigil-foregrounding' (#2762)
* mp/sigil-foregrounding: groups: lint contact-card.js groups: restrict sigil color input to 6 hex chars various: set sigil foreground based on contrast Signed-off-by: Matilde Park <matilde@tlon.io>
This commit is contained in:
commit
14dd521fbc
@ -1,28 +1,44 @@
|
||||
import React, { Component } from 'react';
|
||||
import { sigil, reactRenderer } from 'urbit-sigil-js';
|
||||
|
||||
|
||||
export class Sigil extends Component {
|
||||
render() {
|
||||
const { props } = this;
|
||||
|
||||
let classes = props.classes || "";
|
||||
const classes = props.classes || '';
|
||||
|
||||
const rgb = {
|
||||
r: parseInt(props.color.slice(1, 3), 16),
|
||||
g: parseInt(props.color.slice(3, 5), 16),
|
||||
b: parseInt(props.color.slice(5, 7), 16)
|
||||
};
|
||||
const brightness = ((299 * rgb.r) + (587 * rgb.g) + (114 * rgb.b)) / 1000;
|
||||
const whiteBrightness = 255;
|
||||
|
||||
let foreground = 'white';
|
||||
|
||||
if ((whiteBrightness - brightness) < 50) {
|
||||
foreground = 'black';
|
||||
}
|
||||
|
||||
if (props.ship.length > 14) {
|
||||
return (
|
||||
<div
|
||||
className={"bg-black dib " + classes}
|
||||
style={{width: props.size, height: props.size}}>
|
||||
</div>
|
||||
className={'bg-black dib ' + classes}
|
||||
style={{ width: props.size, height: props.size }}
|
||||
></div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={"dib " + classes} style={{ flexBasis: props.size, backgroundColor: props.color }}>
|
||||
<div
|
||||
className={'dib ' + classes}
|
||||
style={{ flexBasis: 32, backgroundColor: props.color }}
|
||||
>
|
||||
{sigil({
|
||||
patp: props.ship,
|
||||
renderer: reactRenderer,
|
||||
size: props.size,
|
||||
colors: [props.color, "white"],
|
||||
colors: [props.color, foreground],
|
||||
class: props.svgClass
|
||||
})}
|
||||
</div>
|
||||
@ -30,4 +46,3 @@ export class Sigil extends Component {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||
import { Sigil } from './icons/sigil';
|
||||
|
||||
import { api } from '/api';
|
||||
import { Route, Link } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { EditElement } from '/components/lib/edit-element';
|
||||
import { Spinner } from './icons/icon-spinner';
|
||||
import { uxToHex } from '/lib/util';
|
||||
@ -19,7 +19,7 @@ export class ContactCard extends Component {
|
||||
websiteToSet: null,
|
||||
notesToSet: null,
|
||||
awaiting: false,
|
||||
type: "Saving to group"
|
||||
type: 'Saving to group'
|
||||
};
|
||||
this.editToggle = this.editToggle.bind(this);
|
||||
this.sigilColorSet = this.sigilColorSet.bind(this);
|
||||
@ -50,10 +50,9 @@ export class ContactCard extends Component {
|
||||
}
|
||||
|
||||
editToggle() {
|
||||
const { props } = this;
|
||||
let editSwitch = this.state.edit;
|
||||
editSwitch = !editSwitch;
|
||||
this.setState({edit: editSwitch});
|
||||
this.setState({ edit: editSwitch });
|
||||
}
|
||||
|
||||
emailToSet(value) {
|
||||
@ -82,172 +81,169 @@ export class ContactCard extends Component {
|
||||
|
||||
shipParser(ship) {
|
||||
switch (ship.length) {
|
||||
case 3: return "Galaxy";
|
||||
case 6: return "Star";
|
||||
case 13: return "Planet";
|
||||
case 56: return "Comet";
|
||||
default: return "Unknown";
|
||||
case 3: return 'Galaxy';
|
||||
case 6: return 'Star';
|
||||
case 13: return 'Planet';
|
||||
case 56: return 'Comet';
|
||||
default: return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
setField(field) {
|
||||
const { props, state } = this;
|
||||
let ship = "~" + props.ship;
|
||||
let emailTest = new RegExp(''
|
||||
+ /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*/.source
|
||||
const ship = '~' + props.ship;
|
||||
const emailTest = new RegExp(String(/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*/.source)
|
||||
+ /@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/.source
|
||||
);
|
||||
|
||||
let phoneTest = new RegExp(''
|
||||
+ /^\s*(?:\+?(\d{1,3}))?/.source
|
||||
const phoneTest = new RegExp(String(/^\s*(?:\+?(\d{1,3}))?/.source)
|
||||
+ /([-. (]*(\d{3})[-. )]*)?((\d{3})[-. ]*(\d{2,4})(?:[-.x ]*(\d+))?)\s*$/.source
|
||||
);
|
||||
|
||||
let websiteTest = new RegExp(''
|
||||
+ /[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}/.source
|
||||
const websiteTest = new RegExp(String(/[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}/.source)
|
||||
+ /\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/.source
|
||||
);
|
||||
|
||||
switch (field) {
|
||||
case "color": {
|
||||
let currentColor = (props.contact.color) ? props.contact.color : "000000";
|
||||
case 'color': {
|
||||
let currentColor = (props.contact.color) ? props.contact.color : '000000';
|
||||
currentColor = uxToHex(currentColor);
|
||||
let hexExp = /([0-9A-Fa-f]{6})/
|
||||
let hexTest = hexExp.exec(this.state.colorToSet);
|
||||
const hexExp = /([0-9A-Fa-f]{6})/;
|
||||
const hexTest = hexExp.exec(this.state.colorToSet);
|
||||
|
||||
if (hexTest && (hexTest[1] !== currentColor) && !props.share) {
|
||||
this.setState({ awaiting: true, type: "Saving to group" }, (() => {
|
||||
this.setState({ awaiting: true, type: 'Saving to group' }, (() => {
|
||||
api.contactEdit(props.path, `~${props.ship}`, { color: hexTest[1] }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}))
|
||||
}));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "email": {
|
||||
case 'email': {
|
||||
if (
|
||||
(state.emailToSet === "") ||
|
||||
(state.emailToSet === '') ||
|
||||
(state.emailToSet === props.contact.email)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
let emailTestResult = emailTest.exec(state.emailToSet);
|
||||
const emailTestResult = emailTest.exec(state.emailToSet);
|
||||
if (emailTestResult) {
|
||||
this.setState({ awaiting: true, type: "Saving to group" }, (() => {
|
||||
this.setState({ awaiting: true, type: 'Saving to group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { email: state.emailToSet }).then(() => {
|
||||
this.setState({awaiting: false});
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}))
|
||||
}));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "nickname": {
|
||||
case 'nickname': {
|
||||
if (
|
||||
(state.nickNameToSet === "") ||
|
||||
(state.nickNameToSet === '') ||
|
||||
(state.nickNameToSet === props.contact.nickname)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
this.setState({ awaiting: true, type: "Saving to group" }, (() => {
|
||||
this.setState({ awaiting: true, type: 'Saving to group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { nickname: state.nickNameToSet }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}))
|
||||
}));
|
||||
|
||||
break;
|
||||
}
|
||||
case "notes": {
|
||||
case 'notes': {
|
||||
if (
|
||||
(state.notesToSet === "") ||
|
||||
(state.notesToSet === '') ||
|
||||
(state.notesToSet === props.contact.notes)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
this.setState({ awaiting: true, type: "Saving to group" }, (() => {
|
||||
this.setState({ awaiting: true, type: 'Saving to group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { notes: state.notesToSet }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}))
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case "phone": {
|
||||
case 'phone': {
|
||||
if (
|
||||
(state.phoneToSet === "") ||
|
||||
(state.phoneToSet === '') ||
|
||||
(state.phoneToSet === props.contact.phone)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
let phoneTestResult = phoneTest.exec(state.phoneToSet);
|
||||
const phoneTestResult = phoneTest.exec(state.phoneToSet);
|
||||
if (phoneTestResult) {
|
||||
this.setState({ awaiting: true, type: "Saving to group" }, (() => {
|
||||
this.setState({ awaiting: true, type: 'Saving to group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { phone: state.phoneToSet }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}))
|
||||
}));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "website": {
|
||||
case 'website': {
|
||||
if (
|
||||
(state.websiteToSet === "") ||
|
||||
(state.websiteToSet === '') ||
|
||||
(state.websiteToSet === props.contact.website)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
let websiteTestResult = websiteTest.exec(state.websiteToSet);
|
||||
const websiteTestResult = websiteTest.exec(state.websiteToSet);
|
||||
if (websiteTestResult) {
|
||||
this.setState({ awaiting: true, type: "Saving to group" }, (() => {
|
||||
this.setState({ awaiting: true, type: 'Saving to group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { website: state.websiteToSet }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}))
|
||||
}));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "removeAvatar": {
|
||||
this.setState({ awaiting: true, type: "Removing from group" }, (() => {
|
||||
case 'removeAvatar': {
|
||||
this.setState({ awaiting: true, type: 'Removing from group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { avatar: null }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}))
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case "removeEmail": {
|
||||
this.setState({ emailToSet: "", awaiting: true, type: "Removing from group" }, (() => {
|
||||
api.contactEdit(props.path, ship, { email: "" }).then(() => {
|
||||
this.setState({awaiting: false});
|
||||
case 'removeEmail': {
|
||||
this.setState({ emailToSet: '', awaiting: true, type: 'Removing from group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { email: '' }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case "removeNickname": {
|
||||
this.setState({ nicknameToSet: "", awaiting: true, type: "Removing from group" }, (() => {
|
||||
api.contactEdit(props.path, ship, { nickname: "" }).then(() => {
|
||||
this.setState({awaiting: false});
|
||||
case 'removeNickname': {
|
||||
this.setState({ nicknameToSet: '', awaiting: true, type: 'Removing from group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { nickname: '' }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case "removePhone": {
|
||||
this.setState({ phoneToSet: "", awaiting: true, type: "Removing from group" }, (() => {
|
||||
api.contactEdit(props.path, ship, { phone: "" }).then(() => {
|
||||
this.setState({awaiting: false});
|
||||
case 'removePhone': {
|
||||
this.setState({ phoneToSet: '', awaiting: true, type: 'Removing from group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { phone: '' }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case "removeWebsite": {
|
||||
this.setState({ websiteToSet: "", awaiting: true, type: "Removing from group" }, (() => {
|
||||
api.contactEdit(props.path, ship, { website: "" }).then(() => {
|
||||
this.setState({awaiting: false});
|
||||
case 'removeWebsite': {
|
||||
this.setState({ websiteToSet: '', awaiting: true, type: 'Removing from group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { website: '' }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case "removeNotes": {
|
||||
this.setState({ notesToSet: "", awaiting: true, type: "Removing from group" }, (() => {
|
||||
api.contactEdit(props.path, ship, { notes: "" }).then(() => {
|
||||
this.setState({awaiting: false});
|
||||
case 'removeNotes': {
|
||||
this.setState({ notesToSet: '', awaiting: true, type: 'Removing from group' }, (() => {
|
||||
api.contactEdit(props.path, ship, { notes: '' }).then(() => {
|
||||
this.setState({ awaiting: false });
|
||||
});
|
||||
}));
|
||||
break;
|
||||
@ -264,7 +260,7 @@ export class ContactCard extends Component {
|
||||
|
||||
shareWithGroup() {
|
||||
const { props, state } = this;
|
||||
let defaultVal = props.share ? {
|
||||
const defaultVal = props.share ? {
|
||||
nickname: props.rootIdentity.nickname,
|
||||
email: props.rootIdentity.email,
|
||||
phone: props.rootIdentity.phone,
|
||||
@ -280,7 +276,7 @@ export class ContactCard extends Component {
|
||||
color: props.contact.color
|
||||
};
|
||||
|
||||
let contact = {
|
||||
const contact = {
|
||||
nickname: this.pickFunction(state.nickNameToSet, defaultVal.nickname),
|
||||
email: this.pickFunction(state.emailToSet, defaultVal.email),
|
||||
phone: this.pickFunction(state.phoneToSet, defaultVal.phone),
|
||||
@ -289,26 +285,26 @@ export class ContactCard extends Component {
|
||||
color: this.pickFunction(state.colorToSet, defaultVal.color),
|
||||
avatar: null
|
||||
};
|
||||
this.setState({awaiting: true, type: "Sharing with group"}, (() => {
|
||||
this.setState({ awaiting: true, type: 'Sharing with group' }, (() => {
|
||||
api.contactView.share(
|
||||
`~${props.ship}`, props.path, `~${window.ship}`, contact
|
||||
).then(() => {
|
||||
props.history.push(`/~groups/view${props.path}/${window.ship}`)
|
||||
props.history.push(`/~groups/view${props.path}/${window.ship}`);
|
||||
});
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
removeFromGroup() {
|
||||
const { props } = this;
|
||||
// share empty contact so that we can remove ourselves from group
|
||||
// if we haven't shared yet
|
||||
let contact = {
|
||||
nickname: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
website: "",
|
||||
notes: "",
|
||||
color: "000000",
|
||||
const contact = {
|
||||
nickname: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
website: '',
|
||||
notes: '',
|
||||
color: '000000',
|
||||
avatar: null
|
||||
};
|
||||
|
||||
@ -316,20 +312,20 @@ export class ContactCard extends Component {
|
||||
`~${props.ship}`, props.path, `~${window.ship}`, contact
|
||||
);
|
||||
|
||||
this.setState({awaiting: true, type: "Removing from group"}, (() => {
|
||||
this.setState({ awaiting: true, type: 'Removing from group' }, (() => {
|
||||
api.contactView.delete(props.path).then(() => {
|
||||
let destination = (props.ship === window.ship)
|
||||
? "" : props.path;
|
||||
this.setState({awaiting: false});
|
||||
const destination = (props.ship === window.ship)
|
||||
? '' : props.path;
|
||||
this.setState({ awaiting: false });
|
||||
props.history.push(`/~groups${destination}`);
|
||||
});
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
renderEditCard() {
|
||||
const { props, state } = this;
|
||||
// if this is our first edit in a new group, propagate from root identity
|
||||
let defaultValue = props.share ? {
|
||||
const defaultValue = props.share ? {
|
||||
nickname: props.rootIdentity.nickname,
|
||||
email: props.rootIdentity.email,
|
||||
phone: props.rootIdentity.phone,
|
||||
@ -345,47 +341,52 @@ export class ContactCard extends Component {
|
||||
color: props.contact.color
|
||||
};
|
||||
|
||||
let shipType = this.shipParser(props.ship);
|
||||
const shipType = this.shipParser(props.ship);
|
||||
|
||||
let defaultColor = !!defaultValue.color ? defaultValue.color : "000000";
|
||||
let defaultColor = defaultValue.color ? defaultValue.color : '000000';
|
||||
defaultColor = uxToHex(defaultColor);
|
||||
let currentColor = !!state.colorToSet ? state.colorToSet : defaultColor;
|
||||
let currentColor = state.colorToSet ? state.colorToSet : defaultColor;
|
||||
currentColor = uxToHex(currentColor);
|
||||
|
||||
let sigilColor = "";
|
||||
let hasAvatar =
|
||||
'avatar' in props.contact && props.contact.avatar !== "TODO";
|
||||
let sigilColor = '';
|
||||
const hasAvatar =
|
||||
'avatar' in props.contact && props.contact.avatar !== 'TODO';
|
||||
|
||||
if (!hasAvatar) {
|
||||
sigilColor = (
|
||||
<div className="tl mt4 mb4 w-auto ml-auto mr-auto"
|
||||
style={{ width: "fit-content" }}>
|
||||
style={{ width: 'fit-content' }}
|
||||
>
|
||||
<p className="f9 gray2 lh-copy">Sigil Color</p>
|
||||
<textarea
|
||||
className={"b--gray4 b--gray2-d black white-d bg-gray0-d f7 ba db pl2 " +
|
||||
"focus-b--black focus-b--white-d"}
|
||||
className={'b--gray4 b--gray2-d black white-d bg-gray0-d f7 ba db pl2 ' +
|
||||
'focus-b--black focus-b--white-d'}
|
||||
onChange={this.sigilColorSet}
|
||||
defaultValue={defaultColor}
|
||||
key={"default" + defaultColor}
|
||||
onBlur={(() => this.setField("color"))}
|
||||
key={'default' + defaultColor}
|
||||
onKeyPress={ e => !e.key.match(/[0-9a-f]/i) ? e.preventDefault() : null}
|
||||
onBlur={(() => this.setField('color'))}
|
||||
maxLength={6}
|
||||
style={{
|
||||
resize: "none",
|
||||
resize: 'none',
|
||||
height: 40,
|
||||
paddingTop: 10,
|
||||
width: 114
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
</textarea>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let avatar = (hasAvatar)
|
||||
const avatar = (hasAvatar)
|
||||
? <img className="dib h-auto" width={128} src={props.contact.avatar} />
|
||||
: <Sigil
|
||||
ship={props.ship}
|
||||
size={128}
|
||||
color={"#" + currentColor}
|
||||
key={"avatar" + currentColor} />;
|
||||
color={'#' + currentColor}
|
||||
key={'avatar' + currentColor}
|
||||
/>;
|
||||
|
||||
return (
|
||||
<div className="w-100 mt8 flex justify-center pa4 pt8 pt0-l pa0-xl pt4-xl pb8">
|
||||
@ -402,38 +403,43 @@ export class ContactCard extends Component {
|
||||
title="Nickname"
|
||||
defaultValue={defaultValue.nickname}
|
||||
onChange={this.nickNameToSet}
|
||||
onDeleteClick={() => this.setField("removeNickname")}
|
||||
onSaveClick={() => this.setField("nickname")}
|
||||
showButtons={!props.share} />
|
||||
onDeleteClick={() => this.setField('removeNickname')}
|
||||
onSaveClick={() => this.setField('nickname')}
|
||||
showButtons={!props.share}
|
||||
/>
|
||||
<EditElement
|
||||
title="Email"
|
||||
defaultValue={defaultValue.email}
|
||||
onChange={this.emailToSet}
|
||||
onDeleteClick={() => this.setField("removeEmail")}
|
||||
onSaveClick={() => this.setField("email")}
|
||||
showButtons={!props.share} />
|
||||
onDeleteClick={() => this.setField('removeEmail')}
|
||||
onSaveClick={() => this.setField('email')}
|
||||
showButtons={!props.share}
|
||||
/>
|
||||
<EditElement
|
||||
title="Phone"
|
||||
defaultValue={defaultValue.phone}
|
||||
onChange={this.phoneToSet}
|
||||
onDeleteClick={() => this.setField("removePhone")}
|
||||
onSaveClick={() => this.setField("phone")}
|
||||
showButtons={!props.share} />
|
||||
onDeleteClick={() => this.setField('removePhone')}
|
||||
onSaveClick={() => this.setField('phone')}
|
||||
showButtons={!props.share}
|
||||
/>
|
||||
<EditElement
|
||||
title="Website"
|
||||
defaultValue={defaultValue.website}
|
||||
onChange={this.websiteToSet}
|
||||
onDeleteClick={() => this.setField("removeWebsite")}
|
||||
onSaveClick={() => this.setField("website")}
|
||||
showButtons={!props.share} />
|
||||
onDeleteClick={() => this.setField('removeWebsite')}
|
||||
onSaveClick={() => this.setField('website')}
|
||||
showButtons={!props.share}
|
||||
/>
|
||||
<EditElement
|
||||
title="Notes"
|
||||
defaultValue={defaultValue.notes}
|
||||
onChange={this.notesToSet}
|
||||
onDeleteClick={() => this.setField("removeNotes")}
|
||||
onSaveClick={() => this.setField("notes")}
|
||||
onDeleteClick={() => this.setField('removeNotes')}
|
||||
onSaveClick={() => this.setField('notes')}
|
||||
resizable={true}
|
||||
showButtons={!props.share} />
|
||||
showButtons={!props.share}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -442,22 +448,23 @@ export class ContactCard extends Component {
|
||||
|
||||
renderCard() {
|
||||
const { props } = this;
|
||||
let shipType = this.shipParser(props.ship);
|
||||
let currentColor = props.contact.color ? props.contact.color : "0x0";
|
||||
let hexColor = uxToHex(currentColor);
|
||||
const shipType = this.shipParser(props.ship);
|
||||
const currentColor = props.contact.color ? props.contact.color : '0x0';
|
||||
const hexColor = uxToHex(currentColor);
|
||||
|
||||
let avatar =
|
||||
('avatar' in props.contact && props.contact.avatar !== "TODO") ?
|
||||
const avatar =
|
||||
('avatar' in props.contact && props.contact.avatar !== 'TODO') ?
|
||||
<img className="dib h-auto" width={128} src={props.contact.avatar} /> :
|
||||
<Sigil
|
||||
ship={props.ship}
|
||||
size={128}
|
||||
color={"#" + hexColor}
|
||||
key={hexColor} />;
|
||||
color={'#' + hexColor}
|
||||
key={hexColor}
|
||||
/>;
|
||||
|
||||
let websiteHref =
|
||||
(props.contact.website && props.contact.website.includes("://")) ?
|
||||
props.contact.website : "http://" + props.contact.website;
|
||||
const websiteHref =
|
||||
(props.contact.website && props.contact.website.includes('://')) ?
|
||||
props.contact.website : 'http://' + props.contact.website;
|
||||
|
||||
return (
|
||||
<div className="w-100 mt8 flex justify-center pa4 pt8 pt0-l pa0-xl pt4-xl">
|
||||
@ -470,39 +477,41 @@ export class ContactCard extends Component {
|
||||
<p className="f8">{shipType}</p>
|
||||
<hr className="mv8 gray4 b--gray4 bb-0 b--solid" />
|
||||
<div>
|
||||
{ !!props.contact.nickname ? (
|
||||
{ props.contact.nickname ? (
|
||||
<div>
|
||||
<p className="f9 gray2">Nickname</p>
|
||||
<p className="f8">{props.contact.nickname}</p>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
{ !!props.contact.email ? (
|
||||
{ props.contact.email ? (
|
||||
<div>
|
||||
<p className="f9 mt6 gray2">Email</p>
|
||||
<p className="f8">{props.contact.email}</p>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
{ !!props.contact.phone ? (
|
||||
{ props.contact.phone ? (
|
||||
<div>
|
||||
<p className="f9 mt6 gray2">Phone</p>
|
||||
<p className="f8">{props.contact.phone}</p>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
{ !!props.contact.website ? (
|
||||
{ props.contact.website ? (
|
||||
<div>
|
||||
<p className="f9 mt6 gray2">website</p>
|
||||
<a target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="bb b--black f8"
|
||||
href={websiteHref}>
|
||||
href={websiteHref}
|
||||
>
|
||||
{props.contact.website}
|
||||
</a>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
{ !!props.contact.notes ? (
|
||||
{ props.contact.notes ? (
|
||||
<div>
|
||||
<p className="f9 mt6 gray2">notes</p>
|
||||
<p className="f8">{props.contact.notes}</p>
|
||||
@ -520,32 +529,33 @@ export class ContactCard extends Component {
|
||||
const { props, state } = this;
|
||||
|
||||
let editInfoText =
|
||||
state.edit ? "Finish" : "Edit";
|
||||
state.edit ? 'Finish' : 'Edit';
|
||||
if (props.share && state.edit) {
|
||||
editInfoText = "Share";
|
||||
editInfoText = 'Share';
|
||||
}
|
||||
|
||||
let ourOpt = (props.ship === window.ship) ? "dib" : "dn";
|
||||
const ourOpt = (props.ship === window.ship) ? 'dib' : 'dn';
|
||||
|
||||
let adminOpt =
|
||||
const adminOpt =
|
||||
((props.path.includes(`~${window.ship}/`)) || ((props.ship === window.ship) &&
|
||||
!(props.path.includes('/~/default'))))
|
||||
? "dib" : "dn";
|
||||
? 'dib' : 'dn';
|
||||
|
||||
let meLink = (props.path === "/~/default")
|
||||
? `/~groups` : `/~groups/detail${props.path}`;
|
||||
const meLink = (props.path === '/~/default')
|
||||
? '/~groups' : `/~groups/detail${props.path}`;
|
||||
|
||||
let card = state.edit ? this.renderEditCard() : this.renderCard();
|
||||
const card = state.edit ? this.renderEditCard() : this.renderCard();
|
||||
return (
|
||||
<div className="w-100 h-100 overflow-hidden">
|
||||
<div
|
||||
className={
|
||||
"flex justify-between w-100 bg-white bg-gray0-d " +
|
||||
"bb b--gray4 b--gray1-d "
|
||||
}>
|
||||
'flex justify-between w-100 bg-white bg-gray0-d ' +
|
||||
'bb b--gray4 b--gray1-d '
|
||||
}
|
||||
>
|
||||
<div className="f9 mv4 mh3 pt1 dib w-100">
|
||||
<Link to={meLink}>
|
||||
{"⟵"}
|
||||
{'⟵'}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex">
|
||||
@ -558,20 +568,22 @@ export class ContactCard extends Component {
|
||||
}
|
||||
}}
|
||||
className={
|
||||
`white-d bg-gray0-d mv4 mh3 f9 pa1 pointer flex-shrink-0 ` +
|
||||
'white-d bg-gray0-d mv4 mh3 f9 pa1 pointer flex-shrink-0 ' +
|
||||
ourOpt
|
||||
}>
|
||||
}
|
||||
>
|
||||
{editInfoText}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
className={
|
||||
`bg-gray0-d mv4 mh3 pa1 f9 red2 pointer flex-shrink-0 ` + adminOpt
|
||||
'bg-gray0-d mv4 mh3 pa1 f9 red2 pointer flex-shrink-0 ' + adminOpt
|
||||
}
|
||||
onClick={this.removeFromGroup}>
|
||||
onClick={this.removeFromGroup}
|
||||
>
|
||||
{props.ship === window.ship
|
||||
? "Leave Group"
|
||||
: "Remove from Group"}
|
||||
? 'Leave Group'
|
||||
: 'Remove from Group'}
|
||||
</button>
|
||||
</div>
|
||||
<div className="h-100 w-100 overflow-x-hidden pb8 white-d">{card}</div>
|
||||
|
@ -1,28 +1,44 @@
|
||||
import React, { Component } from "react";
|
||||
import { sigil, reactRenderer } from "urbit-sigil-js";
|
||||
import React, { Component } from 'react';
|
||||
import { sigil, reactRenderer } from 'urbit-sigil-js';
|
||||
|
||||
export class Sigil extends Component {
|
||||
render() {
|
||||
const { props } = this;
|
||||
|
||||
let classes = props.classes || "";
|
||||
const classes = props.classes || '';
|
||||
|
||||
const rgb = {
|
||||
r: parseInt(props.color.slice(1, 3), 16),
|
||||
g: parseInt(props.color.slice(3, 5), 16),
|
||||
b: parseInt(props.color.slice(5, 7), 16)
|
||||
};
|
||||
const brightness = ((299 * rgb.r) + (587 * rgb.g) + (114 * rgb.b)) / 1000;
|
||||
const whiteBrightness = 255;
|
||||
|
||||
let foreground = 'white';
|
||||
|
||||
if ((whiteBrightness - brightness) < 50) {
|
||||
foreground = 'black';
|
||||
}
|
||||
|
||||
if (props.ship.length > 14) {
|
||||
return (
|
||||
<div
|
||||
className={"bg-black dib " + classes}
|
||||
style={{ width: props.size, height: props.size }}></div>
|
||||
className={'bg-black dib ' + classes}
|
||||
style={{ width: props.size, height: props.size }}
|
||||
></div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
className={"dib " + classes}
|
||||
style={{ flexBasis: 32, backgroundColor: props.color }}>
|
||||
className={'dib ' + classes}
|
||||
style={{ flexBasis: 32, backgroundColor: props.color }}
|
||||
>
|
||||
{sigil({
|
||||
patp: props.ship,
|
||||
renderer: reactRenderer,
|
||||
size: props.size,
|
||||
colors: [props.color, "white"]
|
||||
colors: [props.color, foreground]
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,28 +1,44 @@
|
||||
import React, { Component } from "react";
|
||||
import { sigil, reactRenderer } from "urbit-sigil-js";
|
||||
import React, { Component } from 'react';
|
||||
import { sigil, reactRenderer } from 'urbit-sigil-js';
|
||||
|
||||
export class Sigil extends Component {
|
||||
render() {
|
||||
const { props } = this;
|
||||
|
||||
let classes = props.classes || "";
|
||||
const classes = props.classes || '';
|
||||
|
||||
const rgb = {
|
||||
r: parseInt(props.color.slice(1, 3), 16),
|
||||
g: parseInt(props.color.slice(3, 5), 16),
|
||||
b: parseInt(props.color.slice(5, 7), 16)
|
||||
};
|
||||
const brightness = ((299 * rgb.r) + (587 * rgb.g) + (114 * rgb.b)) / 1000;
|
||||
const whiteBrightness = 255;
|
||||
|
||||
let foreground = 'white';
|
||||
|
||||
if ((whiteBrightness - brightness) < 50) {
|
||||
foreground = 'black';
|
||||
}
|
||||
|
||||
if (props.ship.length > 14) {
|
||||
return (
|
||||
<div
|
||||
className={"bg-black dib " + classes}
|
||||
style={{ width: props.size, height: props.size }}></div>
|
||||
className={'bg-black dib ' + classes}
|
||||
style={{ width: props.size, height: props.size }}
|
||||
></div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
className={"dib " + classes}
|
||||
style={{ flexBasis: props.size, backgroundColor: props.color }}>
|
||||
className={'dib ' + classes}
|
||||
style={{ flexBasis: props.size, backgroundColor: props.color }}
|
||||
>
|
||||
{sigil({
|
||||
patp: props.ship,
|
||||
renderer: reactRenderer,
|
||||
size: props.size,
|
||||
colors: [props.color, "white"]
|
||||
colors: [props.color, foreground]
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,32 +1,47 @@
|
||||
import React, { Component } from 'react';
|
||||
import { sigil, reactRenderer } from 'urbit-sigil-js';
|
||||
|
||||
|
||||
export class Sigil extends Component {
|
||||
render() {
|
||||
const { props } = this;
|
||||
|
||||
let classes = props.classes || "";
|
||||
const classes = props.classes || '';
|
||||
|
||||
const rgb = {
|
||||
r: parseInt(props.color.slice(1, 3), 16),
|
||||
g: parseInt(props.color.slice(3, 5), 16),
|
||||
b: parseInt(props.color.slice(5, 7), 16)
|
||||
};
|
||||
const brightness = ((299 * rgb.r) + (587 * rgb.g) + (114 * rgb.b)) / 1000;
|
||||
const whiteBrightness = 255;
|
||||
|
||||
let foreground = 'white';
|
||||
|
||||
if ((whiteBrightness - brightness) < 50) {
|
||||
foreground = 'black';
|
||||
}
|
||||
|
||||
if (props.ship.length > 14) {
|
||||
return (
|
||||
<div
|
||||
className={"bg-black dib " + classes}
|
||||
style={{ width: props.size, height: props.size }}>
|
||||
</div>
|
||||
className={'bg-black dib ' + classes}
|
||||
style={{ width: props.size, height: props.size }}
|
||||
></div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={"dib " + classes} style={{ flexBasis: props.size, backgroundColor: props.color }}>
|
||||
<div
|
||||
className={'dib ' + classes}
|
||||
style={{ backgroundColor: props.color }}
|
||||
>
|
||||
{sigil({
|
||||
patp: props.ship,
|
||||
renderer: reactRenderer,
|
||||
size: props.size,
|
||||
colors: [props.color, "white"]
|
||||
colors: [props.color, foreground]
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user