Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions moon/apps/web/components/Issues/IssueDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,12 @@ export default function IssueDetailPage({ link, id }: { link: string; id: number
/>
</div>
</div>
<div className='mt-4 flex justify-end gap-4'>
<div
style={{
marginTop: `16px`
}}
Comment on lines +332 to +334

Copilot AI Jul 28, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using inline styles with hardcoded values when you already have the equivalent Tailwind class 'mt-4' (which is 16px) creates redundancy. Consider removing either the inline style or the Tailwind class.

Suggested change
style={{
marginTop: `16px`
}}

Copilot uses AI. Check for mistakes.
className='mt-4 flex justify-end gap-4'
>
<Button
className='!mb-32'
// loading={motivation === 'close' ? loadings[3] : undefined}
Expand Down Expand Up @@ -384,7 +389,12 @@ export default function IssueDetailPage({ link, id }: { link: string; id: number
</div>
</div>
</div>
<div className='mt-4 flex justify-end gap-4'>
<div
style={{
marginTop: `16px`
}}
Comment thread
liuyangjuncong20202570 marked this conversation as resolved.
className='mt-4 flex justify-end gap-4'
>
<Button
className='!mb-32'
loading={buttonLoading.reopen}
Expand Down
35 changes: 7 additions & 28 deletions moon/apps/web/components/Issues/IssuesContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,12 @@ import {
import { filterAtom, issueCloseCurrentPage, issueOpenCurrentPage, sortAtom } from '@/components/Issues/utils/store'
import { useScope } from '@/contexts/scope'
import { useGetIssueLists } from '@/hooks/issues/useGetIssueLists'
import { useGetOrganizationMember } from '@/hooks/useGetOrganizationMember'
import { useSyncedMembers } from '@/hooks/useSyncedMembers'
import { apiErrorToast } from '@/utils/apiErrorToast'
import { atomWithWebStorage } from '@/utils/atomWithWebStorage'

import { MemberAvatar } from '../MemberAvatar'
import { IssueIndexTabFilter } from './IssueIndex'
import { MemberHovercard } from './MemberHoverCardNE'
import { MemberHoverAvatarList } from './MemberHoverAvatarList'
import { Pagination } from './Pagenation'
import { orderTags, tags } from './utils/consts'
import { generateAllMenuItems, MenuConfig } from './utils/generateAllMenuItems'
Expand All @@ -56,7 +54,7 @@ export interface Item {
updated_at: number
}

type ItemsType = NonNullable<PostApiIssueListData['data']>['items']
export type ItemsType = NonNullable<PostApiIssueListData['data']>['items']

export type AdditionType = NonNullable<PageParamsListPayload>['additional']

Expand Down Expand Up @@ -424,6 +422,7 @@ export function IssuesContent({ searching }: Props) {
))
}}
</IssueList>

{status === 'open' && (
<div className='mt-auto'>
<Pagination
Expand Down Expand Up @@ -460,26 +459,9 @@ function IssueSearchList(_props: { searchIssueList?: Item[]; hideProject?: boole
}

export const RightAvatar = ({ item }: { item: ItemsType[number] }) => {
const shouldFetch = item.assignees.length > 0

const [index, setIndex] = useState(0)

const { data, error } = useGetOrganizationMember({
username: item.assignees[index],
org: 'mega',
enabled: shouldFetch
})

useEffect(() => {
if (index >= item.assignees.length) return
if (error) {
setIndex((prev) => prev + 1)
}
}, [error, index, item.assignees])

return (
<>
<div className='mr-10 flex w-[120px] items-center justify-between gap-10'>
<div className='mr-10 flex w-fit items-center justify-between gap-10'>
<div
style={{
visibility: `${item.comment_num === 0 ? 'hidden' : 'unset'}`
Expand All @@ -490,12 +472,9 @@ export const RightAvatar = ({ item }: { item: ItemsType[number] }) => {
<span>{item.comment_num}</span>
</div>

{data && (
// <div>展示头像</div>
<MemberHovercard username={item.assignees[0]} side='top' align='end' member={data}>
<MemberAvatar size='sm' member={data} />
</MemberHovercard>
)}
<div className='min-w-45'>
Comment thread
liuyangjuncong20202570 marked this conversation as resolved.
<MemberHoverAvatarList users={item} />
</div>
</div>
</>
)
Expand Down
80 changes: 80 additions & 0 deletions moon/apps/web/components/Issues/MemberHoverAvatarList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react'
import { Avatar, AvatarStack } from '@primer/react'
import { useQueries } from '@tanstack/react-query'

import { OrganizationMember } from '@gitmono/types/generated'

import { useScope } from '@/contexts/scope'
import { apiClient } from '@/utils/queryClient'

import { ItemsType } from './IssuesContent'
import { MemberHovercard } from './MemberHoverCardNE'

export const MemberHoverAvatarList = ({ users }: { users: ItemsType[number] }) => {
const shouldFetch = users.assignees.length > 0
const query = apiClient.organizations.getMembersByUsername()

const { scope } = useScope()

const queries = useQueries({
queries: users.assignees.map((u) => ({
queryKey: query.requestKey(`${scope}`, `${u}`),
queryFn: () => query.request(`${scope}`, `${u}`),
enabled: shouldFetch
})),
combine: (res) => {
return {
data: res.map((r) => r.data),
pending: res.some((r) => r.isPending)
}
}
})

return (
<>
<AvatarStack alignRight>
{queries.pending
? Array.from({ length: users.assignees.length }).map((_, i) => (
// eslint-disable-next-line react/no-array-index-key
<div className='h-[48px] w-[48px] rounded-full bg-[#f3f4f5]' key={i} />
Comment thread
liuyangjuncong20202570 marked this conversation as resolved.
))
: queries.data.map(
(q) =>
q && (
<AvatarwithHover
key={q.id}
src={q?.user.avatar_url}
hoverProps={{ username: q.user.username, userData: q }}
/>
)
)}
</AvatarStack>
</>
)
}

interface HoverProps {
username: string
userData: OrganizationMember
}
const AvatarwithHover = ({
src,
hoverProps,
className,
style
}: {
src: string
hoverProps: HoverProps
className?: string
style?: React.CSSProperties
}) => {
return (
<>
<MemberHovercard username={hoverProps.username} side='top' align='end' member={hoverProps.userData}>
<div className={className} style={style}>
<Avatar src={src} />

Copilot AI Jul 28, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Avatar component should include an alt attribute for accessibility. Consider adding alt text based on the user's username or display name.

Suggested change
<Avatar src={src} />
<Avatar src={src} alt={`Avatar of ${hoverProps.username}`} />

Copilot uses AI. Check for mistakes.
</div>
</MemberHovercard>
</>
)
}
18 changes: 8 additions & 10 deletions moon/apps/web/components/Issues/MemberHoverCardNE.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,7 @@ export function MemberHovercard({

return (
<HoverCard.Root open={open} onOpenChange={setOpen} openDelay={200} closeDelay={200}>
<HoverCard.Trigger asChild>
<span>{children}</span>
</HoverCard.Trigger>
<HoverCard.Trigger asChild>{children}</HoverCard.Trigger>

<AnimatePresence>
{open && (
Expand All @@ -102,12 +100,12 @@ export function MemberHovercard({
{member && (
<div>
<div className='flex h-6 items-center gap-4 border border-b-gray-300 px-4 py-4 text-sm text-gray-500'>
{/* these are fixed, we need fetch the data from backend in this cmp */}
{/* these are fixed, we need fetch the member from backend in this cmp */}
<ChatBubbleIcon />
<span>I may be slow to respond</span>
<span>{member?.user.email}</span>
</div>
<div className='flex items-center justify-between px-6 py-6'>
<MemberAvatar member={member} size='xl' />
{member && <MemberAvatar member={member} size='xl' />}
<Button
onClick={(e: React.SyntheticEvent) => {
e.stopPropagation()
Expand All @@ -123,9 +121,9 @@ export function MemberHovercard({
<span className='text-black'>{member.user.username} </span>
<span>{member.user.display_name}</span>
</div>
<span>Stay foolish</span>
<span>China</span>
<span>Commit timeline</span>
{/* <span>Stay foolish</span> */}
{/* <span>China</span> */}
{/* <span>Commit timeline</span> */}
</div>
</div>
)}
Expand All @@ -136,4 +134,4 @@ export function MemberHovercard({
</AnimatePresence>
</HoverCard.Root>
)
}
}