Placeholder: display placeholder statuses while timelines are loading

merge-requests/788/head
Alex Gleason 3 years ago
parent d41e3f96ee
commit 47b433915b
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -29,6 +29,8 @@ export default class ScrollableList extends PureComponent {
children: PropTypes.node,
onScrollToTop: PropTypes.func,
onScroll: PropTypes.func,
placeholderComponent: PropTypes.node,
placeholderCount: PropTypes.number,
};
state = {
@ -222,7 +224,13 @@ export default class ScrollableList extends PureComponent {
}
renderLoading = () => {
const { prepend } = this.props;
const { prepend, placeholderComponent: Placeholder, placeholderCount } = this.props;
if (Placeholder && placeholderCount > 0) {
return Array(placeholderCount).fill().map((_, i) => (
<Placeholder key={i} />
));
}
return (
<div className='slist slist--flex'>

@ -9,6 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import LoadGap from './load_gap';
import ScrollableList from './scrollable_list';
import TimelineQueueButtonHeader from './timeline_queue_button_header';
import PlaceholderMaterialStatus from 'soapbox/features/placeholder/components/placeholder_material_status';
const messages = defineMessages({
queue: { id: 'status_list.queue_label', defaultMessage: 'Click to see {count} new {count, plural, one {post} other {posts}}' },
@ -213,7 +214,16 @@ export default class StatusList extends ImmutablePureComponent {
count={totalQueuedItemsCount}
message={messages.queue}
/>,
<ScrollableList key='scrollable-list' {...other} isLoading={isLoading} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
<ScrollableList
key='scrollable-list'
isLoading={isLoading}
showLoading={isLoading && statusIds.size === 0}
onLoadMore={onLoadMore && this.handleLoadOlder}
placeholderComponent={PlaceholderMaterialStatus}
placeholderCount={20}
ref={this.setRef}
{...other}
>
{this.renderScrollableContent()}
</ScrollableList>,
];

@ -0,0 +1,35 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
export default class Avatar extends React.PureComponent {
static propTypes = {
size: PropTypes.number,
style: PropTypes.object,
inline: PropTypes.bool,
};
static defaultProps = {
inline: false,
};
render() {
const { size, inline } = this.props;
// : TODO : remove inline and change all avatars to be sized using css
const style = !size ? {} : {
width: `${size}px`,
height: `${size}px`,
};
return (
<div
className={classNames('account__avatar', { 'account__avatar-inline': inline })}
style={style}
alt=''
/>
);
}
}

@ -0,0 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import { randomIntFromInterval, generateText } from '../utils';
export default class DisplayName extends React.Component {
static propTypes = {
maxLength: PropTypes.number.isRequired,
minLength: PropTypes.number.isRequired,
}
render() {
const { maxLength, minLength } = this.props;
const length = randomIntFromInterval(maxLength, minLength);
return (
<span className='display-name'>
<span>
<span className='display-name__name'>
<bdi><strong className='display-name__html'>{generateText(length)}</strong></bdi>
</span>
</span>
</span>
);
}
}

@ -0,0 +1,16 @@
import React from 'react';
import PlaceholderStatus from './placeholder_status';
export default class PlaceholderMaterialStatus extends React.Component {
render() {
return (
<div className='material-status' tabIndex={-1} aria-hidden>
<div className='material-status__status' tabIndex={0}>
<PlaceholderStatus {...this.props} focusable={false} />
</div>
</div>
);
}
}

@ -0,0 +1,40 @@
import React from 'react';
import PlaceholderAvatar from './placeholder_avatar';
import PlaceholderDisplayName from './placeholder_display_name';
import PlaceholderStatusContent from './placeholder_status_content';
export default class PlaceholderStatus extends React.Component {
shouldComponentUpdate() {
// Re-rendering this will just cause the random lengths to jump around.
// There's basically no reason to ever do it.
return false;
}
render() {
return (
<div className='placeholder-status'>
<div className='status__wrapper'>
<div className='status'>
<div className='status__info'>
<div className='status__profile'>
<div className='status__avatar'>
<PlaceholderAvatar size={48} />
</div>
<span className='status__display-name'>
<PlaceholderDisplayName minLength={3} maxLength={25} />
</span>
</div>
</div>
<PlaceholderStatusContent minLength={5} maxLength={120} />
{/* TODO */}
{/* <PlaceholderActionBar /> */}
</div>
</div>
</div>
);
}
}

@ -0,0 +1,23 @@
import React from 'react';
import PropTypes from 'prop-types';
import { randomIntFromInterval, generateText } from '../utils';
export default class PlaceholderStatusContent extends React.Component {
static propTypes = {
maxLength: PropTypes.number.isRequired,
minLength: PropTypes.number.isRequired,
}
render() {
const { maxLength, minLength } = this.props;
const length = randomIntFromInterval(maxLength, minLength);
return(
<div className='status__content' tabIndex='0' key='content'>
{generateText(length)}
</div>
);
}
}

@ -0,0 +1,16 @@
export const PLACEHOLDER_CHAR = '█';
export const generateText = length => {
let text = '';
for (let i = 0; i < length; i++) {
text += PLACEHOLDER_CHAR;
}
return text;
};
// https://stackoverflow.com/a/7228322/8811886
export const randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
};

@ -204,3 +204,12 @@ noscript {
.greentext {
color: #789922;
}
.placeholder-status {
.status__display-name strong,
.status__content {
letter-spacing: -1px;
color: var(--brand-color);
opacity: 0.1;
}
}

@ -881,7 +881,8 @@
}
// Make MaterialStatus flush against SubNavigation
.sub-navigation ~ .slist .item-list > article:first-child .material-status__status {
.sub-navigation ~ .slist .item-list > article:first-child .material-status__status,
.sub-navigation ~ .material-status .material-status__status {
border-top-left-radius: 0;
border-top-right-radius: 0;
}

@ -1,6 +1,5 @@
.display-name__account {
position: relative;
cursor: pointer;
}
.display-name .profile-hover-card {

Loading…
Cancel
Save