Skip to content

Commit

Permalink
User Profile: List component (#98225)
Browse files Browse the repository at this point in the history
* Add first pass at list component

* Fix infinite API calls in UserLists by removing isLoading from useEffect

* Initial working commit

* Improve styles

* Revert unnecessary changes to user stream component and store

* Remove unused action types

* Add clarifying comments

* Revert changes to list items store

* Fix typo

* Fix type errors and simplify lists connect()

* Make the whole lists card a link; fix styles

---------

Co-authored-by: Ricardo Artemio Morales <[email protected]>
Co-authored-by: DustyReagan <[email protected]>
  • Loading branch information
3 people authored Jan 16, 2025
1 parent 94473bd commit ed3c9fc
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 10 deletions.
1 change: 1 addition & 0 deletions client/lib/user/user.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions client/reader/list-manage/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type List = {
description: string;
is_public: boolean;
is_owner: boolean;
owner: string;
slug: string;
title: string;
};
Expand Down
19 changes: 19 additions & 0 deletions client/reader/user-stream/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
}
79 changes: 70 additions & 9 deletions client/reader/user-stream/views/lists.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="user-profile__lists">
<UserProfileHeader user={ user } />
<EmptyContent
illustration={ null }
icon={ <Icon icon={ formatListBullets } size={ 48 } /> }
title={ null }
line={ translate( 'No lists yet.' ) }
/>
</div>
);
}

return (
<div className="user-stream__lists">
<div className="user-profile__lists">
<UserProfileHeader user={ user } />
<EmptyContent
illustration={ null }
icon={ <Icon icon={ formatListBullets } size={ 48 } /> }
title={ null }
line={ translate( 'No lists yet.' ) }
/>
<div className="user-profile__lists-body">
{ lists.map( ( list: List ) => (
<a
className="user-profile__lists-body-link"
href={ `/read/list/${ list.owner }/${ list.slug }` }
key={ list.ID }
>
<div className="card reader-post-card is-compact is-clickable">
<div className="reader-post-card__post-heading">
<h2 className="reader-post-card__title">{ list.title }</h2>
</div>
<div className="reader-post-card__post-content">{ list.description }</div>
</div>
</a>
) ) }
</div>
</div>
);
};

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 );
25 changes: 24 additions & 1 deletion client/state/data-layer/wpcom/read/lists/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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,
} ),
],
} );
2 changes: 2 additions & 0 deletions client/state/reader/action-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
8 changes: 8 additions & 0 deletions client/state/reader/lists/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -271,3 +272,10 @@ export const deleteReaderList = ( listId, listOwner, listSlug ) => ( {
listOwner,
listSlug,
} );

export function requestUserLists( userSlug ) {
return {
type: READER_USER_LISTS_REQUEST,
userSlug,
};
}
33 changes: 33 additions & 0 deletions client/state/reader/lists/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand All @@ -206,4 +237,6 @@ export default combineReducers( {
isRequestingList,
isRequestingLists,
isUpdatingList,
userLists,
isRequestingUserLists,
} );

0 comments on commit ed3c9fc

Please sign in to comment.