chat: sidebar redesign, sorted by group

This commit is contained in:
Matilde Park 2020-03-26 14:29:22 -04:00
parent f5e9b2d171
commit afa38b1d2d
7 changed files with 185 additions and 172 deletions

View File

@ -190,8 +190,8 @@ h2 {
}
@media all and (min-width: 34.375em) and (max-width: 46.875em) {
.flex-basis-300-m {
flex-basis: 300px;
.flex-basis-250-m {
flex-basis: 250px;
}
.h-100-minus-40-m {
height: calc(100% - 40px);
@ -202,8 +202,8 @@ h2 {
}
@media all and (min-width: 46.875em) and (max-width: 60em) {
.flex-basis-300-l {
flex-basis: 300px;
.flex-basis-250-l {
flex-basis: 250px;
}
.h-100-minus-40-l {
height: calc(100% - 40px);
@ -214,8 +214,8 @@ h2 {
}
@media all and (min-width: 60em) {
.flex-basis-300-xl {
flex-basis: 300px;
.flex-basis-250-xl {
flex-basis: 250px;
}
.h-100-minus-40-xl {
height: calc(100% - 40px);

View File

@ -0,0 +1,39 @@
import React, { Component } from 'react';
export class ChannelItem extends Component {
constructor(props) {
super(props);
}
onClick() {
const { props } = this;
props.history.push('/~chat/room' + props.box);
}
render() {
const { props, state } = this;
let unreadElem = !!props.unread
? "fw6"
: "";
let title = props.title;
let selectedCss = !!props.selected
? 'bg-gray4 bg-gray1-d gray3-d c-default'
: 'bg-white bg-gray0-d gray3-d hover-bg-gray5 hover-bg-gray1-d pointer';
return (
<div
className={"z1 ph4 pv1 " + selectedCss}
onClick={this.onClick.bind(this)}>
<div className="w-100 v-mid">
<p className={"dib f9 " + unreadElem}>
{title}
</p>
</div>
</div>
);
}
}

View File

@ -0,0 +1,53 @@
import React, { Component } from 'react';
import { ChannelItem } from './channel-item';
export class GroupItem extends Component {
render() {
const { props, state } = this;
let association = !!props.association ? props.association : {};
let title = association["app-path"] ? association["app-path"] : "Direct Messages";
if (association.metadata && association.metadata.title) {
title = association.metadata.title !== ""
? association.metadata.title
: title;
}
let channels = !!props.channels ? props.channels : [];
let first = (props.index === 0) ? "pt1" : "pt4"
let channelItems = channels.map((each, i) => {
let unread = props.unreads[each];
let title = each.substr(1);
if (
each in props.chatMetadata &&
props.chatMetadata[each].metadata
) {
title = props.chatMetadata[each].metadata.title
? props.chatMetadata[each].metadata.title
: each.substr(1);
}
let selected = props.station === each;
return (
<ChannelItem
key={i}
unread={unread}
title={title}
selected={selected}
box={each}
{...props}
/>
)
})
return (
<div className={first}>
<p className="f9 ph4 fw6 gray3">{title}</p>
{channelItems}
</div>
)
}
}
export default GroupItem

View File

@ -1,98 +0,0 @@
import React, { Component } from 'react';
import { cite } from '../../lib/util';
import classnames from 'classnames';
import moment from 'moment';
export class SidebarItem extends Component {
constructor(props) {
super(props);
this.state = {
timeSinceNewestMessage: this.getTimeSinceNewestMessage()
};
}
componentDidMount() {
this.updateTimeSinceNewestMessageInterval = setInterval( () => {
this.setState({timeSinceNewestMessage: this.getTimeSinceNewestMessage()});
}, 60000);
}
componentWillUnmount() {
if (this.updateTimeSinceNewestMessageInterval) {
clearInterval(this.updateTimeSinceNewestMessageInterval);
this.updateTimeSinceNewestMessageInterval = null;
}
}
getTimeSinceNewestMessage() {
return !!this.props.when ?
moment.unix(this.props.when / 1000).from(moment.utc())
: '';
}
onClick() {
const { props } = this;
props.history.push('/~chat/room' + props.box);
}
getLetter(lett) {
if ('text' in lett) {
return lett.text;
} else if ('url' in lett) {
return lett.url;
} else if ('code' in lett) {
return lett.code.expression;
} else if ('me' in lett) {
return lett.me;
} else {
return '';
}
}
render() {
const { props, state } = this;
let unreadElem = !!props.unread
? "green2"
: "";
let title = props.title;
let box = props.box.substr(1);
let latest = this.getLetter(props.latest);
let selectedCss = !!props.selected
? 'bg-gray5 bg-gray1-d gray3-d c-default'
: 'bg-white bg-gray0-d gray3-d pointer';
let authorCss = (props.nickname === props.ship)
? "mono" : "";
let author = (props.nickname === props.ship)
? cite(props.ship) : props.nickname;
return (
<div
className={"z1 pa3 pt2 pb2 bb b--gray4 b--gray1-d " + selectedCss}
onClick={this.onClick.bind(this)}>
<div className="w-100 v-mid">
<p className="dib f8">
{title}
</p>
<p className="f8 db mono gray3 gray2-d pt1">{box}</p>
</div>
<div className="w-100 pt3">
<p className={((unreadElem === "") ? "black white-d" : "") +
unreadElem + " dib f9 mr3 mw4 truncate v-mid " + authorCss}
title={props.ship || ""}>
{(author === "~") ? "" : author}
</p>
<p className="dib mono f9 gray3 v-mid">{state.timeSinceNewestMessage}</p>
</div>
<p className="f8 clamp-3 pt1">{latest}</p>
</div>
);
}
}

View File

@ -1,15 +1,13 @@
import React, { Component } from 'react';
import { Link } from "react-router-dom";
import classnames from 'classnames';
import _ from 'lodash';
import Welcome from '/components/lib/welcome.js';
import { alphabetiseAssociations } from '../lib/util';
import { SidebarInvite } from '/components/lib/sidebar-invite';
import { SidebarItem } from '/components/lib/sidebar-item';
import { GroupItem } from '/components/lib/group-item';
export class Sidebar extends Component {
onClickNew() {
this.props.history.push('/~chat/new');
}
@ -21,6 +19,32 @@ export class Sidebar extends Component {
render() {
const { props, state } = this;
let associations = alphabetiseAssociations(props.associations.contacts);
let groupedChannels = {};
Object.keys(props.inbox).map((box) => {
if (box.startsWith("/~/")) {
if (groupedChannels["/~/"]) {
let array = groupedChannels["/~/"];
array.push(box);
groupedChannels["/~/"] = array;
} else {
groupedChannels["/~/"] = [box]
};
};
let path = !!props.associations.chat[box]
? props.associations.chat[box]["group-path"] : box;
if (path in associations) {
if (groupedChannels[path]) {
let array = groupedChannels[path];
array.push(box);
groupedChannels[path] = array;
} else {
groupedChannels[path] = [box];
}
}
});
let sidebarInvites = Object.keys(props.invites)
.map((uid) => {
return (
@ -31,75 +55,48 @@ export class Sidebar extends Component {
);
});
let sidebarItems = Object.keys(props.inbox)
.map((box) => {
let msg = props.messagePreviews[box];
let letter = _.has(msg, 'letter')
? msg.letter
: {text: 'No messages yet'};
let author = !!msg ? msg.author : '';
let when = !!msg ? msg.when : 0;
let title = box.substr(1);
let associatedGroup = box;
if (
box in props.associations["chat"] &&
props.associations.chat[box].metadata
) {
title = props.associations.chat[box].metadata.title
? props.associations.chat[box].metadata.title
: box.substr(1);
associatedGroup = props.associations.chat[box]["group-path"]
? props.associations.chat[box]["group-path"]
: box;
let groupedItems = Object.keys(associations)
.map((each, i) => {
let channels = groupedChannels[each];
if (channels.length === 0) return;
if (groupedChannels["/~/"] && groupedChannels["/~/"].length !== 0) {
i = i + 1;
}
let nickname = author;
if (associatedGroup in props.contacts &&
author in props.contacts[associatedGroup]) {
nickname = props.contacts[associatedGroup][author].nickname
? props.contacts[associatedGroup][author].nickname
: author;
}
return {
msg,
when,
author,
nickname,
letter,
box,
title: title,
selected: props.station === box
};
})
.sort((a, b) => {
return b.when - a.when;
})
.map((obj) => {
let unread = props.unreads[obj.box];
return (
<SidebarItem
key={obj.box + '/' + obj.when}
title={obj.title}
latest={obj.letter}
box={obj.box}
when={obj.when}
ship={obj.author}
nickname={obj.nickname}
selected={obj.selected}
unread={unread}
history={props.history}
return(
<GroupItem
key={i}
index={i}
association={associations[each]}
chatMetadata={props.associations["chat"]}
channels={channels}
inbox={props.inbox}
station={props.station}
unreads={props.unreads}
{...props}
/>
);
)
});
if (groupedChannels["/~/"] && groupedChannels["/~/"].length !== 0) {
groupedItems.unshift(
<GroupItem
association={"/~/"}
chatMetadata={props.associations["chat"]}
channels={groupedChannels["/~/"]}
inbox={props.inbox}
station={props.station}
unreads={props.unreads}
index={0}
key={"/~/"}
{...props}
/>
)
}
return (
<div
className={`h-100-minus-96-s h-100 w-100 overflow-x-hidden flex
bg-gray0-d flex-column relative z1`}>
<div className="w-100 bg-transparent pa4 bb b--gray4 b--gray1-d"
style={{paddingBottom: 13}}>
<div className="w-100 bg-transparent pa4">
<a
className="dib f9 pointer green2 gray4-d mr4"
onClick={this.onClickNew.bind(this)}>
@ -114,7 +111,7 @@ export class Sidebar extends Component {
<div className="overflow-y-auto h-100">
<Welcome inbox={props.inbox}/>
{sidebarInvites}
{sidebarItems}
{groupedItems}
</div>
</div>
);

View File

@ -45,8 +45,8 @@ export class Skeleton extends Component {
<div
className={
`fl h-100 br b--gray4 b--gray1-d overflow-x-hidden
flex-basis-full-s flex-basis-300-m flex-basis-300-l
flex-basis-300-xl ` +
flex-basis-full-s flex-basis-250-m flex-basis-250-l
flex-basis-250-xl ` +
sidebarHide +
" " +
sidebarHideOnMobile

View File

@ -132,3 +132,25 @@ export function cite(ship) {
}
return `~${patp}`;
}
export function alphabetiseAssociations(associations) {
let result = {};
Object.keys(associations).sort((a, b) => {
let aName = a.substr(1);
let bName = b.substr(1);
if (a.metadata && a.metadata.title) {
aName = a.metadata.title !== ""
? a.metadata.title
: a.substr(1);
}
if (b.metadata && b.metadata.title) {
bName = b.metadata.title !== ""
? b.metadata.title
: b.substr(1);
}
return aName.toLowerCase().localeCompare(bName.toLowerCase());
}).map((each) => {
result[each] = associations[each];
})
return result;
}