Show user listings in profile

This commit is contained in:
Kimmo Puputti 2017-11-01 16:41:42 +02:00
parent 89758e6318
commit fec0c03bed
4 changed files with 123 additions and 10 deletions

View file

@ -106,16 +106,56 @@
.listingsTitle {
@apply --marketplaceH3FontStyles;
color: var(--matterColorAnti);
margin-bottom: 0;
@media (--viewportMedium) {
margin-bottom: 0;
}
@media (--viewportLarge) {
margin-top: 19px;
}
}
.withBioMissingAbove {
/* Avoid extra borders in mobile when bio is missing */
border-top: none;
@media (--viewportLarge) {
border-top: 1px solid var(--matterColorNegative);
}
}
.listings {
margin: 0;
}
.listing {
width: 100%;
float: left;
/* Single column in mobile */
margin-top: 34px;
&:first-of-type {
margin-top: 18px;
}
@media (--viewportMedium) {
/* Two columns in desktop */
width: calc(50% - 12px);
/* Horizontal space */
&:nth-of-type(odd) {
margin-right: 12px;
}
&:nth-of-type(even) {
margin-left: 12px;
}
/* Vertical space */
margin-top: 41px;
&:nth-of-type(1),
&:nth-of-type(2) {
margin-top: 20px;
}
}
}

View file

@ -8,12 +8,20 @@ export const SHOW_USER_REQUEST = 'app/ProfilePage/SHOW_USER_REQUEST';
export const SHOW_USER_SUCCESS = 'app/ProfilePage/SHOW_USER_SUCCESS';
export const SHOW_USER_ERROR = 'app/ProfilePage/SHOW_USER_ERROR';
export const QUERY_LISTINGS_REQUEST = 'app/ProfilePage/QUERY_LISTINGS_REQUEST';
export const QUERY_LISTINGS_SUCCESS = 'app/ProfilePage/QUERY_LISTINGS_SUCCESS';
export const QUERY_LISTINGS_ERROR = 'app/ProfilePage/QUERY_LISTINGS_ERROR';
// ================ Reducer ================ //
const initialState = {
userId: null,
userShowInProgress: false,
userShowError: null,
queryListingsError: null,
userListingRefs: [],
};
export default function profilePageReducer(state = initialState, action = {}) {
@ -26,6 +34,13 @@ export default function profilePageReducer(state = initialState, action = {}) {
case SHOW_USER_ERROR:
return { ...state, userShowInProgress: false, userShowError: payload };
case QUERY_LISTINGS_REQUEST:
return { ...state, userListingRefs: [], queryListingsError: null };
case QUERY_LISTINGS_SUCCESS:
return { ...state, userListingRefs: payload.listingRefs };
case QUERY_LISTINGS_ERROR:
return { ...state, queryListingsError: payload };
default:
return state;
}
@ -48,11 +63,39 @@ export const showUserError = e => ({
payload: e,
});
export const queryListingsRequest = () => ({
type: QUERY_LISTINGS_REQUEST,
});
export const queryListingsSuccess = listingRefs => ({
type: QUERY_LISTINGS_SUCCESS,
payload: { listingRefs },
});
export const queryListingsError = e => ({
type: QUERY_LISTINGS_ERROR,
error: true,
payload: e,
});
// ================ Thunks ================ //
export const queryUserListings = userId => (dispatch, getState, sdk) => {
dispatch(queryListingsRequest());
return sdk.listings
.query({ author_id: userId, include: ['images'] })
.then(response => {
// Pick only the id and type properties from the response listings
const listingRefs = response.data.data.map(({ id, type }) => ({ id, type }));
dispatch(addMarketplaceEntities(response));
dispatch(queryListingsSuccess(listingRefs));
return response;
})
.catch(e => dispatch(queryListingsError(storableError(e))));
};
export const showUser = userId => (dispatch, getState, sdk) => {
dispatch(showUserRequest(userId));
dispatch(fetchCurrentUser());
return sdk.users
.show({ id: userId, include: ['profileImage'] })
.then(response => {
@ -60,5 +103,13 @@ export const showUser = userId => (dispatch, getState, sdk) => {
dispatch(showUserSuccess());
return response;
})
.catch(e => showUserError(storableError(e)));
.catch(e => dispatch(showUserError(storableError(e))));
};
export const loadData = userId => (dispatch, getState, sdk) => {
return Promise.all([
dispatch(fetchCurrentUser()),
dispatch(showUser(userId)),
dispatch(queryUserListings(userId)),
]);
};

View file

@ -19,9 +19,10 @@ import {
Footer,
AvatarLarge,
NamedLink,
ListingCard,
} from '../../components';
import { TopbarContainer } from '../../containers';
import { showUser } from './ProfilePage.duck';
import { loadData } from './ProfilePage.duck';
import config from '../../config';
import css from './ProfilePage.css';
@ -36,6 +37,8 @@ export const ProfilePageComponent = props => {
user,
userShowInProgress,
userShowError,
queryListingsError,
listings,
intl,
} = props;
const ensuredCurrentUser = ensureCurrentUser(currentUser);
@ -44,7 +47,6 @@ export const ProfilePageComponent = props => {
ensuredCurrentUser.id && profileUser.id && ensuredCurrentUser.id.uuid === profileUser.id.uuid;
const displayName = profileUser.attributes.profile.displayName;
const bio = profileUser.attributes.profile.bio;
const listings = [];
const hasBio = !!bio;
const hasListings = listings.length > 0;
@ -87,6 +89,13 @@ export const ProfilePageComponent = props => {
<h2 className={css.listingsTitle}>
<FormattedMessage id="ProfilePage.listingsTitle" values={{ count: listings.length }} />
</h2>
<ul className={css.listings}>
{listings.map(l => (
<li className={css.listing} key={l.id.uuid}>
<ListingCard listing={l} />
</li>
))}
</ul>
</div>
) : null}
</div>
@ -94,7 +103,7 @@ export const ProfilePageComponent = props => {
let content;
if (userShowError) {
if (userShowError || queryListingsError) {
content = (
<p className={css.error}>
<FormattedMessage id="ProfilePage.loadingDataFailed" />
@ -150,9 +159,10 @@ ProfilePageComponent.defaultProps = {
currentUser: null,
user: null,
userShowError: null,
queryListingsError: null,
};
const { bool } = PropTypes;
const { bool, arrayOf } = PropTypes;
ProfilePageComponent.propTypes = {
logoutError: propTypes.error,
@ -161,6 +171,8 @@ ProfilePageComponent.propTypes = {
user: propTypes.user,
userShowInProgress: bool.isRequired,
userShowError: propTypes.error,
queryListingsError: propTypes.error,
listings: arrayOf(propTypes.listing).isRequired,
// from injectIntl
intl: intlShape.isRequired,
@ -170,9 +182,16 @@ const mapStateToProps = state => {
// Page needs logoutError
const { logoutError } = state.Auth;
const { currentUser } = state.user;
const { userId, userShowInProgress, userShowError } = state.ProfilePage;
const matches = getMarketplaceEntities(state, [{ type: 'user', id: userId }]);
const user = matches.length === 1 ? matches[0] : null;
const {
userId,
userShowInProgress,
userShowError,
queryListingsError,
userListingRefs,
} = state.ProfilePage;
const userMatches = getMarketplaceEntities(state, [{ type: 'user', id: userId }]);
const user = userMatches.length === 1 ? userMatches[0] : null;
const listings = getMarketplaceEntities(state, userListingRefs);
return {
logoutError,
scrollingDisabled: isScrollingDisabled(state),
@ -180,6 +199,8 @@ const mapStateToProps = state => {
user,
userShowInProgress,
userShowError,
queryListingsError,
listings,
};
};
@ -187,7 +208,7 @@ const ProfilePage = compose(connect(mapStateToProps), injectIntl)(ProfilePageCom
ProfilePage.loadData = params => {
const id = new UUID(params.id);
return showUser(id);
return loadData(id);
};
export default ProfilePage;

View file

@ -12,6 +12,7 @@ describe('ProfilePage', () => {
scrollingDisabled={false}
user={createUser('test-user')}
userShowInProgress={false}
listings={[]}
intl={fakeIntl}
/>
);