From 2f5a4129d35dcae3caf5e5746d9ea6103e5cbac3 Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Wed, 20 Nov 2024 10:16:19 +0200 Subject: [PATCH] feat: add user tooltip for user links --- .../FollowedLists/FollowedUsersList.tsx | 26 ++++++ .../src/components/FollowedLists/index.ts | 1 + .../qeta-react/src/components/Links/Links.tsx | 14 ++- .../PostsContainer/PostListItem.tsx | 10 +-- .../components/PostsGrid/PostsGridItem.tsx | 10 +-- .../components/TagsAndEntities/UserChip.tsx | 89 +++++++++++++++++++ plugins/qeta-react/src/translation.ts | 1 + .../src/components/UsersPage/UsersPage.tsx | 20 +++-- 8 files changed, 149 insertions(+), 22 deletions(-) create mode 100644 plugins/qeta-react/src/components/FollowedLists/FollowedUsersList.tsx create mode 100644 plugins/qeta-react/src/components/TagsAndEntities/UserChip.tsx diff --git a/plugins/qeta-react/src/components/FollowedLists/FollowedUsersList.tsx b/plugins/qeta-react/src/components/FollowedLists/FollowedUsersList.tsx new file mode 100644 index 00000000..80c8b67b --- /dev/null +++ b/plugins/qeta-react/src/components/FollowedLists/FollowedUsersList.tsx @@ -0,0 +1,26 @@ +import ListItem from '@mui/material/ListItem'; +import React from 'react'; +import { useTranslation, useUserFollow } from '../../hooks'; +import { RightList, RightListContainer } from '../Styled/RightList'; +import { UserChip } from '../TagsAndEntities/UserChip'; + +export const FollowedUsersList = () => { + const users = useUserFollow(); + const { t } = useTranslation(); + + if (users.users.length === 0 || users.loading) { + return null; + } + + return ( + + + + {users.users.map(user => ( + + ))} + + + + ); +}; diff --git a/plugins/qeta-react/src/components/FollowedLists/index.ts b/plugins/qeta-react/src/components/FollowedLists/index.ts index d2c3c642..389cd8e6 100644 --- a/plugins/qeta-react/src/components/FollowedLists/index.ts +++ b/plugins/qeta-react/src/components/FollowedLists/index.ts @@ -1,3 +1,4 @@ export { FollowedEntitiesList } from './FollowedEntitiesList'; export { FollowedTagsList } from './FollowedTagsList'; export { FollowedCollectionsList } from './FollowedCollectionsList'; +export { FollowedUsersList } from './FollowedUsersList'; diff --git a/plugins/qeta-react/src/components/Links/Links.tsx b/plugins/qeta-react/src/components/Links/Links.tsx index 73061636..0f522b93 100644 --- a/plugins/qeta-react/src/components/Links/Links.tsx +++ b/plugins/qeta-react/src/components/Links/Links.tsx @@ -5,6 +5,8 @@ import { Link, LinkProps } from '@backstage/core-components'; import { userRouteRef } from '../../routes'; import { Answer, Comment, Post } from '@drodil/backstage-plugin-qeta-common'; import { useTranslation } from '../../hooks'; +import { UserTooltip } from '../TagsAndEntities/UserChip'; +import Tooltip from '@mui/material/Tooltip'; export const UserLink = (props: { entityRef: string; @@ -20,9 +22,15 @@ export const UserLink = (props: { return <>{t('userLink.anonymous')}; } return ( - - {userName} - + } + enterDelay={400} + > + + {userName} + + ); }; diff --git a/plugins/qeta-react/src/components/PostsContainer/PostListItem.tsx b/plugins/qeta-react/src/components/PostsContainer/PostListItem.tsx index b4c937f9..df4069e4 100644 --- a/plugins/qeta-react/src/components/PostsContainer/PostListItem.tsx +++ b/plugins/qeta-react/src/components/PostsContainer/PostListItem.tsx @@ -14,7 +14,7 @@ import { } from '@drodil/backstage-plugin-qeta-common'; import { TagsAndEntities } from '../TagsAndEntities/TagsAndEntities'; import { useRouteRef } from '@backstage/core-plugin-api'; -import { articleRouteRef, questionRouteRef, userRouteRef } from '../../routes'; +import { articleRouteRef, questionRouteRef } from '../../routes'; import { RelativeTimeWithTooltip } from '../RelativeTimeWithTooltip'; import { useSignal } from '@backstage/plugin-signals-react'; import { VoteButtons } from '../Buttons/VoteButtons'; @@ -25,6 +25,7 @@ import HelpOutlined from '@mui/icons-material/HelpOutlined'; import { useTranslation } from '../../hooks'; import { useEntityAuthor } from '../../hooks/useEntityAuthor'; import { VoteButtonContainer } from '../Styled/VoteButtonContainer'; +import { UserLink } from '../Links'; export interface PostListItemProps { post: PostResponse; @@ -52,7 +53,6 @@ export const PostListItem = (props: PostListItemProps) => { const questionRoute = useRouteRef(questionRouteRef); const articleRoute = useRouteRef(articleRouteRef); - const userRoute = useRouteRef(userRouteRef); const { name, initials, user } = useEntityAuthor(post); const route = post.type === 'question' ? questionRoute : articleRoute; @@ -168,11 +168,7 @@ export const PostListItem = (props: PostListItemProps) => { > {initials} - {post.author === 'anonymous' ? ( - t('common.anonymousAuthor') - ) : ( - {name} - )}{' '} + {' '} diff --git a/plugins/qeta-react/src/components/PostsGrid/PostsGridItem.tsx b/plugins/qeta-react/src/components/PostsGrid/PostsGridItem.tsx index b19605f0..b769bf4e 100644 --- a/plugins/qeta-react/src/components/PostsGrid/PostsGridItem.tsx +++ b/plugins/qeta-react/src/components/PostsGrid/PostsGridItem.tsx @@ -8,7 +8,7 @@ import { import React, { useEffect, useState } from 'react'; import { useSignal } from '@backstage/plugin-signals-react'; import { useApi, useRouteRef } from '@backstage/core-plugin-api'; -import { articleRouteRef, questionRouteRef, userRouteRef } from '../../routes'; +import { articleRouteRef, questionRouteRef } from '../../routes'; import Card from '@mui/material/Card'; import CardContent from '@mui/material/CardContent'; import CardActionArea from '@mui/material/CardActionArea'; @@ -31,6 +31,7 @@ import VerticalAlignBottomIcon from '@mui/icons-material/VerticalAlignBottom'; import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import Stack from '@mui/material/Stack'; +import { UserLink } from '../Links'; export interface PostsGridItemProps { post: PostResponse; @@ -57,7 +58,6 @@ export const PostsGridItem = (props: PostsGridItemProps) => { const questionRoute = useRouteRef(questionRouteRef); const articleRoute = useRouteRef(articleRouteRef); - const userRoute = useRouteRef(userRouteRef); const { name, initials, user } = useEntityAuthor(post); const navigate = useNavigate(); @@ -119,11 +119,7 @@ export const PostsGridItem = (props: PostsGridItemProps) => { > {initials} - {post.author === 'anonymous' ? ( - t('common.anonymousAuthor') - ) : ( - {name} - )}{' '} + {' '} diff --git a/plugins/qeta-react/src/components/TagsAndEntities/UserChip.tsx b/plugins/qeta-react/src/components/TagsAndEntities/UserChip.tsx new file mode 100644 index 00000000..187d30ba --- /dev/null +++ b/plugins/qeta-react/src/components/TagsAndEntities/UserChip.tsx @@ -0,0 +1,89 @@ +import { useRouteRef } from '@backstage/core-plugin-api'; +import { userRouteRef } from '../../routes'; +import { useTranslation, useUserFollow } from '../../hooks'; +import { useEntityPresentation } from '@backstage/plugin-catalog-react'; +import React from 'react'; +import Chip from '@mui/material/Chip'; +import { useNavigate } from 'react-router-dom'; +import Grid from '@mui/material/Grid'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import Tooltip from '@mui/material/Tooltip'; + +export const UserTooltip = (props: { entityRef: string }) => { + const { entityRef } = props; + const { t } = useTranslation(); + const { + primaryTitle: userName, + Icon, + secondaryTitle, + } = useEntityPresentation( + entityRef.startsWith('user:') ? entityRef : `user:${entityRef}`, + ); + const users = useUserFollow(); + + return ( + + + + {Icon ? : null} + {entityRef === 'anonymous' ? t('userLink.anonymous') : userName} + + + + {secondaryTitle} + + {!users.loading && ( + + + + )} + + ); +}; + +export const UserChip = (props: { entityRef: string }) => { + const navigate = useNavigate(); + const { entityRef } = props; + const userRoute = useRouteRef(userRouteRef); + const { t } = useTranslation(); + const { primaryTitle: userName } = useEntityPresentation( + entityRef.startsWith('user:') ? entityRef : `user:${entityRef}`, + ); + if (entityRef === 'anonymous') { + return <>{t('userLink.anonymous')}; + } + return ( + } + enterDelay={400} + > + { + navigate(`${userRoute()}/${entityRef}`); + }} + clickable + /> + + ); +}; diff --git a/plugins/qeta-react/src/translation.ts b/plugins/qeta-react/src/translation.ts index 069459b2..a7cdb6f2 100644 --- a/plugins/qeta-react/src/translation.ts +++ b/plugins/qeta-react/src/translation.ts @@ -218,6 +218,7 @@ export const qetaTranslationRef = createTranslationRef({ followedEntities: 'Followed entities', followedTags: 'Followed tags', followedCollections: 'Followed collections', + followedUsers: 'Followed users', }, highlights: { loadError: 'Failed to load questions', diff --git a/plugins/qeta/src/components/UsersPage/UsersPage.tsx b/plugins/qeta/src/components/UsersPage/UsersPage.tsx index c46a25e0..ea9fefcf 100644 --- a/plugins/qeta/src/components/UsersPage/UsersPage.tsx +++ b/plugins/qeta/src/components/UsersPage/UsersPage.tsx @@ -1,14 +1,24 @@ import React from 'react'; -import { UsersGrid, useTranslation } from '@drodil/backstage-plugin-qeta-react'; +import { + FollowedUsersList, + UsersGrid, + useTranslation, +} from '@drodil/backstage-plugin-qeta-react'; import { ContentHeader } from '@backstage/core-components'; +import Grid from '@mui/material/Grid'; export const UsersPage = () => { const { t } = useTranslation(); return ( - <> - - - + + + + + + + + + ); };