-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Subscribers: Add subscribers-dataviews flag and component #97946
Changes from all commits
613340b
4b0d8fa
a682e73
98080d5
9fe9bec
15c656e
67ac007
64411f4
d4eb8c3
05c3240
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as SubscriberDataViews } from './subscriber-data-views'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
@import '@wordpress/base-styles/breakpoints'; | ||
@import '@wordpress/dataviews/build-style/style.css'; | ||
|
||
.subscribers.subscribers--dataviews.main { | ||
.navigation-header { | ||
padding: 0 48px; | ||
|
||
@media ( max-width: $break-small ) { | ||
padding: 0 24px; | ||
} | ||
} | ||
} | ||
|
||
.subscriber-data-views { | ||
.subscriber-profile.subscriber-profile--compact { | ||
@media ( max-width: $break-xlarge ) { | ||
max-width: 225px; | ||
} | ||
|
||
.subscriber-profile__user-details { | ||
.subscriber-profile__name { | ||
text-align: left; | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
import { useBreakpoint } from '@automattic/viewport-react'; | ||
import { DataViews } from '@wordpress/dataviews'; | ||
import { useTranslate } from 'i18n-calypso'; | ||
import { useMemo } from 'react'; | ||
import TimeSince from 'calypso/components/time-since'; | ||
import { EmptyListView } from 'calypso/my-sites/subscribers/components/empty-list-view'; | ||
import { SubscriberLaunchpad } from 'calypso/my-sites/subscribers/components/subscriber-launchpad'; | ||
import { SubscriberProfile } from 'calypso/my-sites/subscribers/components/subscriber-profile'; | ||
import { useSubscribersPage } from 'calypso/my-sites/subscribers/components/subscribers-page/subscribers-page-context'; | ||
import { useSubscriptionPlans } from 'calypso/my-sites/subscribers/hooks'; | ||
import { Subscriber } from 'calypso/my-sites/subscribers/types'; | ||
import { useSelector } from 'calypso/state'; | ||
import isAtomicSite from 'calypso/state/selectors/is-site-automated-transfer'; | ||
import { isSimpleSite } from 'calypso/state/sites/selectors'; | ||
import { SubscribersSortBy } from '../../constants'; | ||
import type { View, Field, Action } from '@wordpress/dataviews'; | ||
import './style.scss'; | ||
|
||
const SubscriptionTypeCell = ( { subscriber }: { subscriber: Subscriber } ) => { | ||
const plans = useSubscriptionPlans( subscriber ); | ||
return ( | ||
<> | ||
{ plans.map( ( plan, index ) => ( | ||
<div key={ index }>{ plan.plan }</div> | ||
) ) } | ||
</> | ||
); | ||
}; | ||
|
||
type SubscriberDataViewsProps = { | ||
siteId: number | null; | ||
onClickView: ( subscriber: Subscriber ) => void; | ||
onClickUnsubscribe: ( subscriber: Subscriber ) => void; | ||
onGiftSubscription: ( subscriber: Subscriber ) => void; | ||
}; | ||
|
||
const SubscriberDataViews = ( { | ||
siteId, | ||
onClickView, | ||
onClickUnsubscribe, | ||
}: SubscriberDataViewsProps ) => { | ||
const translate = useTranslate(); | ||
const isMobile = useBreakpoint( '<1040px' ); | ||
const { | ||
grandTotal, | ||
page, | ||
pageChangeCallback, | ||
searchTerm, | ||
isLoading, | ||
subscribers, | ||
pages, | ||
isOwnerSubscribed, | ||
perPage, | ||
setPerPage, | ||
handleSearch, | ||
sortTerm, | ||
setSortTerm, | ||
} = useSubscribersPage(); | ||
|
||
const isSimple = useSelector( isSimpleSite ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm preserving this empty view logic, but we could refactor or replace it. |
||
const isAtomic = useSelector( ( state ) => isAtomicSite( state, siteId ) ); | ||
const EmptyComponent = isSimple || isAtomic ? SubscriberLaunchpad : EmptyListView; | ||
const shouldShowLaunchpad = | ||
! isLoading && ! searchTerm && ( ! grandTotal || ( grandTotal === 1 && isOwnerSubscribed ) ); | ||
|
||
const fields = useMemo< Field< Subscriber >[] >( () => { | ||
const baseFields = [ | ||
{ | ||
id: 'name', | ||
label: translate( 'Name' ), | ||
getValue: ( { item }: { item: Subscriber } ) => item.display_name, | ||
render: ( { item }: { item: Subscriber } ) => ( | ||
<button onClick={ () => onClickView( item ) }> | ||
<SubscriberProfile | ||
avatar={ item.avatar } | ||
displayName={ item.display_name } | ||
email={ item.email_address } | ||
url={ item.url } | ||
/> | ||
</button> | ||
), | ||
enableHiding: false, | ||
enableSorting: true, | ||
}, | ||
]; | ||
|
||
if ( ! isMobile ) { | ||
baseFields.push( | ||
{ | ||
id: 'subscription_type', | ||
label: translate( 'Subscription type' ), | ||
getValue: ( { item }: { item: Subscriber } ) => ( item.plans?.length ? 'Paid' : 'Free' ), | ||
render: ( { item }: { item: Subscriber } ) => ( | ||
<SubscriptionTypeCell subscriber={ item } /> | ||
), | ||
enableHiding: false, | ||
enableSorting: true, | ||
}, | ||
{ | ||
id: 'date_subscribed', | ||
label: translate( 'Since' ), | ||
getValue: ( { item }: { item: Subscriber } ) => item.date_subscribed, | ||
render: ( { item }: { item: Subscriber } ) => <TimeSince date={ item.date_subscribed } />, | ||
enableHiding: false, | ||
enableSorting: true, | ||
} | ||
); | ||
} | ||
|
||
return baseFields; | ||
}, [ translate, onClickView, isMobile ] ); | ||
|
||
const actions = useMemo< Action< Subscriber >[] >( | ||
() => [ | ||
{ | ||
id: 'view', | ||
label: translate( 'View' ), | ||
callback: ( items: Subscriber[] ) => onClickView( items[ 0 ] ), | ||
isPrimary: true, | ||
}, | ||
{ | ||
id: 'remove', | ||
label: translate( 'Remove' ), | ||
callback: ( items: Subscriber[] ) => onClickUnsubscribe( items[ 0 ] ), | ||
}, | ||
], | ||
[ translate, onClickView, onClickUnsubscribe ] | ||
); | ||
|
||
const handleViewChange = ( newView: View ) => { | ||
if ( typeof newView.page === 'number' && newView.page !== page ) { | ||
pageChangeCallback( newView.page ); | ||
} | ||
|
||
if ( typeof newView.perPage === 'number' && newView.perPage !== perPage ) { | ||
setPerPage( newView.perPage ); | ||
pageChangeCallback( 1 ); | ||
} | ||
|
||
if ( typeof newView.search === 'string' && newView.search !== searchTerm ) { | ||
handleSearch( newView.search ); | ||
} | ||
|
||
if ( newView.sort?.field ) { | ||
const newSortTerm = | ||
newView.sort.field === 'name' ? SubscribersSortBy.Name : SubscribersSortBy.DateSubscribed; | ||
if ( newSortTerm !== sortTerm ) { | ||
setSortTerm( newSortTerm ); | ||
} | ||
} | ||
}; | ||
|
||
const currentView = useMemo< View >( | ||
() => ( { | ||
type: 'table', | ||
layout: {}, | ||
search: searchTerm, | ||
page, | ||
perPage, | ||
sort: { | ||
field: sortTerm === SubscribersSortBy.Name ? 'name' : 'date_subscribed', | ||
direction: 'desc', | ||
}, | ||
} ), | ||
[ searchTerm, page, perPage, sortTerm ] | ||
); | ||
|
||
const { data, paginationInfo } = useMemo( () => { | ||
return { | ||
data: subscribers, | ||
paginationInfo: { | ||
totalItems: grandTotal, | ||
totalPages: pages ?? 0, | ||
}, | ||
}; | ||
}, [ subscribers, grandTotal, pages ] ); | ||
|
||
return ( | ||
<section className="subscriber-data-views"> | ||
{ shouldShowLaunchpad ? ( | ||
<EmptyComponent /> | ||
) : ( | ||
<DataViews< Subscriber > | ||
data={ data } | ||
fields={ fields } | ||
view={ currentView } | ||
onChangeView={ handleViewChange } | ||
isLoading={ isLoading } | ||
paginationInfo={ paginationInfo } | ||
getItemId={ ( item: Subscriber ) => item.subscription_id.toString() } | ||
defaultLayouts={ { table: {} } } | ||
actions={ actions } | ||
search | ||
searchLabel={ translate( 'Search by name, username or email…' ) } | ||
/> | ||
) } | ||
</section> | ||
); | ||
}; | ||
|
||
export default SubscriberDataViews; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import { isEnabled } from '@automattic/calypso-config'; | ||
import page from '@automattic/calypso-router'; | ||
import { Button, Gridicon } from '@automattic/components'; | ||
import { HelpCenter, Subscriber as SubscriberDataStore } from '@automattic/data-stores'; | ||
|
@@ -14,6 +15,7 @@ import NavigationHeader from 'calypso/components/navigation-header'; | |
import SubscriberValidationGate from 'calypso/components/subscribers-validation-gate'; | ||
import isJetpackCloud from 'calypso/lib/jetpack/is-jetpack-cloud'; | ||
import GiftSubscriptionModal from 'calypso/my-sites/subscribers/components/gift-modal/gift-modal'; | ||
import { SubscriberDataViews } from 'calypso/my-sites/subscribers/components/subscriber-data-views'; | ||
import { SubscriberListContainer } from 'calypso/my-sites/subscribers/components/subscriber-list-container'; | ||
import { | ||
SubscribersPageProvider, | ||
|
@@ -179,20 +181,36 @@ const SubscribersPage = ( { | |
sortTermChanged={ sortTermChanged } | ||
> | ||
<QueryMembershipsSettings siteId={ siteId ?? 0 } source="calypso" /> | ||
<Main wideLayout className="subscribers"> | ||
<Main | ||
wideLayout | ||
className={ `subscribers${ | ||
isEnabled( 'subscribers-dataviews' ) ? ' subscribers--dataviews' : '' | ||
}` } | ||
> | ||
<DocumentHead title={ translate( 'Subscribers' ) } /> | ||
|
||
<SubscribersHeader | ||
selectedSiteId={ selectedSite?.ID } | ||
disableCta={ isUnverified || isStagingSite } | ||
/> | ||
<SubscriberValidationGate siteId={ siteId }> | ||
<SubscriberListContainer | ||
siteId={ siteId } | ||
onClickView={ onClickView } | ||
onGiftSubscription={ onGiftSubscription } | ||
onClickUnsubscribe={ onClickUnsubscribe } | ||
/> | ||
{ isEnabled( 'subscribers-dataviews' ) ? ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm showing the new DataViews component when the flag is enabled. I repurposed a lot from the existing |
||
// Your new dataviews component | ||
<SubscriberDataViews | ||
siteId={ siteId } | ||
onClickView={ onClickView } | ||
onGiftSubscription={ onGiftSubscription } | ||
onClickUnsubscribe={ onClickUnsubscribe } | ||
/> | ||
) : ( | ||
// Existing subscriber list | ||
<SubscriberListContainer | ||
siteId={ siteId } | ||
onClickView={ onClickView } | ||
onGiftSubscription={ onGiftSubscription } | ||
onClickUnsubscribe={ onClickUnsubscribe } | ||
/> | ||
) } | ||
|
||
<UnsubscribeModal | ||
subscriber={ currentSubscriber } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found that we're not returning plans data from the endpoint used here. So this is how it works in the individual subscriber page, but it's not actually working yet.