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
123 changes: 81 additions & 42 deletions moon/apps/web/components/Issues/IssueDetailPage.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,39 @@
'use client'

import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { IssueClosedIcon, IssueOpenedIcon, IssueReopenedIcon } from '@primer/octicons-react'
import { Stack } from '@primer/react'
import { useRouter } from 'next/router'
import toast from 'react-hot-toast'

import { CommonResultIssueDetailRes } from '@gitmono/types'
import { Button, LoadingSpinner, PicturePlusIcon } from '@gitmono/ui'

import { EMPTY_HTML } from '@/atoms/markdown'
import { useHandleBottomScrollOffset } from '@/components/NoteEditor/useHandleBottomScrollOffset'
import { ComposerReactionPicker } from '@/components/Reactions/ComposerReactionPicker'
import { SimpleNoteContent, SimpleNoteContentRef } from '@/components/SimpleNoteEditor/SimpleNoteContent'
import { useGetIssueDetail } from '@/hooks/issues/useGetIssueDetail'
import { usePostIssueAssignees } from '@/hooks/issues/usePostIssueAssignees'
import { usePostIssueClose } from '@/hooks/issues/usePostIssueClose'
import { usePostIssueComment } from '@/hooks/issues/usePostIssueComment'
import { usePostIssueReopen } from '@/hooks/issues/usePostIssueReopen'
import { useGetCurrentUser } from '@/hooks/useGetCurrentUser'
import { useGetOrganizationMember } from '@/hooks/useGetOrganizationMember'
import { useUploadHelpers } from '@/hooks/useUploadHelpers'
import { apiErrorToast } from '@/utils/apiErrorToast'
import { trimHtml } from '@/utils/trimHtml'

import { MemberAvatar } from '../MemberAvatar'
import TimelineItems from '../MrView/TimelineItems'
import { BadgeItem } from './IssueNewPage'
import { pickWithReflect } from './utils/pickWithReflectDeep'
import {
splitFun,
useAssigneesSelector,
useAvatars,
useChange,
useLabelMap,
useLabels,
useMemberMap
} from './utils/sideEffect'
'use client';

import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { IssueClosedIcon, IssueOpenedIcon, IssueReopenedIcon } from '@primer/octicons-react';
import { Stack } from '@primer/react';
import { useRouter } from 'next/router';
import toast from 'react-hot-toast';


import { CommonResultIssueDetailRes } from '@gitmono/types';
import { Button, LoadingSpinner, PicturePlusIcon } from '@gitmono/ui';

import { EMPTY_HTML } from '@/atoms/markdown';
import { useHandleBottomScrollOffset } from '@/components/NoteEditor/useHandleBottomScrollOffset';
import { ComposerReactionPicker } from '@/components/Reactions/ComposerReactionPicker';
import { SimpleNoteContent, SimpleNoteContentRef } from '@/components/SimpleNoteEditor/SimpleNoteContent';
import { useGetIssueDetail } from '@/hooks/issues/useGetIssueDetail';
import { usePostIssueAssignees } from '@/hooks/issues/usePostIssueAssignees';
import { usePostIssueClose } from '@/hooks/issues/usePostIssueClose';
import { usePostIssueComment } from '@/hooks/issues/usePostIssueComment';
import { usePostIssueReopen } from '@/hooks/issues/usePostIssueReopen';
import { useGetCurrentUser } from '@/hooks/useGetCurrentUser';
import { useGetOrganizationMember } from '@/hooks/useGetOrganizationMember';
import { usePostIssueLabels } from '@/hooks/usePostIssueLabels';
import { useUploadHelpers } from '@/hooks/useUploadHelpers';
import { apiErrorToast } from '@/utils/apiErrorToast';
import { trimHtml } from '@/utils/trimHtml';



import { MemberAvatar } from '../MemberAvatar';
import TimelineItems from '../MrView/TimelineItems';
import { BadgeItem } from './IssueNewPage';
import { pickWithReflect } from './utils/pickWithReflectDeep';
import { splitFun, useAssigneesSelector, useAvatars, useChange, useLabelMap, useLabels, useLabelsSelector, useMemberMap } from './utils/sideEffect';


// interface IssueDetail {
// status: string
Expand All @@ -53,7 +50,8 @@ export default function IssueDetailPage({ link, id }: { link: string; id: number
status: '',
conversations: [],
title: '',
assignees: []
assignees: [],
labels: []
})
const [buttonLoading, setButtonLoading] = useState<{ [key: string]: boolean }>({
comment: false,
Expand All @@ -76,6 +74,8 @@ export default function IssueDetailPage({ link, id }: { link: string; id: number

const { mutate: issueAssignees } = usePostIssueAssignees()

const { mutate: issueLabels } = usePostIssueLabels()

const { data: issueDetailObj, error, isError, refetch, isLoading: detailIsLoading } = useGetIssueDetail(link)

const issueDetail = issueDetailObj?.data as CommonResultIssueDetailRes['data'] | undefined
Expand All @@ -86,7 +86,8 @@ export default function IssueDetailPage({ link, id }: { link: string; id: number
title: detail.data.title,
status: detail.data.status,
conversations: detail.data.conversations,
assignees: detail.data.assignees
assignees: detail.data.assignees,
labels: detail.data.labels
})

// selectRef.current = detail.data.assignees
Expand Down Expand Up @@ -262,6 +263,36 @@ export default function IssueDetailPage({ link, id }: { link: string; id: number
avatars
})

const {
open: label_open,
handleLabels,
handleOpenChange: label_handleOpenChange,
fetchSelected: label_fetchSelected
} = useLabelsSelector({
labelList: labels,
labels: info?.labels ?? [],
updateLabelsRequest: (selected) => {
issueLabels(
{
data: {
item_id: Number(id),
label_ids: selected,
link
}
},
{
onSuccess: async () => {
editorRef.current?.clearAndBlur()
const { data: issueDetailObj } = await refetch({ throwOnError: true })

applyDetailData(issueDetailObj)
},
onError: apiErrorToast
}
)
}
})

return (
<>
<div className='h-screen overflow-auto pt-10'>
Expand Down Expand Up @@ -445,7 +476,15 @@ export default function IssueDetailPage({ link, id }: { link: string; id: number
)
}}
</BadgeItem>
<BadgeItem selectPannelProps={{ title: 'Apply labels to this issue' }} items={labels} title='Labels'>
<BadgeItem
selectPannelProps={{ title: 'Apply labels to this issue' }}
items={labels}
title='Labels'
handleGroup={(selected) => handleLabels(selected)}
open={label_open}
onOpenChange={(open) => label_handleOpenChange(open)}
selected={label_fetchSelected}
>
{(el) => {
const names = splitFun(el)

Expand Down
10 changes: 6 additions & 4 deletions moon/apps/web/components/Issues/IssuesContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ import { atomWithWebStorage } from '@/utils/atomWithWebStorage'
import { IssueIndexTabFilter } from './IssueIndex'
import { MemberHoverAvatarList } from './MemberHoverAvatarList'
import { Pagination } from './Pagenation'
import { orderTags, tags } from './utils/consts'
import { orderTags } from './utils/consts'
import { generateAllMenuItems, MenuConfig } from './utils/generateAllMenuItems'
import { useGetLabelList } from '@/hooks/useGetLabelList'

interface Props {
getIssues?: ReturnType<typeof useInfiniteQuery<PostApiIssueListData>>
Expand Down Expand Up @@ -94,6 +95,7 @@ export function IssuesContent({ searching }: Props) {
const [label, setLabel] = useAtom(labelAtom)

const { members } = useSyncedMembers()
const { labels } = useGetLabelList()

const router = useRouter()

Expand Down Expand Up @@ -225,7 +227,7 @@ export function IssuesContent({ searching }: Props) {

const member = generateAllMenuItems(members, MemberConfig)

const labels = generateAllMenuItems(tags, LabelConfig)
const labelList = generateAllMenuItems(labels, LabelConfig)

const orders = generateAllMenuItems(orderTags, OrderConfig)

Expand Down Expand Up @@ -268,8 +270,8 @@ export function IssuesContent({ searching }: Props) {
isChosen={!label?.length}
key={p}
name={p}
dropdownArr={labels?.get('Labels').all}
dropdownItem={labels?.get('Labels').chosen}
dropdownArr={labelList?.get('Labels').all}
dropdownItem={labelList?.get('Labels').chosen}
/>
)
case `${order.sort}`:
Expand Down
74 changes: 67 additions & 7 deletions moon/apps/web/components/Issues/utils/sideEffect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { ItemInput } from '@primer/react/lib/SelectPanel/types'
import { MemberAvatar } from '@/components/MemberAvatar'
import { useSyncedMembers } from '@/hooks/useSyncedMembers'

import { tags } from './consts'
import { extractTextArray } from './extractText'
import { useGetLabelList } from '@/hooks/useGetLabelList'
import { LabelItem } from '@gitmono/types'

export const useAvatars = () => {
const { members } = useSyncedMembers()
Expand Down Expand Up @@ -41,10 +42,13 @@ export const useMemberMap = () => {
}

export const useLabels = () => {
const { labels } = useGetLabelList()

return useMemo(
() =>
tags.map((i) => ({
text: i.description,
labels.map((i) => ({
text: `${i.name}`,
id: i.id,
leadingVisual: () => (
<div
className='h-[14px] w-[14px] rounded-full border'
Expand All @@ -53,19 +57,21 @@ export const useLabels = () => {
/>
)
})),
[]
[labels]
)
}

export const useLabelMap = () => {
const { labels } = useGetLabelList()

return useMemo(() => {
const map = new Map()

tags.map((i) => {
map.set(i.description, i)
labels.forEach((i) => {
map.set(i.name, i)
})
return map
}, [])
}, [labels])
}

// assignees逻辑
Expand Down Expand Up @@ -151,3 +157,57 @@ export const useChange = ({ title = 'Close issue' }: { title?: string }) => {
handleCloseChange
}
}

export const useLabelsSelector = ({
labels,
updateLabelsRequest,
labelList
}: {
labels: LabelItem[]
updateLabelsRequest: (selected_id: number[]) => void
labelList: ReturnType<typeof useLabels>
}) => {
const selectRef = useRef<number[]>([])
let selects: number[] = labels.map((i) => i.id)
const shouldFetch = useRef(false)
const [open, setOpen] = useState(false)

const handleLabels = (selected: ItemInput[]) => {
selects = [...selected.map((i) => i.id).filter((t): t is number => typeof t === 'number')]
}

const handleOpenChange = (open: boolean) => {
if (selectRef.current.length !== selects.length) {
shouldFetch.current = true
} else {
const set = new Set(selects)

for (let i = 0; i < selectRef.current.length; i++) {
if (!set.has(selectRef.current[i])) {
shouldFetch.current = true
break
}
}
}

setOpen(open)
if (!open && shouldFetch.current) {
selectRef.current = selects
updateLabelsRequest(selectRef.current)
setTimeout(() => (shouldFetch.current = false), 0)
}
}

const fetchSelected = useMemo(() => {
const set = new Set(selectRef.current.length ? selectRef.current : selects)

return labelList.filter((label) => set.has(label.id))
}, [selectRef, labelList, selects])

return {
open,
handleLabels,
handleOpenChange,
fetchSelected
}
}
Loading