diff --git a/client/lib/user/user.d.ts b/client/lib/user/user.d.ts index 92c672c3f9d92..3dafc6b05faf7 100644 --- a/client/lib/user/user.d.ts +++ b/client/lib/user/user.d.ts @@ -39,6 +39,7 @@ export type OptionalUserData = { has_jetpack_partner_access?: boolean; jetpack_partner_types?: string[]; social_login_connections: unknown; + user_login: string; user_ip_country_code: string; user_URL: string; username: string; diff --git a/client/reader/list-manage/types.d.ts b/client/reader/list-manage/types.d.ts index 8d53ac73e90eb..0d7bf4515d662 100644 --- a/client/reader/list-manage/types.d.ts +++ b/client/reader/list-manage/types.d.ts @@ -3,6 +3,7 @@ export type List = { description: string; is_public: boolean; is_owner: boolean; + owner: string; slug: string; title: string; }; diff --git a/client/reader/user-stream/style.scss b/client/reader/user-stream/style.scss index 31d67c5539792..2c74522a7e495 100644 --- a/client/reader/user-stream/style.scss +++ b/client/reader/user-stream/style.scss @@ -17,4 +17,23 @@ border-block-end: none; } } + + .user-profile__lists-body { + max-width: 768px; + margin: 0 auto; + + &-link { + color: var(--color-neutral-100); + cursor: pointer; + display: block; + line-height: 29px; + text-wrap: pretty; + + .reader-post-card__title { + margin-top: 0; + font-size: 1.25rem; + font-weight: 600; + } + } + } } diff --git a/client/reader/user-stream/views/lists.tsx b/client/reader/user-stream/views/lists.tsx index 84c3c99c21244..f953395178a51 100644 --- a/client/reader/user-stream/views/lists.tsx +++ b/client/reader/user-stream/views/lists.tsx @@ -1,27 +1,88 @@ import { formatListBullets, Icon } from '@wordpress/icons'; import { useTranslate } from 'i18n-calypso'; +import { useEffect, useState } from 'react'; +import { connect } from 'react-redux'; import EmptyContent from 'calypso/components/empty-content'; import { UserData } from 'calypso/lib/user/user'; +import { List } from 'calypso/reader/list-manage/types'; import UserProfileHeader from 'calypso/reader/user-stream/components/user-profile-header'; +import { requestUserLists } from 'calypso/state/reader/lists/actions'; + +interface AppState { + reader: { + lists: { + userLists: Record< string, List[] >; + isRequestingUserLists: Record< string, boolean >; + }; + }; +} interface UserListsProps { user: UserData; + requestUserLists?: ( userSlug: string ) => void; + lists?: List[]; + isLoading?: boolean; } -const UserLists = ( { user }: UserListsProps ): JSX.Element => { +const UserLists = ( { user, requestUserLists, lists, isLoading }: UserListsProps ): JSX.Element => { const translate = useTranslate(); + const [ hasRequested, setHasRequested ] = useState( false ); + const userSlug = user.user_login; + + useEffect( () => { + if ( ! hasRequested && requestUserLists && userSlug ) { + requestUserLists( userSlug ); + setHasRequested( true ); + } + }, [ userSlug, requestUserLists, hasRequested ] ); + + if ( isLoading || ! hasRequested ) { + return <>; + } + + if ( ! lists || lists.length === 0 ) { + return ( +
+ + } + title={ null } + line={ translate( 'No lists yet.' ) } + /> +
+ ); + } return ( -
+
- } - title={ null } - line={ translate( 'No lists yet.' ) } - /> +
+ { lists.map( ( list: List ) => ( + +
+
+

{ list.title }

+
+
{ list.description }
+
+
+ ) ) } +
); }; -export default UserLists; +export default connect( + ( state: AppState, ownProps: UserListsProps ) => ( { + lists: state.reader.lists.userLists[ ownProps.user.user_login ?? '' ] ?? [], + isLoading: state.reader.lists.isRequestingUserLists[ ownProps.user.user_login ?? '' ] ?? false, + } ), + { + requestUserLists, + } +)( UserLists ); diff --git a/client/state/data-layer/wpcom/read/lists/index.js b/client/state/data-layer/wpcom/read/lists/index.js index 0bc12a4ce145e..f1f4a5de25240 100644 --- a/client/state/data-layer/wpcom/read/lists/index.js +++ b/client/state/data-layer/wpcom/read/lists/index.js @@ -12,6 +12,8 @@ import { READER_LIST_UNFOLLOW, READER_LIST_UPDATE, READER_LISTS_REQUEST, + READER_USER_LISTS_REQUEST, + READER_USER_LISTS_RECEIVE, } from 'calypso/state/reader/action-types'; import { handleReaderListRequestFailure, @@ -136,13 +138,14 @@ registerHandlers( 'state/data-layer/wpcom/read/lists/index.js', { ], } ), ], + // Request public and private lists for the current user [ READER_LISTS_REQUEST ]: [ dispatchRequest( { fetch: ( action ) => http( { method: 'GET', - path: `/read/lists`, + path: '/read/lists', apiVersion: '1.2', }, action @@ -151,4 +154,24 @@ registerHandlers( 'state/data-layer/wpcom/read/lists/index.js', { onError: () => noop, } ), ], + // Request only public lists for a specific user + [ READER_USER_LISTS_REQUEST ]: [ + dispatchRequest( { + fetch: ( action ) => + http( + { + method: 'GET', + path: `/read/lists/${ action.userSlug }`, + apiVersion: '1', + }, + action + ), + onSuccess: ( action, apiResponse ) => ( { + type: READER_USER_LISTS_RECEIVE, + userSlug: action.userSlug, + lists: apiResponse?.lists, + } ), + onError: () => noop, + } ), + ], } ); diff --git a/client/state/reader/action-types.js b/client/state/reader/action-types.js index 37443b0c278d7..52659e2ab0181 100644 --- a/client/state/reader/action-types.js +++ b/client/state/reader/action-types.js @@ -99,5 +99,7 @@ export const READER_UPDATE_NEW_POST_EMAIL_SUBSCRIPTION = export const READER_USER_REQUEST = 'READER_USER_REQUEST'; export const READER_USER_REQUEST_FAILURE = 'READER_USER_REQUEST_FAILURE'; export const READER_USER_REQUEST_SUCCESS = 'READER_USER_REQUEST_SUCCESS'; +export const READER_USER_LISTS_REQUEST = 'READER_USER_LISTS_REQUEST'; +export const READER_USER_LISTS_RECEIVE = 'READER_USER_LISTS_RECEIVE'; export const READER_VIEWING_FULL_POST_SET = 'READER_VIEWING_FULL_POST_SET'; export const READER_VIEWING_FULL_POST_UNSET = 'READER_VIEWING_FULL_POST_UNSET'; diff --git a/client/state/reader/lists/actions.js b/client/state/reader/lists/actions.js index a712a3506651a..6daa6617b1571 100644 --- a/client/state/reader/lists/actions.js +++ b/client/state/reader/lists/actions.js @@ -22,6 +22,7 @@ import { READER_LIST_ITEM_ADD_TAG_RECEIVE, READER_LISTS_RECEIVE, READER_LISTS_REQUEST, + READER_USER_LISTS_REQUEST, } from 'calypso/state/reader/action-types'; import 'calypso/state/data-layer/wpcom/read/lists'; import 'calypso/state/data-layer/wpcom/read/lists/delete'; @@ -271,3 +272,10 @@ export const deleteReaderList = ( listId, listOwner, listSlug ) => ( { listOwner, listSlug, } ); + +export function requestUserLists( userSlug ) { + return { + type: READER_USER_LISTS_REQUEST, + userSlug, + }; +} diff --git a/client/state/reader/lists/reducer.js b/client/state/reader/lists/reducer.js index a0f4e3ae3df9f..e7b658a13c267 100644 --- a/client/state/reader/lists/reducer.js +++ b/client/state/reader/lists/reducer.js @@ -19,6 +19,8 @@ import { READER_LIST_ITEM_DELETE_SITE, READER_LIST_ITEM_DELETE_TAG, READER_LIST_ITEM_ADD_FEED_RECEIVE, + READER_USER_LISTS_RECEIVE, + READER_USER_LISTS_REQUEST, } from 'calypso/state/reader/action-types'; import { combineReducers, withSchemaValidation } from 'calypso/state/utils'; import { itemsSchema, subscriptionsSchema } from './schema'; @@ -198,6 +200,35 @@ export function isRequestingLists( state = false, action ) { return state; } +export const userLists = ( state = {}, action ) => { + switch ( action.type ) { + case READER_USER_LISTS_RECEIVE: + return { + ...state, + [ action.userSlug ]: action.lists, + }; + default: + return state; + } +}; + +export const isRequestingUserLists = ( state = {}, action ) => { + switch ( action.type ) { + case READER_USER_LISTS_REQUEST: + return { + ...state, + [ action.userSlug ]: true, + }; + case READER_USER_LISTS_RECEIVE: + return { + ...state, + [ action.userSlug ]: false, + }; + default: + return state; + } +}; + export default combineReducers( { items, listItems, @@ -206,4 +237,6 @@ export default combineReducers( { isRequestingList, isRequestingLists, isUpdatingList, + userLists, + isRequestingUserLists, } );