diff --git a/app/assets/stylesheets/crayons.scss b/app/assets/stylesheets/crayons.scss index 0f4d0a1a0..defc39419 100644 --- a/app/assets/stylesheets/crayons.scss +++ b/app/assets/stylesheets/crayons.scss @@ -28,4 +28,5 @@ @import 'views/article'; @import 'views/article-form'; +@import 'views/listings'; @import 'views/dashboard'; diff --git a/app/assets/stylesheets/listings.scss b/app/assets/stylesheets/listings.scss index 27899f469..95fd1de94 100644 --- a/app/assets/stylesheets/listings.scss +++ b/app/assets/stylesheets/listings.scss @@ -2,292 +2,163 @@ @import 'mixins'; .listings-container { - margin: 24px 0; - text-align: left; - max-width: 100%; - min-height: 100vh; - .listing-filters { - padding: 0 16px; - a { - padding: 3px 16px; - border-radius: 100px; - display: inline-block; - font-size: 15px; - &.selected { - background: $bold-blue; - color: white; + &.listings-form-container { + form { + margin: 60px auto 0px; + width: 100%; + max-width: 580px; + background: var(--card-bg); + box-sizing: border-box; + border: 1px solid var(--card-color-tertiary); + @media screen and (min-width: 580px) { + border-radius: 8px; + margin: 20px auto 20px; } - &.new-listing-button { - color: $black; - background: $green; - margin: 0px 4px; + * { + box-sizing: border-box; } - } - .listing-filters-categories { - a { - display: block; - padding: 7px 16px; - &.listing-create-link { - border: 2px solid darken($green, 14%); - color: darken($green, 19%); - padding: 6px 16px; - margin-top: 6px; + header { + padding: 25px 22px; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + } + .listing-form-inner, + .listings-form-inner { + padding: 0px 15px 40px; + @media screen and (min-width: 580px) { + padding: 0px 15px 18px; } } - @media screen and (min-width: 550px) { - a { - display: inline-block; - margin: 0 8px 8px 0; - border: 1px solid $bold-blue; - min-width: 50px; - text-align: center; - padding: 3px 16px; - &.listing-create-link { - padding: 2px 16px; - } - } - } - } - .listing-filters-tags { - display: flex; - padding: 20px 0px; - position: relative; - input { + .listing-back-button { font-size: 0.8em; + margin: 2px 0px 8px; + display: block; + } + h2 { + font-size: 1.45em; + color: var(--accent-brand); + margin: 0px; + } + .listing-errors { + background: $red; + color: $black; + padding: 20px; + border-radius: 3px; + h2 { + color: $black; + } + } + label { + display: inline-block; + font-size: 0.7em; + margin: 12px 0px 3px; + border-radius: 3px; + padding: 6px 0px; + font-weight: bold; + } + input { + width: 100%; + font-size: 18px; + padding: 10px 7px; + border-radius: 3px; + border: 1px solid var(--form-border); + background: var(--form-bg); color: var(--body-color); - @include themeable( - background, - theme-container-accent-background, - $light-gray + &[type='checkbox'] { + width: 100px; + } + } + textarea { + width: 100%; + height: 300px; + padding: 5px; + font-size: 17px; + border-radius: 3px; + background: var(--form-bg); + color: var(--body-color); + border: 1px solid var(--form-border); + } + select { + width: 100%; + font-size: 20px; + } + details { + cursor: pointer; + font-size: 0.8em; + padding: 8px 0px; + color: var(--card-color-tertiary); + } + .cta-main-listing-form { + border: 0px; + padding: 12px 0px; + border-radius: 3px; + background-color: $dark-purple; + background-image: linear-gradient( + 141deg, + $dark-purple 0%, + #1f89db 51%, + #2981e5 75% ); border-radius: 100px; - padding: 3px 14px; - border: 1px solid $light-medium-gray; - width: calc(100% - 26px); - margin-bottom: 4px; - @media screen and (min-width: 550px) { - width: 280px; - margin-right: 8px; + font-size: 1.4em; + color: white; + margin-top: 20px; + width: 380px; + max-width: 90%; + margin: 35px auto; + display: block; + text-align: center; + @include themeable( + box-shadow, + theme-container-box-shadow, + 2px 2px 8px darken($light-medium-gray, 5%) + ); + &.cta-draft { + color: $black; + background: $yellow; } } - .listing-search-clear { - background: $light-gray; - border: 1px solid $light-medium-gray; - position: absolute; - left: calc(100% - 25px); - top: 24px; - border-radius: 8px; - @media screen and (min-width: 550px) { - left: 282px; - } - } - .listing-tag { - font-size: 0.7em; - color: $black; - border-radius: 100px; - display: inline-block; - background: $dark-gray; - margin-right: 5px; - a { - padding: 0px 8px; - } - &.clear-all { - background: $light-medium-gray; - a { - color: $black; - } - } - span { - color: white; - padding: 3px 2px; + .listings-current-credits { + position: fixed; + bottom: 0px; + left: 0px; + right: 0px; + padding: 18px 0px; + background-color: #aecfef; + background-image: linear-gradient( + 141deg, + #aecfef 0%, + #ddeeff 51%, + #c8ebf1 75% + ); + color: #32325d; + text-align: center; + z-index: 20; + font-size: 0.85em; + .listings-current-credits-inner { display: inline-block; - &.tag-name { - padding-right: 0px; + margin: 20px; + select { + width: initial; } } - } - } - } - .listings-columns { - display: grid; - grid-gap: 24px; - padding: 0 16px; - grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); - grid-auto-rows: 0; - } - form { - margin: 60px auto 0px; - width: 100%; - max-width: 580px; - background: var(--card-bg); - box-sizing: border-box; - border: 1px solid var(--card-color-tertiary); - @media screen and (min-width: 580px) { - border-radius: 8px; - margin: 20px auto 20px; - } - * { - box-sizing: border-box; - } - header { - padding: 25px 22px; - border-top-left-radius: 8px; - border-top-right-radius: 8px; - } - .listing-form-inner { - padding: 0px 15px 40px; - @media screen and (min-width: 580px) { - padding: 0px 15px 18px; - } - } - .listing-back-button { - font-size: 0.8em; - margin: 2px 0px 8px; - display: block; - } - h2 { - font-size: 1.45em; - color: var(--accent-brand); - margin: 0px; - } - .listing-errors { - background: $red; - color: $black; - padding: 20px; - border-radius: 3px; - h2 { - color: $black; - } - } - label { - display: inline-block; - font-size: 0.7em; - margin: 12px 0px 3px; - border-radius: 3px; - padding: 6px 0px; - font-weight: bold; - } - input { - width: 100%; - font-size: 18px; - padding: 10px 7px; - border-radius: 3px; - border: 1px solid var(--form-border); - background: var(--form-bg); - color: var(--body-color); - &[type='checkbox'] { - width: 100px; - } - } - textarea { - width: 100%; - height: 300px; - padding: 5px; - font-size: 17px; - border-radius: 3px; - background: var(--form-bg); - color: var(--body-color); - border: 1px solid var(--form-border); - } - select { - width: 100%; - font-size: 20px; - } - details { - cursor: pointer; - font-size: 0.8em; - padding: 8px 0px; - color: var(--card-color-tertiary); - } - .cta-main-listing-form { - border: 0px; - padding: 12px 0px; - border-radius: 3px; - background-color: $dark-purple; - background-image: linear-gradient( - 141deg, - $dark-purple 0%, - #1f89db 51%, - #2981e5 75% - ); - border-radius: 100px; - font-size: 1.4em; - color: white; - margin-top: 20px; - width: 380px; - max-width: 90%; - margin: 35px auto; - display: block; - text-align: center; - @include themeable( - box-shadow, - theme-container-box-shadow, - 2px 2px 8px darken($light-medium-gray, 5%) - ); - &.cta-draft { - color: $black; - background: $yellow; - } - } - .listings-current-credits { - position: fixed; - bottom: 0px; - left: 0px; - right: 0px; - padding: 18px 0px; - background-color: #aecfef; - background-image: linear-gradient( - 141deg, - #aecfef 0%, - #ddeeff 51%, - #c8ebf1 75% - ); - color: #32325d; - text-align: center; - z-index: 20; - font-size: 0.85em; - .listings-current-credits-inner { - display: inline-block; - margin: 20px; - select { - width: initial; + a { + text-decoration: underline; + display: inline-block; + color: var(--link-brand-color); + } + @media screen and (min-width: 580px) { + font-size: 1.1em; } } - a { - text-decoration: underline; - display: inline-block; - color: var(--link-brand-color); + .listings-unpublish { + background: $red; } - @media screen and (min-width: 580px) { - font-size: 1.1em; + p { + font-size: 0.8em; + color: var(--card-color-tertiary); } } - .listings-unpublish { - background: $red; - } - p { - font-size: 0.8em; - color: var(--card-color-tertiary); - } - } - .listings-load-more-button { - text-align: center; - - button { - background: var(--theme-container-background); - color: var(--body-color); - border: 1px solid var(--card-color-tertiary); - font-size: 17px; - padding: 14px 5px; - margin: 40px auto 70px; - width: 320px; - max-width: 80%; - border-radius: 100px; - font-weight: bold; - } - } - .field { - font-size: 18px; } } @@ -343,230 +214,6 @@ font-weight: bold; } -.single-listing-container { - width: 100%; - z-index: 20; - position: fixed; - left: 0; - top: 0; - right: 0; - bottom: 0px; - margin: 12px auto 0px; - overflow: hidden; - .single-listing-container__inner { - position: absolute; - height: 100%; - width: 100%; - overflow-y: auto; - box-sizing: content-box; - padding-right: 17px; - -webkit-overflow-scrolling: touch; - padding-top: 95px; - margin-bottom: 150px; - @media screen and (min-width: 950px) { - left: 0; - right: 0; - box-sizing: border-box; - } - .single-listing-container__spacer { - height: 150px; - margin-bottom: 150px; - } - } - .single-listing-container__inner::-webkit-scrollbar { - display: none; - } -} - -.single-listing { - width: 100%; - display: inline-block; - border: 1px solid $light-medium-gray; - box-sizing: border-box; - border-radius: 3px; - overflow: hidden; - text-overflow: ellipsis; - font-size: 0.9em; - break-inside: avoid; - background: var(--card-bg); - h3 { - display: flex; - justify-content: space-between; - margin: 0px; - padding: 9px 3px 9px 12px; - position: relative; - @include themeable( - background, - theme-container-accent-background, - lighten($light-medium-gray, 8%) - ); - border-bottom: 1px solid $light-medium-gray; - font-size: 16px; - a { - color: var(--body-color); - } - } - .dropdown-btn { - background: none; - border: none; - cursor: pointer; - - .dropdown-icon { - max-width: 15px; - max-height: 15px; - } - } - - .dropdown { - position: absolute; - bottom: 0; - right: 0; - display: inline-block; - - .dropdown-content { - display: none; - position: absolute; - right: 0; - border: 1px solid $light-medium-gray; - border-radius: 3px; - background: white; - z-index: 20; - width: 130px; - box-shadow: $shadow; - &.showing { - display: block; - } - } - - .dropdown-content a { - color: black; - padding: 12px 16px; - width: 98px; - height: 14px; - font-weight: bold; - display: block; - font-size: 14px; - white-space: nowrap; - } - - .dropdown-content a:hover { - background-color: #f1f1f1; - } - } - .single-listing-body { - padding: 10px 12px; - font-size: 15px; - .listing-contact-cta { - border: 2px solid $black; - color: $black; - padding: 8px 10px; - text-align: center; - display: block; - border-radius: 200px; - margin: 4px auto; - width: 80%; - } - } - .single-listing-tags { - padding: 10px 15px; - a { - color: var(--card-color-tertiary); - @include themeable( - background, - theme-container-accent-background, - $light-medium-gray - ); - display: inline-block; - margin: 2px; - padding: 2px 7px; - font-size: 0.77em; - border-radius: 3px; - font-weight: bold; - } - } - .single-listing-author-info { - font-size: 0.69em; - color: var(--card-color-tertiary); - padding: 0px 15px 4px; - a { - color: var(--card-color-tertiary); - display: inline-block; - &.listing-edit-button { - margin-left: 4px; - } - } - } - &.single-listing--opened { - z-index: 20; - display: block; - position: relative; - left: 0px; - right: 0px; - padding-bottom: 12px; - max-width: 800px; - margin: auto; - @media screen and (min-width: 950px) { - h3 { - font-size: calc(20px + 0.05vw); - } - .single-listing-body { - font-size: calc(18px + 0.05vw); - } - } - } -} -.single-listing-info-link { - display: block; - width: 320px; - max-width: 92%; - margin: auto; - padding: 14px 0px; - text-align: center; - border-radius: 100px; - font-weight: bold; - margin: 40px auto 10px; - color: var(--body-color); - border: 3px solid var(--body-color); - background: var(--card-bg); -} - -form.listings-contact-via-connect { - border-radius: 3px; - z-index: 20; - position: relative; - padding: 10px 15px; - margin: 12px auto; - width: 100%; - padding-bottom: 12px; - box-shadow: none; - max-width: 100%; - text-align: right; - p { - text-align: left; - } - width: 94%; - max-width: 800px; - textarea#new-message { - height: 100px; - margin: 5px auto 10px auto; - font-size: 15px; - @media screen and (min-width: 950px) { - font-size: calc(18px + 0.05vw); - } - } - .submit-button { - margin: none 10px; - padding: 0.5em; - height: 2em; - width: 4em; - font-size: 1em; - line-height: 1em; - text-align: center; - border-radius: 3px; - border: 0px; - } -} - .listings-modal-background { z-index: 15; position: fixed; diff --git a/app/assets/stylesheets/views/listings.scss b/app/assets/stylesheets/views/listings.scss new file mode 100644 index 000000000..20f64b4a8 --- /dev/null +++ b/app/assets/stylesheets/views/listings.scss @@ -0,0 +1,31 @@ +@import '../variables'; +@import '../config/import'; + +.listings-columns { + margin-bottom: var(--su-6); + + @media (min-width: $breakpoint-m) { + display: grid; + grid-gap: var(--su-4); + grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); + } +} + +.single-listing { + $el: &; + overflow-wrap: anywhere; + word-break: break-word; + overflow: hidden; + + &__dropdown { + display: none; + + #{$el}:hover & { + display: block; + } + } + + &__tags { + min-height: var(--su-4); + } +} diff --git a/app/javascript/listings/__tests__/AllListings.test.jsx b/app/javascript/listings/__tests__/AllListings.test.jsx index 6c7ce3468..c5193671c 100644 --- a/app/javascript/listings/__tests__/AllListings.test.jsx +++ b/app/javascript/listings/__tests__/AllListings.test.jsx @@ -89,7 +89,7 @@ describe('', () => { // Ensure each listing is present const titleOptions = { - selector: 'h3 > a', + selector: 'h2 > a', }; // 1st listings diff --git a/app/javascript/listings/__tests__/ListingFiltersCategories.test.jsx b/app/javascript/listings/__tests__/ListingFiltersCategories.test.jsx index 5accce6da..a60b4c98e 100644 --- a/app/javascript/listings/__tests__/ListingFiltersCategories.test.jsx +++ b/app/javascript/listings/__tests__/ListingFiltersCategories.test.jsx @@ -4,6 +4,10 @@ import { axe } from 'jest-axe'; import ListingFiltersCategories from '../components/ListingFiltersCategories'; describe('', () => { + beforeEach(() => { + global.selectNavigation = jest.fn(); + }); + const firstCategory = { slug: 'clojure', name: 'Clojure', @@ -39,9 +43,8 @@ describe('', () => { it('should render an all link', () => { const { getByText } = renderListingFilterCategories(); - const allLink = getByText(/all/i); + const allLink = getByText(/all listings/i, { selector: 'a' }); expect(allLink.getAttribute('href')).toEqual('/listings'); - expect(allLink.textContent).toEqual('all'); }); it('should be "selected" when there is no category selected', () => { @@ -52,27 +55,13 @@ describe('', () => { expect(queryByTestId('selected')).toBeDefined(); }); - - it('should render a create listing link', () => { - const { getByText } = renderListingFilterCategories(); - const createListing = getByText(/create a listing/i); - expect(createListing.getAttribute('href')).toContain('/listings/new'); - }); - - it('should render a manage listings link', () => { - const { getByText } = renderListingFilterCategories(); - const manageListing = getByText(/manage listings/i); - expect(manageListing.getAttribute('href')).toContain( - '/listings/dashboard', - ); - }); }); describe('should render all the categories links', () => { it('should render the categories name and their respective links', () => { const { getByText } = renderListingFilterCategories(); categories.forEach((category) => { - const categoryLink = getByText(`${category.name}`); + const categoryLink = getByText(`${category.name}`, { selector: 'a' }); expect(categoryLink.getAttribute('href')).toEqual( `/listings/${category.slug}`, ); diff --git a/app/javascript/listings/__tests__/MessageModal.test.jsx b/app/javascript/listings/__tests__/MessageModal.test.jsx index dee47a417..a8ba4efc7 100644 --- a/app/javascript/listings/__tests__/MessageModal.test.jsx +++ b/app/javascript/listings/__tests__/MessageModal.test.jsx @@ -60,13 +60,6 @@ describe('', () => { ), ).toBeDefined(); }); - - it('should show the personalized message about the interactions', () => { - const { getByTestId } = renderMessageModal(listingWithCurrentUserId); - expect( - getByTestId('personal-message-about-interactions').textContent, - ).toEqual('All private interactions must abide by the code of conduct'); - }); }); describe('When current user is not the author', () => { @@ -86,16 +79,5 @@ describe('', () => { ), ).toBeDefined(); }); - - it('should show a generic message about the interactions', () => { - const { getByTestId } = renderMessageModal( - listingWithDifferentCurrentUserId, - ); - expect( - getByTestId('generic-message-about-interactions').textContent, - ).toEqual( - 'Message must be relevant and on-topic with the listing. All private interactions must abide by the code of conduct', - ); - }); }); }); diff --git a/app/javascript/listings/__tests__/NextPageButton.test.jsx b/app/javascript/listings/__tests__/NextPageButton.test.jsx index 2aa2f277c..d0d165840 100644 --- a/app/javascript/listings/__tests__/NextPageButton.test.jsx +++ b/app/javascript/listings/__tests__/NextPageButton.test.jsx @@ -20,13 +20,13 @@ describe('', () => { it('should show a button', () => { const { queryByText } = render(); - expect(queryByText(/load more listings/i)).toBeDefined(); + expect(queryByText(/load more/i)).toBeDefined(); }); it('should call the onclick handler', () => { const onClick = jest.fn(); const { getByText } = render(); - const button = getByText(/load more listings/i); + const button = getByText(/load more/i); button.click(); expect(onClick).toHaveBeenCalledTimes(1); diff --git a/app/javascript/listings/__tests__/SingleListing.test.jsx b/app/javascript/listings/__tests__/SingleListing.test.jsx index c89dcae37..ee6d1815b 100644 --- a/app/javascript/listings/__tests__/SingleListing.test.jsx +++ b/app/javascript/listings/__tests__/SingleListing.test.jsx @@ -2,7 +2,7 @@ import { h } from 'preact'; import { axe } from 'jest-axe'; import { render } from '@testing-library/preact'; -import SingleListing from '../singleListing'; +import { SingleListing } from '../singleListing/SingleListing'; const listing = { id: 22, diff --git a/app/javascript/listings/components/AllListings.jsx b/app/javascript/listings/components/AllListings.jsx index 2455361a8..8f509391f 100644 --- a/app/javascript/listings/components/AllListings.jsx +++ b/app/javascript/listings/components/AllListings.jsx @@ -1,6 +1,7 @@ import { h } from 'preact'; import PropTypes from 'prop-types'; -import SingleListing from '../singleListing'; +import { SingleListing } from '../singleListing/SingleListing'; +import NextPageButton from './NextPageButton'; const AllListings = ({ listings, @@ -8,20 +9,26 @@ const AllListings = ({ onChangeCategory, currentUserId, onOpenModal, + showNextPageButton, + loadNextPage, }) => { return ( -
- {listings.map((listing) => ( - - ))} -
+
+
+ {listings.map((listing) => ( + + ))} +
+ + {showNextPageButton && } +
); }; @@ -31,6 +38,8 @@ AllListings.propTypes = { onAddTag: PropTypes.func.isRequired, onChangeCategory: PropTypes.func.isRequired, onOpenModal: PropTypes.func.isRequired, + showNextPageButton: PropTypes.bool.isRequired, + loadNextPage: PropTypes.func.isRequired, }; AllListings.defaultProps = { diff --git a/app/javascript/listings/components/CategoryLinks.jsx b/app/javascript/listings/components/CategoryLinks.jsx index c2d958b1d..303094050 100644 --- a/app/javascript/listings/components/CategoryLinks.jsx +++ b/app/javascript/listings/components/CategoryLinks.jsx @@ -14,7 +14,9 @@ const CategoryLinks = ({ categories, onClick, selectedCategory }) => { onClick(e, category.slug)} data-no-instant {...dataTestIdProp} diff --git a/app/javascript/listings/components/CategoryLinksMobile.jsx b/app/javascript/listings/components/CategoryLinksMobile.jsx new file mode 100644 index 000000000..b03a9caa6 --- /dev/null +++ b/app/javascript/listings/components/CategoryLinksMobile.jsx @@ -0,0 +1,52 @@ +/* + global selectNavigation +*/ +import { h, Component } from 'preact'; +import PropTypes from 'prop-types'; + +export class CategoryLinks extends Component { + componentDidMount() { + selectNavigation('mobile_nav_listings'); + } + + render() { + const { categories, selectedCategory } = this.props; + + return ( +
+ +
+ ); + } +} + +CategoryLinks.propTypes = { + categories: PropTypes.arrayOf( + PropTypes.shape({ + slug: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + }), + ).isRequired, + selectedCategory: PropTypes.string.isRequired, +}; + +export default CategoryLinks; diff --git a/app/javascript/listings/components/ClearQueryButton.jsx b/app/javascript/listings/components/ClearQueryButton.jsx index e8ed309c6..56a8590ea 100644 --- a/app/javascript/listings/components/ClearQueryButton.jsx +++ b/app/javascript/listings/components/ClearQueryButton.jsx @@ -1,16 +1,17 @@ import { h } from 'preact'; import PropTypes from 'prop-types'; +import { Button } from '@crayons/Button/Button'; const ClearQueryButton = ({ onClick }) => ( - + ); ClearQueryButton.propTypes = { diff --git a/app/javascript/listings/components/ListingFilters.jsx b/app/javascript/listings/components/ListingFilters.jsx index d491c1d99..c23dcc0df 100644 --- a/app/javascript/listings/components/ListingFilters.jsx +++ b/app/javascript/listings/components/ListingFilters.jsx @@ -16,12 +16,7 @@ const ListingFilters = ({ query, }) => { return ( -
- +
+ + ); }; diff --git a/app/javascript/listings/components/ListingFiltersCategories.jsx b/app/javascript/listings/components/ListingFiltersCategories.jsx index eff67193d..151372602 100644 --- a/app/javascript/listings/components/ListingFiltersCategories.jsx +++ b/app/javascript/listings/components/ListingFiltersCategories.jsx @@ -1,38 +1,30 @@ import { h } from 'preact'; import PropTypes from 'prop-types'; import CategoryLinks from './CategoryLinks'; +import CategoryLinksMobile from './CategoryLinksMobile'; const ListingFiltersCategories = ({ categories, category, onClick }) => ( -
- - all - - - - Create a Listing - - - Manage Listings - + ); diff --git a/app/javascript/listings/components/ListingFiltersTags.jsx b/app/javascript/listings/components/ListingFiltersTags.jsx index f8314690d..cdf035197 100644 --- a/app/javascript/listings/components/ListingFiltersTags.jsx +++ b/app/javascript/listings/components/ListingFiltersTags.jsx @@ -15,13 +15,14 @@ const ListingFiltersTags = ({ const shouldRenderClearQueryButton = query.length > 0; return ( -
+
diff --git a/app/javascript/listings/components/MessageModal.jsx b/app/javascript/listings/components/MessageModal.jsx index d2a0e47ce..d2dee74ac 100644 --- a/app/javascript/listings/components/MessageModal.jsx +++ b/app/javascript/listings/components/MessageModal.jsx @@ -1,5 +1,6 @@ import { h } from 'preact'; import PropTypes from 'prop-types'; +import { Button } from '@crayons'; const MessageModal = ({ currentUserId, @@ -14,58 +15,48 @@ const MessageModal = ({
- {isCurrentUserOnListing ? ( -

- This is your active listing. Any member can contact you via this form. -

- ) : ( -

- - Contact - {` ${listing.author.name} `} - via DEV Connect - -

- )} +
+

Interested?

+ {isCurrentUserOnListing ? ( +

+ This is your active listing. Any member can contact you via this + form. +

+ ) : ( +

Message {` ${listing.author.name} `}

+ )} +