mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 12:43:11 +10:00
Extract images section into a separate file
This commit is contained in:
parent
df9a2b9a39
commit
f6507657d4
6 changed files with 203 additions and 204 deletions
61
src/containers/ListingPage/ActionBarMaybe.js
Normal file
61
src/containers/ListingPage/ActionBarMaybe.js
Normal 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;
|
||||
|
|
@ -17,6 +17,9 @@
|
|||
margin: 24px;
|
||||
}
|
||||
|
||||
.sectionImages {
|
||||
}
|
||||
|
||||
.threeToTwoWrapper {
|
||||
/* Layout */
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -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' }}>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
75
src/containers/ListingPage/SectionImages.js
Normal file
75
src/containers/ListingPage/SectionImages.js
Normal 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;
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue