groups: restore previous virtualiser

This commit is contained in:
Matilde Park 2020-09-15 22:09:26 -04:00
parent e35bd1a2c9
commit b1573ccae5
4 changed files with 109 additions and 108 deletions

View File

@ -7989,6 +7989,15 @@
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.0.tgz",
"integrity": "sha512-IgmcegOSi5SNX+2Snh1vqmF0Vg/CbkycU9XZbOHJlZ6kMzTmi3yc254oB1WCkgA7OQtIAoLmcSFuHTc/tlcqXg=="
},
"react-virtuoso": {
"version": "0.20.0",
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-0.20.0.tgz",
"integrity": "sha512-h+U6t/+m91AzfUe6bBfaacdLLJl1y8v7CfcXwPgQ/Dic+vNlgQmi6cIKTq18zuF+kI8Q7QN0ojIeqPHWbU8TZA==",
"requires": {
"resize-observer-polyfill": "^1.5.1",
"tslib": "^1.11.1"
}
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@ -8267,6 +8276,11 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
},
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",

View File

@ -32,6 +32,7 @@
"react-markdown": "^4.3.1",
"react-oembed-container": "^1.0.0",
"react-router-dom": "^5.0.0",
"react-virtuoso": "^0.20.0",
"remark-disable-tokenizers": "^1.0.24",
"style-loader": "^1.2.1",
"styled-components": "^5.1.0",

View File

@ -1,48 +1,50 @@
import React, { PureComponent } from 'react';
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Virtuoso as VirtualList } from 'react-virtuoso';
import { ContactItem } from './contact-item';
import { ShareSheet } from './share-sheet';
import { Sigil } from '~/logic/lib/sigil';
import { Spinner } from '~/views/components/Spinner';
import { cite } from '~/logic/lib/util';
import { roleForShip, resourceFromPath } from '~/logic/lib/group';
import { Path, PatpNoSig } from '~/types/noun';
import { Contacts } from '~/types/contact-update';
import { Rolodex, Contacts, Contact } from '~/types/contact-update';
import { Groups, Group } from '~/types/group-update';
import GlobalApi from '~/logic/api/global';
import VirtualScroller from '~/views/components/VirtualScroller';
import { ContactItem } from './contact-item';
import { ShareSheet } from './share-sheet';
interface ContactSidebarProps {
activeDrawer: 'contacts' | 'detail' | 'rightPanel';
groups: Groups;
group: Group
contacts: Contacts;
path: Path;
api: GlobalApi;
defaultContacts: Contacts;
selectedContact?: PatpNoSig;
activeDrawer: 'contacts' | 'detail' | 'rightPanel';
groups: Groups;
group: Group
contacts: Contacts;
path: Path;
api: GlobalApi;
defaultContacts: Contacts;
selectedContact?: PatpNoSig;
}
interface ContactSidebarState {
awaiting: boolean;
awaiting: boolean;
memberboxHeight: number;
}
export class ContactSidebar extends PureComponent<ContactSidebarProps, ContactSidebarState> {
private virtualList: VirtualScroller | null;
export class ContactSidebar extends Component<ContactSidebarProps, ContactSidebarState> {
constructor(props) {
super(props);
this.state = {
awaiting: false
awaiting: false,
memberboxHeight: 0
};
this.virtualList = null;
this.memberbox = this.memberbox.bind(this);
}
componentDidUpdate(prevProps: ContactSidebarProps, prevState: ContactSidebarState) {
if (prevProps.path !== this.props.path && this.virtualList) {
this.virtualList.calculateVisibleItems();
memberbox(element) {
if (element) {
this.setState({
memberboxHeight: element.getBoundingClientRect().height
})
}
}
@ -55,17 +57,17 @@ export class ContactSidebar extends PureComponent<ContactSidebarProps, ContactSi
const group = props.groups[props.path];
const members = new Set(group.members || []);
const members = new Set(group.members || []);
const me = (window.ship in props.contacts)
? props.contacts[window.ship]
: (window.ship in props.defaultContacts)
? props.defaultContacts[window.ship]
? props.contacts[window.ship]
: (window.ship in props.defaultContacts)
? props.defaultContacts[window.ship]
: { color: '0x0', nickname: null, avatar: null };
const shareSheet =
!(window.ship in props.contacts) ?
( <ShareSheet
(<ShareSheet
ship={window.ship}
nickname={me.nickname}
avatar={me.avatar}
@ -73,57 +75,57 @@ export class ContactSidebar extends PureComponent<ContactSidebarProps, ContactSi
path={props.path}
selected={props.path + '/' + window.ship === props.selectedContact}
/>
) : (
<>
<h2 className="f9 pt4 pr4 pb2 pl4 gray2 c-default">You</h2>
<ContactItem
ship={window.ship}
nickname={me.nickname}
avatar={me.avatar}
color={me.color}
path={props.path}
selected={props.path + '/' + window.ship === props.selectedContact}
/>
</>
);
) : (
<>
<h2 className="f9 pt4 pr4 pb2 pl4 gray2 c-default">You</h2>
<ContactItem
ship={window.ship}
nickname={me.nickname}
avatar={me.avatar}
color={me.color}
path={props.path}
selected={props.path + '/' + window.ship === props.selectedContact}
/>
</>
);
members.delete(window.ship);
const contactItems =
Object.keys(props.contacts)
.filter(c => c !== window.ship)
.map((contact) => {
members.delete(contact);
const path = props.path + '/' + contact;
const obj = props.contacts[contact];
return (
<ContactItem
key={contact}
ship={contact}
nickname={obj.nickname}
color={obj.color}
avatar={obj.avatar}
path={props.path}
selected={path === props.selectedContact}
share={false}
/>
);
});
.filter(c => c !== window.ship)
.map((contact) => {
members.delete(contact);
const path = props.path + '/' + contact;
const obj = props.contacts[contact];
return (
<ContactItem
key={contact}
ship={contact}
nickname={obj.nickname}
color={obj.color}
avatar={obj.avatar}
path={props.path}
selected={path === props.selectedContact}
share={false}
/>
);
});
const role = roleForShip(group, window.ship);
const resource = resourceFromPath(props.path);
const resource = resourceFromPath(props.path);
const groupItems =
Array.from(members).map((member) => {
const memberRole = roleForShip(group, member);
const adminOpt = (role === 'admin' && memberRole !== 'admin')
|| (role === 'moderator' &&
(memberRole !== 'admin' && memberRole !== 'moderator'))
|| (role === 'moderator' &&
(memberRole !== 'admin' && memberRole !== 'moderator'))
? 'dib' : 'dn';
return (
<div
key={member}
className={'pl4 pt1 pb1 f9 flex justify-start content-center ' +
'bg-white bg-gray0-d relative'}
key={member}
className={'pl4 pt1 pb1 f9 flex justify-start content-center ' +
'bg-white bg-gray0-d relative'}
>
<Sigil
ship={member}
@ -156,18 +158,10 @@ export class ContactSidebar extends PureComponent<ContactSidebarProps, ContactSi
const detailHref = `/~groups/detail${props.path}`;
const items = new Map();
groupItems.forEach((item, index) => {
items.set(index + 1, item);
});
contactItems.forEach((item, index) => {
items.set(index + groupItems.length + 1, item);
});
return (
<div className={'bn br-m br-l br-xl b--gray4 b--gray1-d lh-copy h-100 ' +
'flex-basis-100-s flex-basis-30-ns mw5-m mw5-l mw5-xl relative ' +
'overflow-hidden flex-shrink-0 ' + responsiveClasses}
<div ref={this.memberbox} className={'bn br-m br-l br-xl b--gray4 b--gray1-d lh-copy h-100 ' +
'flex-basis-100-s flex-basis-30-ns mw5-m mw5-l mw5-xl relative ' +
'overflow-hidden flex-shrink-0 ' + responsiveClasses}
>
<div className="pt3 pb5 pl3 f8 db dn-m dn-l dn-xl">
<Link to="/~groups/">{'⟵ All Groups'}</Link>
@ -186,20 +180,21 @@ export class ContactSidebar extends PureComponent<ContactSidebarProps, ContactSi
>Channels</Link>
{shareSheet}
<h2 className="f9 pt4 pr4 pb2 pl4 gray2 c-default">Members</h2>
<VirtualScroller
ref={list => {this.virtualList = list}}
origin="top"
style={{ height: '100%' }}
loadRows={(start, end) => {}}
size={contactItems.length + groupItems.length}
data={items}
renderer={({ index, measure, scrollWindow }) => {
return <div key={index} onLoad={event => measure(event.target)}>{items.get(index)}</div>;
}}
<VirtualList
style={{ height: this.state.memberboxHeight, width: '100%' }}
className="flex-auto"
totalCount={contactItems.length + groupItems.length}
itemHeight={44} // We happen to know this
item={
(index) => index <= (contactItems.length - 1) // If the index is within the length of contact items,
? contactItems[index] // show a contact item
: groupItems[index - contactItems.length] // Otherwise show a group item
}
/>
</div>
<Spinner awaiting={this.state.awaiting} text="Removing from group..." classes="pa2 ba absolute right-1 bottom-1 b--gray1-d" />
</div>
</div>
);
}
}

View File

@ -1,11 +1,9 @@
import React, { Component } from 'react';
import _, { capitalize } from 'lodash';
import { Virtuoso as VirtualList } from 'react-virtuoso';
import { cite, deSig } from '~/logic/lib/util';
import { roleForShip, resourceFromPath } from '~/logic/lib/group';
import VirtualScroller from '~/views/components/VirtualScroller';
import {
Group,
InvitePolicy,
@ -85,7 +83,7 @@ interface GroupViewProps {
export class GroupView extends Component<
GroupViewProps,
{ invites: Invites; awaiting: boolean }
> {
> {
constructor(props) {
super(props);
this.setInvites = this.setInvites.bind(this);
@ -205,23 +203,22 @@ export class GroupView extends Component<
});
}
memberElements(): Map<number, JSX.Element> {
memberElements() {
const { group, permissions } = this.props;
const { members } = group;
const isAdmin = this.isAdmin();
const map: Map<number, JSX.Element> = new Map();
Array.from(members).sort((a, b) => b.localeCompare(a)).map((ship, index) => {
return Array.from(members).map((ship) => {
const role = roleForShip(group, deSig(ship));
const onRoleRemove =
role && isAdmin
? () => {
this.removeTag(ship, { tag: role });
}
this.removeTag(ship, { tag: role });
}
: undefined;
const [present, missing] = this.getAppTags(ship);
const options = this.optionsForShip(ship, missing);
map.set(index, (
return (
<GroupMember ship={ship} options={options}>
{((permissions && role) || present.length > 0) && (
<div className='flex mt1'>
@ -243,9 +240,8 @@ export class GroupView extends Component<
</div>
)}
</GroupMember>
));
});
return map;
);
})
}
setInvites(invites: Invites) {
@ -336,15 +332,10 @@ export class GroupView extends Component<
{'open' in group.policy && this.renderBanned(group.policy)}
<div className='flex flex-column'>
<div className='f9 gray2 mt6 mb3'>Members</div>
<VirtualScroller
size={memberElements.size}
<VirtualList
style={{ height: '500px', width: '100%' }}
origin="top"
loadRows={(start, end) => {}}
data={memberElements}
renderer={({ index, measure, scrollWindow }) => {
return <div key={index} onLoad={event => measure(event.target)} className='flex flex-column pv3'>{memberElements.get(index)}</div>;
}}
totalCount={memberElements.length}
item={(index) => <div key={index} className='flex flex-column pv3'>{memberElements[index]}</div>}
/>
</div>