BranchDropdown.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { useParams } from 'common'
  2. import { useState } from 'react'
  3. import { ShimmeringLoader } from 'ui-patterns'
  4. import { AppLayoutDropdownError, AppLayoutDropdownWithPopover } from './AppLayoutDropdown'
  5. import { BranchBadge } from './BranchBadge'
  6. import { BranchDropdownCommandContent } from './BranchDropdownCommandContent'
  7. import { useEmbeddedCloseHandler } from './useEmbeddedCloseHandler'
  8. import { useBranchesQuery } from '@/data/branches/branches-query'
  9. import type { Branch } from '@/data/branches/branches-query'
  10. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  11. import { useTrack } from '@/lib/telemetry/track'
  12. import { useAppStateSnapshot } from '@/state/app-state'
  13. interface BranchDropdownProps {
  14. embedded?: boolean
  15. className?: string
  16. onClose?: () => void
  17. }
  18. export const BranchDropdown = ({
  19. embedded = false,
  20. className,
  21. onClose,
  22. }: BranchDropdownProps = {}) => {
  23. const { ref } = useParams()
  24. const snap = useAppStateSnapshot()
  25. const { data: projectDetails } = useSelectedProjectQuery()
  26. const [open, setOpen] = useState(false)
  27. const close = useEmbeddedCloseHandler(embedded, onClose, setOpen)
  28. const track = useTrack()
  29. const handleOpenChange = (next: boolean) => {
  30. if (next) track('header_branch_dropdown_opened')
  31. setOpen(next)
  32. }
  33. const projectRef = projectDetails?.parent_project_ref || ref
  34. const {
  35. data: branches,
  36. isPending: isLoading,
  37. isError,
  38. isSuccess,
  39. } = useBranchesQuery({ projectRef }, { enabled: Boolean(projectDetails) })
  40. const isBranchingEnabled = projectDetails?.is_branch_enabled === true
  41. const selectedBranch = branches?.find((branch) => branch.project_ref === ref)
  42. const defaultMainBranch = {
  43. id: 'main',
  44. name: 'main',
  45. project_ref: projectRef ?? ref ?? '',
  46. is_default: true,
  47. } as unknown as Branch
  48. const mainBranch = branches?.find((branch) => branch.is_default)
  49. const restOfBranches = branches
  50. ?.filter((branch) => !branch.is_default)
  51. ?.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
  52. const sortedBranches =
  53. branches && branches.length > 0
  54. ? mainBranch
  55. ? [mainBranch].concat(restOfBranches ?? [])
  56. : (restOfBranches ?? [])
  57. : [defaultMainBranch]
  58. const branchList = isBranchingEnabled ? (sortedBranches ?? []) : [defaultMainBranch]
  59. const commandContent = (
  60. <BranchDropdownCommandContent
  61. embedded={embedded}
  62. className={className}
  63. branchList={branchList}
  64. selectedBranch={selectedBranch}
  65. branchesCount={branches?.length ?? 0}
  66. isBranchingEnabled={isBranchingEnabled}
  67. projectRef={ref}
  68. onClose={close}
  69. onCreateBranch={() => snap.setShowCreateBranchModal(true)}
  70. />
  71. )
  72. if (isLoading) return <ShimmeringLoader className="p-2 md:w-[90px]" />
  73. if (isError) return <AppLayoutDropdownError message="Failed to load branches" />
  74. if (!isSuccess) return null
  75. if (embedded) return commandContent
  76. return (
  77. <AppLayoutDropdownWithPopover
  78. linkHref={`/project/${ref}`}
  79. linkContent={
  80. <>
  81. <span
  82. title={isBranchingEnabled ? selectedBranch?.name : 'main'}
  83. className="text-sm text-foreground max-w-32 lg:max-w-64 truncate"
  84. >
  85. {isBranchingEnabled ? selectedBranch?.name : 'main'}
  86. </span>
  87. <BranchBadge branch={selectedBranch} isBranchingEnabled={isBranchingEnabled} />
  88. </>
  89. }
  90. linkClassName="flex items-center gap-2 shrink-0"
  91. commandContent={commandContent}
  92. open={open}
  93. onOpenChange={handleOpenChange}
  94. />
  95. )
  96. }