chat-js: added contacts metadata to chat

This commit is contained in:
Logan Allen 2020-01-30 13:37:52 -08:00
parent a9434743a0
commit 61709260a8
14 changed files with 149 additions and 21 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -117,6 +117,7 @@ class UrbitApi {
};
this.action("chat-hook", "json", data);
data.message.envelope.author = data.message.envelope.author.substr(1);
this.addPendingMessage(data.message);
}

View File

@ -207,6 +207,7 @@ export class ChatScreen extends Component {
<Message
key={msg.uid}
msg={msg}
contacts={props.contacts}
renderSigil={renderSigil}
paddingTop={paddingTop}
paddingBot={paddingBot}
@ -218,7 +219,9 @@ export class ChatScreen extends Component {
let group = Array.from(props.group.values());
let isinPopout = this.props.popout ? "popout/" : "";
let ownerContact = (window.ship in props.contacts)
? props.contacts[window.ship] : false;
return (
<div
@ -268,6 +271,7 @@ export class ChatScreen extends Component {
numMsgs={lastMsgNum}
station={state.station}
owner={deSig(props.match.params.ship)}
ownerContact={ownerContact}
permissions={props.permissions}
placeholder="Message..."
/>

View File

@ -6,7 +6,7 @@ import classnames from 'classnames';
import { Sigil } from '/components/lib/icons/sigil';
import { uuid } from '/lib/util';
import { uuid, uxToHex } from '/lib/util';
export class ChatInput extends Component {
@ -153,6 +153,10 @@ export class ChatInput extends Component {
}
readOnlyRender() {
const { props } = this;
let color = !!props.ownerContact
? uxToHex(props.ownerContact.color) : '#000000';
return (
<div className="pa3 cf flex black bt b--gray4 o-50">
<div className="fl" style={{
@ -160,7 +164,7 @@ export class ChatInput extends Component {
flexBasis: 24,
height: 24
}}>
<Sigil ship={window.ship} size={24} color="#4330FC" />
<Sigil ship={window.ship} size={24} color={`#${color}`} />
</div>
<div className="fr h-100 flex" style={{ flexGrow: 1, height: 28, paddingTop: 6, resize: "none" }}>
<p className="pl3">This chat is read only and you cannot post.</p>
@ -172,6 +176,8 @@ export class ChatInput extends Component {
writeAccessRender() {
const { props, state } = this;
let color = !!props.ownerContact
? uxToHex(props.ownerContact.color) : '#000000';
this.bindShortcuts();
return (
@ -183,7 +189,7 @@ export class ChatInput extends Component {
flexBasis: 24,
height: 24
}}>
<Sigil ship={window.ship} size={24} color="#4330FC" />
<Sigil ship={window.ship} size={24} color={`#${color}`} />
</div>
<div className="fr h-100 flex bg-gray0-d" style={{ flexGrow: 1 }}>
<textarea

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import { Sigil } from '/components/lib/icons/sigil';
import { uxToHex } from '/lib/util';
export class MemberElement extends Component {
@ -33,15 +34,16 @@ export class MemberElement extends Component {
);
}
let name = !!props.contact
? `${props.contact.nickname} (~${props.ship})` : `~${props.ship}`;
let color = !!props.contact ? uxToHex(props.contact.color) : '000000';
return (
<div className="flex mb2">
<Sigil ship={props.ship} size={32} />
<p
className={
<Sigil ship={props.ship} size={32} color={`#${color}`} />
<p className={
"w-70 mono list-ship dib v-mid black white-d ml2 nowrap f8"
}>
~{props.ship}
</p>
}>{name}</p>
{actionElem}
</div>
);

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { Sigil } from '/components/lib/icons/sigil';
import classnames from 'classnames';
import { Route, Link } from 'react-router-dom'
import { uxToHex } from '/lib/util';
import urbitOb from 'urbit-ob';
import moment from 'moment';
import _ from 'lodash';
@ -160,6 +161,15 @@ export class Message extends Component {
if (props.renderSigil) {
let timestamp = moment.unix(props.msg.when / 1000).format('hh:mm a');
let contact = !!(props.msg.author in props.contacts)
? props.contacts[props.msg.author] : false;
let name = props.msg.author;
let color = "#000000";
if (contact) {
name = contact.nickname;
color = `#${uxToHex(contact.color)}`;
}
return (
<div
className={
@ -172,19 +182,13 @@ export class Message extends Component {
<Sigil
ship={props.msg.author}
size={24}
color={((props.msg.author === window.ship)
|| (props.msg.author.substr(1) === window.ship))
? "#4330FC" : "#000000"}
/>
color={color} />
</div>
<div
className="fr clamp-message white-d"
style={{ flexGrow: 1, marginTop: -8 }}>
<div className="hide-child" style={paddingTop}>
<p className="v-mid mono f9 gray2 dib mr3">
{props.msg.author.slice(0, 1) === "~" ? "" : "~"}
{props.msg.author}
</p>
<p className="v-mid mono f9 gray2 dib mr3">{name}</p>
<p className="v-mid mono f9 gray2 dib">{timestamp}</p>
<p className="v-mid mono f9 ml2 gray2 dib child dn-s">{datestamp}</p>
</div>

View File

@ -50,10 +50,14 @@ export class MemberScreen extends Component {
}
let writeListMembers = writeGroup.map((mem) => {
let contact = (mem in props.contacts)
? props.contacts[mem] : false;
return (
<MemberElement
key={mem}
owner={deSig(props.match.params.ship)}
contact={contact}
ship={mem}
path={`/chat${state.station}/write`}
kind={props.write.kind}
@ -62,10 +66,14 @@ export class MemberScreen extends Component {
});
let readListMembers = readGroup.map((mem) => {
let contact = (mem in props.contacts)
? props.contacts[mem] : false;
return (
<MemberElement
key={mem}
owner={deSig(props.match.params.ship)}
contact={contact}
ship={mem}
path={`/chat${state.station}/read`}
kind={props.read.kind}
@ -73,7 +81,7 @@ export class MemberScreen extends Component {
);
});
let isinPopout = this.props.popout ? "popout/" : "";
let isinPopout = this.props.popout ? "popout/" : "";
return (
<div className="h-100 w-100 overflow-x-hidden flex flex-column white-d">

View File

@ -51,6 +51,8 @@ export class Root extends Component {
let invites = '/chat' in state.invites ?
state.invites['/chat'] : {};
let contacts = !!state.contacts ? state.contacts : {};
const renderChannelSidebar = (props) => (
<Sidebar
inbox={state.inbox}
@ -139,6 +141,11 @@ export class Root extends Component {
envelopes: []
};
/*let defaultContacts = ('/~/default' in contacts)
? contacts['/~/default'] : {};*/
let roomContacts = (station in contacts)
? contacts[station] : {};
let write = state.groups[`/chat${station}/write`] || new Set([]);
let popout = props.match.url.includes("/popout/");
@ -158,6 +165,7 @@ export class Root extends Component {
envelopes={mailbox.envelopes}
inbox={state.inbox}
group={write}
contacts={roomContacts}
permissions={state.permissions}
pendingMessages={state.pendingMessages}
popout={popout}
@ -183,6 +191,9 @@ export class Root extends Component {
};
let popout = props.match.url.includes("/popout/");
let roomContacts = (station in contacts)
? contacts[station] : {};
return (
<Skeleton
sidebarHideOnMobile={true}
@ -195,6 +206,7 @@ export class Root extends Component {
api={api}
read={read}
write={write}
contacts={roomContacts}
permissions={state.permissions}
popout={popout}
sidebarShown={state.sidebarShown}

View File

@ -61,3 +61,13 @@ export function dateToDa(d, mil) {
export function deSig(ship) {
return ship.replace('~', '');
}
export function uxToHex(ux) {
if (ux.length > 2 && ux.substr(0,2) === '0x') {
let value = ux.substr(2).replace('.', '').padStart(6, '0');
return value;
}
let value = ux.replace('.', '').padStart(6, '0');
return value;
}

View File

@ -0,0 +1,68 @@
import _ from 'lodash';
export class ContactUpdateReducer {
reduce(json, state) {
let data = _.get(json, 'contact-update', false);
if (data) {
this.create(data, state);
this.delete(data, state);
this.add(data, state);
this.remove(data, state);
this.edit(data, state);
}
}
create(json, state) {
let data = _.get(json, 'create', false);
if (data) {
state.contacts[data.path] = {};
}
}
delete(json, state) {
let data = _.get(json, 'delete', false);
if (data) {
delete state.contacts[data.path];
}
}
add(json, state) {
let data = _.get(json, 'add', false);
if (
data &&
(data.path in state.contacts)
) {
state.contacts[data.path][data.ship] = data.contact;
}
}
remove(json, state) {
let data = _.get(json, 'remove', false);
if (
data &&
(data.path in state.contacts) &&
(data.ship in state.contacts[data.path])
) {
delete state.contacts[data.path][data.ship];
}
}
edit(json, state) {
let data = _.get(json, 'edit', false);
if (
data &&
(data.path in state.contacts) &&
(data.ship in state.contacts[data.path])
) {
let edit = Object.keys(data['edit-field']);
if (edit.length !== 1) {
return;
}
state.contacts[data.path][data.ship][edit[0]] =
data['edit-field'][edit[0]];
}
}
}

View File

@ -29,6 +29,11 @@ export class InitialReducer {
if (data) {
state.invites = data;
}
data = _.get(json, 'contact-initial', false);
if (data) {
state.contacts = data;
}
}
}

View File

@ -1,5 +1,6 @@
import { InitialReducer } from '/reducers/initial';
import { GroupUpdateReducer } from '/reducers/group-update';
import { ContactUpdateReducer } from '/reducers/contact-update';
import { ChatUpdateReducer } from '/reducers/chat-update';
import { InviteUpdateReducer } from '/reducers/invite-update';
import { PermissionUpdateReducer } from '/reducers/permission-update';
@ -11,6 +12,7 @@ class Store {
this.state = {
inbox: {},
groups: {},
contacts: {},
permissions: {},
invites: {},
spinner: false,
@ -21,6 +23,7 @@ class Store {
this.initialReducer = new InitialReducer();
this.groupUpdateReducer = new GroupUpdateReducer();
this.permissionUpdateReducer = new PermissionUpdateReducer();
this.contactUpdateReducer = new ContactUpdateReducer();
this.chatUpdateReducer = new ChatUpdateReducer();
this.inviteUpdateReducer = new InviteUpdateReducer();
this.localReducer = new LocalReducer();
@ -38,6 +41,7 @@ class Store {
this.initialReducer.reduce(json, this.state);
this.groupUpdateReducer.reduce(json, this.state);
this.permissionUpdateReducer.reduce(json, this.state);
this.contactUpdateReducer.reduce(json, this.state);
this.chatUpdateReducer.reduce(json, this.state);
this.inviteUpdateReducer.reduce(json, this.state);
this.localReducer.reduce(json, this.state);

View File

@ -30,6 +30,10 @@ export class Subscription {
this.handleEvent.bind(this),
this.handleError.bind(this),
this.handleQuitAndResubscribe.bind(this));
api.bind('/primary', 'PUT', api.authTokens.ship, 'contact-view',
this.handleEvent.bind(this),
this.handleError.bind(this),
this.handleQuitAndResubscribe.bind(this));
}
handleEvent(diff) {