diff --git a/web/settings/relationship/friend-list-panel.react.js b/web/settings/relationship/friend-list-panel.react.js
new file mode 100644
index 000000000..856174529
--- /dev/null
+++ b/web/settings/relationship/friend-list-panel.react.js
@@ -0,0 +1,87 @@
+// @flow
+
+import * as React from 'react';
+
+import { useModalContext } from 'lib/components/modal-provider.react.js';
+import { userRelationshipStatus } from 'lib/types/relationship-types.js';
+import type { AccountUserInfo, UserInfo } from 'lib/types/user-types.js';
+
+import AddFriendsModal from './add-friends-modal.react.js';
+import FriendListRow from './friend-list-row.react.js';
+import css from './user-list.css';
+import { UserList } from './user-list.react.js';
+import PanelHeader from '../../components/panel-header.react.js';
+import Panel, { type PanelData } from '../../components/panel.react.js';
+import Search from '../../components/search.react.js';
+
+const relationships = [
+ userRelationshipStatus.REQUEST_RECEIVED,
+ userRelationshipStatus.REQUEST_SENT,
+ userRelationshipStatus.FRIEND,
+];
+
+function filterUser(userInfo: UserInfo) {
+ return relationships.includes(userInfo.relationshipStatus);
+}
+
+function usersComparator(user1: AccountUserInfo, user2: AccountUserInfo) {
+ if (user1.relationshipStatus === user2.relationshipStatus) {
+ return user1.username.localeCompare(user2.username);
+ }
+ return (
+ relationships.indexOf(user1.relationshipStatus) -
+ relationships.indexOf(user2.relationshipStatus)
+ );
+}
+
+function FriendListPanel(): React.Node {
+ const [searchText, setSearchText] = React.useState('');
+
+ const { pushModal, popModal } = useModalContext();
+
+ const onClickAddFriendsButton = React.useCallback(
+ () => pushModal(),
+ [popModal, pushModal],
+ );
+
+ const friendList = React.useMemo(
+ () => (
+
+ ),
+ [searchText],
+ );
+
+ const panelData: $ReadOnlyArray = React.useMemo(
+ () => [
+ {
+ header: (
+
+ ),
+ body: friendList,
+ classNameOveride: css.panelContainer,
+ },
+ ],
+ [friendList, onClickAddFriendsButton],
+ );
+
+ return ;
+}
+
+export default FriendListPanel;
diff --git a/web/settings/relationship/user-list.css b/web/settings/relationship/user-list.css
index 581c24d85..27646f197 100644
--- a/web/settings/relationship/user-list.css
+++ b/web/settings/relationship/user-list.css
@@ -1,16 +1,31 @@
+.panelContainer {
+ flex: 1;
+}
+
.container {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ padding: 8px;
+}
+
+.searchContainer {
+ padding: 8px;
+}
+
+.userListContainer {
overflow: auto;
flex-grow: 1;
flex-basis: 120px;
}
.noScroll {
overflow: hidden;
}
.searchModalContent {
display: flex;
flex-direction: column;
row-gap: 8px;
height: 625px;
}
diff --git a/web/settings/relationship/user-list.react.js b/web/settings/relationship/user-list.react.js
index 06363f9ff..130a3377b 100644
--- a/web/settings/relationship/user-list.react.js
+++ b/web/settings/relationship/user-list.react.js
@@ -1,80 +1,80 @@
// @flow
import classNames from 'classnames';
import * as React from 'react';
import { useENSNames } from 'lib/hooks/ens-cache.js';
import { useUserSearchIndex } from 'lib/selectors/nav-selectors.js';
import type { AccountUserInfo, UserInfo } from 'lib/types/user-types.js';
import { values } from 'lib/utils/objects.js';
import css from './user-list.css';
import { useSelector } from '../../redux/redux-utils.js';
export type UserRowProps = {
+userInfo: AccountUserInfo,
+onMenuVisibilityChange?: (visible: boolean) => void,
};
type UserListProps = {
+userRowComponent: React.ComponentType,
+filterUser: (userInfo: UserInfo) => boolean,
+usersComparator: (user1: AccountUserInfo, user2: AccountUserInfo) => number,
+searchText: string,
};
export function UserList(props: UserListProps): React.Node {
const { userRowComponent, filterUser, usersComparator, searchText } = props;
const userInfos = useSelector(state => state.userStore.userInfos);
const userInfosArray = React.useMemo(
() => values(userInfos).filter(filterUser),
[userInfos, filterUser],
);
const userStoreSearchIndex = useUserSearchIndex(userInfosArray);
const [isMenuVisible, setIsMenuVisible] = React.useState(false);
const onMenuVisibilityChange = React.useCallback(
(visible: boolean) => setIsMenuVisible(visible),
[],
);
const searchResult = React.useMemo(
() => userStoreSearchIndex.getSearchResults(searchText),
[searchText, userStoreSearchIndex],
);
const users = React.useMemo(() => {
const userIDs = searchText ? searchResult : userInfosArray.map(u => u.id);
const matchedUserInfos = [];
for (const id of userIDs) {
const { username, relationshipStatus } = userInfos[id];
if (!username) {
continue;
}
matchedUserInfos.push({
id,
username,
relationshipStatus,
});
}
return matchedUserInfos.sort(usersComparator);
}, [userInfosArray, searchResult, searchText, userInfos, usersComparator]);
const usersWithENSNames = useENSNames(users);
const userRows = React.useMemo(() => {
const UserRow = userRowComponent;
return usersWithENSNames.map(user => (
));
}, [userRowComponent, usersWithENSNames, onMenuVisibilityChange]);
- const containerClasses = classNames(css.container, {
+ const containerClasses = classNames(css.userListContainer, {
[css.noScroll]: isMenuVisible,
});
return {userRows}
;
}