Extract images section into a separate file

This commit is contained in:
Kimmo Puputti 2018-04-10 13:54:56 +03:00
parent df9a2b9a39
commit f6507657d4
6 changed files with 203 additions and 204 deletions

View file

@ -0,0 +1,61 @@
import React from 'react';
import { bool, oneOfType, object } from 'prop-types';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { LISTING_STATE_PENDING_APPROVAL, LISTING_STATE_CLOSED, propTypes } from '../../util/types';
import { NamedLink } from '../../components';
import EditIcon from './EditIcon';
import css from './ListingPage.css';
export const ActionBarMaybe = props => {
const { isOwnListing, listing, editParams } = props;
const state = listing.attributes.state;
const isPendingApproval = state === LISTING_STATE_PENDING_APPROVAL;
const isClosed = state === LISTING_STATE_CLOSED;
if (isOwnListing) {
let ownListingTextTranslationId = 'ListingPage.ownListing';
if (isPendingApproval) {
ownListingTextTranslationId = 'ListingPage.ownListingPendingApproval';
} else if (isClosed) {
ownListingTextTranslationId = 'ListingPage.ownClosedListing';
}
const ownListingTextClasses = classNames(css.ownListingText, {
[css.ownListingTextPendingApproval]: isPendingApproval,
});
return (
<div className={css.actionBar}>
<p className={ownListingTextClasses}>
<FormattedMessage id={ownListingTextTranslationId} />
</p>
<NamedLink className={css.editListingLink} name="EditListingPage" params={editParams}>
<EditIcon className={css.editIcon} />
<FormattedMessage id="ListingPage.editListing" />
</NamedLink>
</div>
);
} else if (isClosed) {
return (
<div className={css.actionBar}>
<p className={css.closedListingText}>
<FormattedMessage id="ListingPage.closedListing" />
</p>
</div>
);
}
return null;
};
ActionBarMaybe.propTypes = {
isOwnListing: bool.isRequired,
listing: oneOfType([propTypes.listing, propTypes.ownListing]).isRequired,
editParams: object.isRequired,
};
ActionBarMaybe.displayName = 'ActionBarMaybe';
export default ActionBarMaybe;

View file

@ -17,6 +17,9 @@
margin: 24px;
}
.sectionImages {
}
.threeToTwoWrapper {
/* Layout */
display: block;

View file

@ -1,11 +1,10 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */
import React, { Component } from 'react';
import { arrayOf, bool, func, object, shape, string, oneOf, oneOfType } from 'prop-types';
import { arrayOf, bool, func, shape, string, oneOf } from 'prop-types';
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import classNames from 'classnames';
import config from '../../config';
import routeConfiguration from '../../routeConfiguration';
import { LISTING_STATE_PENDING_APPROVAL, LISTING_STATE_CLOSED, propTypes } from '../../util/types';
@ -23,11 +22,9 @@ import {
Button,
ModalInMobile,
Page,
ResponsiveImage,
NamedLink,
NamedRedirect,
Modal,
ImageCarousel,
InlineTextButton,
LayoutSingleColumn,
LayoutWrapperTopbar,
@ -41,7 +38,7 @@ import {
import { BookingDatesForm, TopbarContainer, EnquiryForm, NotFoundPage } from '../../containers';
import { sendEnquiry, loadData, setInitialValues } from './ListingPage.duck';
import EditIcon from './EditIcon';
import SectionImages from './SectionImages';
import SectionRulesMaybe from './SectionRulesMaybe';
import SectionMapMaybe from './SectionMapMaybe';
import css from './ListingPage.css';
@ -66,56 +63,6 @@ const priceData = (price, intl) => {
return {};
};
export const ActionBarMaybe = props => {
const { isOwnListing, listing, editParams } = props;
const state = listing.attributes.state;
const isPendingApproval = state === LISTING_STATE_PENDING_APPROVAL;
const isClosed = state === LISTING_STATE_CLOSED;
if (isOwnListing) {
let ownListingTextTranslationId = 'ListingPage.ownListing';
if (isPendingApproval) {
ownListingTextTranslationId = 'ListingPage.ownListingPendingApproval';
} else if (isClosed) {
ownListingTextTranslationId = 'ListingPage.ownClosedListing';
}
const ownListingTextClasses = classNames(css.ownListingText, {
[css.ownListingTextPendingApproval]: isPendingApproval,
});
return (
<div className={css.actionBar}>
<p className={ownListingTextClasses}>
<FormattedMessage id={ownListingTextTranslationId} />
</p>
<NamedLink className={css.editListingLink} name="EditListingPage" params={editParams}>
<EditIcon className={css.editIcon} />
<FormattedMessage id="ListingPage.editListing" />
</NamedLink>
</div>
);
} else if (isClosed) {
return (
<div className={css.actionBar}>
<p className={css.closedListingText}>
<FormattedMessage id="ListingPage.closedListing" />
</p>
</div>
);
}
return null;
};
ActionBarMaybe.propTypes = {
isOwnListing: bool.isRequired,
listing: oneOfType([propTypes.listing, propTypes.ownListing]).isRequired,
editParams: object.isRequired,
};
ActionBarMaybe.displayName = 'ActionBarMaybe';
const openBookModal = (history, listing) => {
if (!listing.id) {
// Listing not fully loaded yet
@ -363,9 +310,6 @@ export class ListingPageComponent extends Component {
);
}
const hasImages = currentListing.images && currentListing.images.length > 0;
const firstImage = hasImages ? currentListing.images[0] : null;
const handleViewPhotosClick = e => {
// Stop event from bubbling up to prevent image click handler
// trying to open the carousel as well.
@ -374,15 +318,6 @@ export class ListingPageComponent extends Component {
imageCarouselOpen: true,
});
};
const viewPhotosButton = hasImages ? (
<button className={css.viewPhotos} onClick={handleViewPhotosClick}>
<FormattedMessage
id="ListingPage.viewImagesButton"
values={{ count: currentListing.images.length }}
/>
</button>
) : null;
const authorAvailable = currentListing && currentListing.author;
const userAndListingAuthorAvailable = !!(currentUser && authorAvailable);
const isOwnListing =
@ -443,18 +378,6 @@ export class ListingPageComponent extends Component {
}
};
// Action bar is wrapped with a div that prevents the click events
// to the parent that would otherwise open the image carousel
const actionBar = currentListing.id ? (
<div onClick={e => e.stopPropagation()}>
<ActionBarMaybe
isOwnListing={isOwnListing}
listing={currentListing}
editParams={editParams}
/>
</div>
) : null;
const listingImages = (listing, variantName) =>
(listing.images || [])
.map(image => {
@ -517,36 +440,16 @@ export class ListingPageComponent extends Component {
<LayoutWrapperTopbar>{topbar}</LayoutWrapperTopbar>
<LayoutWrapperMain>
<div>
<div className={css.threeToTwoWrapper}>
<div className={css.aspectWrapper} onClick={handleViewPhotosClick}>
{actionBar}
<ResponsiveImage
rootClassName={css.rootForImage}
alt={title}
image={firstImage}
nameSet={[
{ name: 'landscape-crop', size: '400w' },
{ name: 'landscape-crop2x', size: '800w' },
{ name: 'landscape-crop4x', size: '1600w' },
{ name: 'landscape-crop6x', size: '2400w' },
]}
sizes="100vw"
/>
{viewPhotosButton}
</div>
</div>
<Modal
id="ListingPage.imageCarousel"
scrollLayerClassName={css.carouselModalScrollLayer}
containerClassName={css.carouselModalContainer}
lightCloseButton
isOpen={this.state.imageCarouselOpen}
onClose={() => this.setState({ imageCarouselOpen: false })}
<SectionImages
title={title}
listing={currentListing}
isOwnListing={isOwnListing}
editParams={editParams}
imageCarouselOpen={this.state.imageCarouselOpen}
onImageCarouselClose={() => this.setState({ imageCarouselOpen: false })}
handleViewPhotosClick={handleViewPhotosClick}
onManageDisableScrolling={onManageDisableScrolling}
>
<ImageCarousel images={currentListing.images} />
</Modal>
/>
<div className={css.contentContainer}>
<div className={css.avatarWrapper}>
<NamedLink name="ListingPage" params={params} to={{ hash: '#host' }}>

View file

@ -24,7 +24,8 @@ import { showListingRequest, showListingError, showListing } from './ListingPage
// Otherwise, ListingPage itself is not initialized correctly when routeConfiguration is imported
// (loadData call fails).
import routeConfiguration from '../../routeConfiguration';
import { ListingPageComponent, ActionBarMaybe } from './ListingPage';
import { ListingPageComponent } from './ListingPage';
import ActionBarMaybe from './ActionBarMaybe';
const { UUID } = sdkTypes;
const noop = () => null;

View file

@ -0,0 +1,75 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { ResponsiveImage, Modal, ImageCarousel } from '../../components';
import ActionBarMaybe from './ActionBarMaybe';
import css from './ListingPage.css';
const SectionImages = props => {
const {
title,
listing,
isOwnListing,
editParams,
handleViewPhotosClick,
imageCarouselOpen,
onImageCarouselClose,
onManageDisableScrolling,
} = props;
const hasImages = listing.images && listing.images.length > 0;
const firstImage = hasImages ? listing.images[0] : null;
// Action bar is wrapped with a div that prevents the click events
// to the parent that would otherwise open the image carousel
const actionBar = listing.id ? (
<div onClick={e => e.stopPropagation()}>
<ActionBarMaybe isOwnListing={isOwnListing} listing={listing} editParams={editParams} />
</div>
) : null;
const viewPhotosButton = hasImages ? (
<button className={css.viewPhotos} onClick={handleViewPhotosClick}>
<FormattedMessage
id="ListingPage.viewImagesButton"
values={{ count: listing.images.length }}
/>
</button>
) : null;
return (
<div className={css.sectionImages}>
<div className={css.threeToTwoWrapper}>
<div className={css.aspectWrapper} onClick={handleViewPhotosClick}>
{actionBar}
<ResponsiveImage
rootClassName={css.rootForImage}
alt={title}
image={firstImage}
nameSet={[
{ name: 'landscape-crop', size: '400w' },
{ name: 'landscape-crop2x', size: '800w' },
{ name: 'landscape-crop4x', size: '1600w' },
{ name: 'landscape-crop6x', size: '2400w' },
]}
sizes="100vw"
/>
{viewPhotosButton}
</div>
</div>
<Modal
id="ListingPage.imageCarousel"
scrollLayerClassName={css.carouselModalScrollLayer}
containerClassName={css.carouselModalContainer}
lightCloseButton
isOpen={imageCarouselOpen}
onClose={onImageCarouselClose}
onManageDisableScrolling={onManageDisableScrolling}
>
<ImageCarousel images={listing.images} />
</Modal>
</div>
);
};
export default SectionImages;

View file

@ -34,103 +34,59 @@ exports[`ListingPage matches snapshot 1`] = `
rootClassName={null}
>
<div>
<div>
<div
onClick={[Function]}
>
<div
onClick={[Function]}
>
<ActionBarMaybe
editParams={
Object {
"id": "listing1",
"slug": "listing1-title",
"tab": "description",
"type": "edit",
}
}
isOwnListing={false}
listing={
Object {
"attributes": Object {
"deleted": false,
"description": "listing1 description",
"geolocation": LatLng {
"lat": 40,
"lng": 60,
},
"price": Money {
"amount": 5500,
"currency": "USD",
},
"publicData": Object {},
"state": "published",
"title": "listing1 title",
},
"author": Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "TT",
"displayName": "user-1 display name",
},
},
"id": UUID {
"uuid": "user-1",
},
"type": "user",
},
"id": UUID {
"uuid": "listing1",
},
"images": Array [],
"type": "listing",
}
}
/>
</div>
<ResponsiveImage
alt="listing1 title"
className={null}
image={null}
nameSet={
Array [
Object {
"name": "landscape-crop",
"size": "400w",
<SectionImages
editParams={
Object {
"id": "listing1",
"slug": "listing1-title",
"tab": "description",
"type": "edit",
}
}
handleViewPhotosClick={[Function]}
imageCarouselOpen={false}
isOwnListing={false}
listing={
Object {
"attributes": Object {
"deleted": false,
"description": "listing1 description",
"geolocation": LatLng {
"lat": 40,
"lng": 60,
},
"price": Money {
"amount": 5500,
"currency": "USD",
},
"publicData": Object {},
"state": "published",
"title": "listing1 title",
},
"author": Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "TT",
"displayName": "user-1 display name",
},
Object {
"name": "landscape-crop2x",
"size": "800w",
},
Object {
"name": "landscape-crop4x",
"size": "1600w",
},
Object {
"name": "landscape-crop6x",
"size": "2400w",
},
]
}
noImageMessage={null}
rootClassName={null}
sizes="100vw"
/>
</div>
</div>
<InjectIntl(ModalComponent)
id="ListingPage.imageCarousel"
isOpen={false}
lightCloseButton={true}
onClose={[Function]}
},
"id": UUID {
"uuid": "user-1",
},
"type": "user",
},
"id": UUID {
"uuid": "listing1",
},
"images": Array [],
"type": "listing",
}
}
onImageCarouselClose={[Function]}
onManageDisableScrolling={[Function]}
>
<InjectIntl(ImageCarousel)
images={Array []}
/>
</InjectIntl(ModalComponent)>
title="listing1 title"
/>
<div>
<div>
<NamedLink