fe: Placeholder loading state & use it for profile tabs + RFPs list (#255)

This commit is contained in:
AMStrix 2019-02-23 15:46:35 -06:00 committed by William O'Beirne
parent c6318e27f7
commit 0b2676563a
5 changed files with 34 additions and 10 deletions

View File

@ -1,16 +1,22 @@
import React from 'react';
import './style.less';
import Loader from 'components/Loader';
interface Props {
title?: React.ReactNode;
subtitle?: React.ReactNode;
style?: React.CSSProperties;
loading?: boolean;
}
const Placeholder: React.SFC<Props> = ({ style = {}, title, subtitle }) => (
<div className="Placeholder" style={style}>
{title && <h3 className="Placeholder-title">{title}</h3>}
{subtitle && <div className="Placeholder-subtitle">{subtitle}</div>}
const Placeholder: React.SFC<Props> = ({ style = {}, title, subtitle, loading }) => (
<div className={`Placeholder ${!!loading && 'is-loading'}`} style={style}>
{(loading && <Loader inline={true} size="small" />) || (
<>
{title && <h3 className="Placeholder-title">{title}</h3>}
{subtitle && <div className="Placeholder-subtitle">{subtitle}</div>}
</>
)}
</div>
);

View File

@ -1,3 +1,5 @@
@import '~styles/variables.less';
.Placeholder {
display: flex;
flex-direction: column;
@ -8,6 +10,10 @@
padding: 3rem;
border-radius: 8px;
&.is-loading {
border-color: fadeout(@primary-color, 60%);
}
&-title {
margin-bottom: 0;
color: rgba(0, 0, 0, 0.6);

View File

@ -95,6 +95,8 @@ class Profile extends React.Component<Props, State> {
invites,
arbitrated,
} = user;
const isLoading = user.isFetching;
const nonePending = pendingProposals.length === 0;
const noneCreated = proposals.length === 0;
const noneFunded = contributions.length === 0;
@ -132,6 +134,7 @@ class Profile extends React.Component<Props, State> {
<div>
{nonePending && (
<Placeholder
loading={isLoading}
title="No pending proposals"
subtitle="You do not have any proposals awaiting approval."
/>
@ -144,6 +147,7 @@ class Profile extends React.Component<Props, State> {
<div>
{noneCreated && (
<Placeholder
loading={isLoading}
title="No created proposals"
subtitle="There have not been any created proposals."
/>
@ -157,6 +161,7 @@ class Profile extends React.Component<Props, State> {
<div>
{noneFunded && (
<Placeholder
loading={isLoading}
title="No proposals funded"
subtitle="There have not been any proposals funded."
/>
@ -175,6 +180,7 @@ class Profile extends React.Component<Props, State> {
<div>
{noneCommented && (
<Placeholder
loading={isLoading}
title="No comments"
subtitle="There have not been any comments made"
/>
@ -193,6 +199,7 @@ class Profile extends React.Component<Props, State> {
<div>
{noneInvites && (
<Placeholder
loading={isLoading}
title="No invitations"
subtitle="Youll be notified when youve been invited to join a proposal"
/>
@ -210,6 +217,7 @@ class Profile extends React.Component<Props, State> {
>
{noneArbitrated && (
<Placeholder
loading={isLoading}
title="No arbitrations"
subtitle="You are not an arbiter of any proposals"
/>

View File

@ -14,6 +14,7 @@ import './index.less';
interface StateProps {
rfps: AppState['rfps']['rfps'];
isFetchingRfps: AppState['rfps']['isFetchingRfps'];
hasFetchedRfps: AppState['rfps']['hasFetchedRfps'];
fetchRfpsError: AppState['rfps']['fetchRfpsError'];
}
@ -29,7 +30,7 @@ class RFPs extends React.Component<Props> {
}
render() {
const { rfps, isFetchingRfps, fetchRfpsError } = this.props;
const { rfps, isFetchingRfps, hasFetchedRfps, fetchRfpsError } = this.props;
let rfpsEl;
if (fetchRfpsError) {
@ -41,7 +42,7 @@ class RFPs extends React.Component<Props> {
/>
</div>
);
} else if (!isFetchingRfps) {
} else if (!hasFetchedRfps && isFetchingRfps) {
rfpsEl = (
<div className="RFPs-loading">
<Loader size="large" />
@ -95,6 +96,7 @@ class RFPs extends React.Component<Props> {
<Placeholder
title="No requests are currently active"
subtitle="Check back later for more opportunities"
loading={this.props.isFetchingRfps}
/>
)}
</div>
@ -106,6 +108,7 @@ export default connect<StateProps, DispatchProps, {}, AppState>(
state => ({
rfps: state.rfps.rfps,
isFetchingRfps: state.rfps.isFetchingRfps,
hasFetchedRfps: state.rfps.hasFetchedRfps,
fetchRfpsError: state.rfps.fetchRfpsError,
}),
{ fetchRfps },

View File

@ -5,18 +5,18 @@ export interface RFPState {
rfps: RFP[];
fetchRfpsError: null | string;
isFetchingRfps: boolean;
hasFetchedRfps: boolean;
}
export const INITIAL_STATE: RFPState = {
rfps: [],
fetchRfpsError: null,
isFetchingRfps: false,
hasFetchedRfps: false,
};
function addRfp(state: RFPState, payload: RFP) {
const existingProposal = state.rfps.find(
(rfp: RFP) => rfp.id === payload.id,
);
const existingProposal = state.rfps.find((rfp: RFP) => rfp.id === payload.id);
const rfps = [...state.rfps];
if (!existingProposal) {
@ -56,7 +56,8 @@ export default (state: RFPState = INITIAL_STATE, action: any): RFPState => {
...state,
rfps: payload,
fetchRfpsError: null,
isFetchingRfps: true,
isFetchingRfps: false,
hasFetchedRfps: true,
};
case types.FETCH_RFP_FULFILLED: