Skip to content

Commit

Permalink
Merge pull request #3616 from tinacms/create-branch
Browse files Browse the repository at this point in the history
Fix Create Branch
  • Loading branch information
Scott Byrne authored Feb 15, 2023
2 parents 29017de + 51240aa commit a612ee8
Show file tree
Hide file tree
Showing 7 changed files with 328 additions and 41 deletions.
6 changes: 6 additions & 0 deletions .changeset/angry-hats-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@tinacms/toolkit': patch
'tinacms': patch
---

Fixes branch switcher accessed from the branch banner, adds indexing status to branch list, improves UI clarity.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as React from 'react'
import { BiChevronDown, BiGitBranch, BiLinkExternal } from 'react-icons/bi'
import { FaSpinner } from 'react-icons/fa'
import { BiChevronDown, BiGitBranch } from 'react-icons/bi'
import {
Modal,
ModalBody,
Expand Down Expand Up @@ -66,8 +65,8 @@ const BranchModal = ({ close }: SubmitModalProps) => {
<ModalBody padded={false}>
<BranchSwitcher
listBranches={tinaApi.listBranches.bind(tinaApi)}
createBranch={() => {
return Promise.resolve('')
createBranch={async (data) => {
return await tinaApi.createBranch(data)
}}
chooseBranch={setCurrentBranch}
/>
Expand Down
133 changes: 110 additions & 23 deletions packages/@tinacms/toolkit/src/plugins/branch-switcher/BranchSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import { useBranchData } from './BranchData'
import { BaseTextField } from '../../packages/fields'
import { Button } from '../../packages/styles'
import { LoadingDots } from '../../packages/form-builder'
import { BiGitBranch, BiPlus, BiRefresh, BiSearch } from 'react-icons/bi'
import {
BiError,
BiGitBranch,
BiPlus,
BiRefresh,
BiSearch,
} from 'react-icons/bi'
import { GrCircleQuestion } from 'react-icons/gr'
import { MdArrowForward, MdOutlineClear } from 'react-icons/md'
import { AiFillWarning } from 'react-icons/ai'
import { FaSpinner } from 'react-icons/fa'
Expand Down Expand Up @@ -73,7 +80,7 @@ export const BranchSwitcher = ({
const cms = useCMS()
const isLocalMode = cms.api?.tina?.isLocalMode
const [listState, setListState] = React.useState<ListState>('loading')
const [branchList, setBranchList] = React.useState([])
const [branchList, setBranchList] = React.useState([] as Branch[])
const { currentBranch } = useBranchData()
const initialBranch = React.useMemo(() => currentBranch, [])
// when modal closes, refresh page is currentBranch has changed
Expand All @@ -91,7 +98,8 @@ export const BranchSwitcher = ({
branchName: value,
baseBranch: currentBranch,
}).then(async (createdBranchName) => {
chooseBranch(createdBranchName)
// @ts-ignore
cms.alerts.success('Branch created.')
await refreshBranchList()
})
}, [])
Expand All @@ -111,6 +119,30 @@ export const BranchSwitcher = ({
refreshBranchList()
}, [])

// Keep branch list up to date
React.useEffect(() => {
if (listState === 'ready') {
// update all branches that have indexing status of 'inprogress' or 'unknown'
branchList
.filter(
(x) =>
x?.indexStatus?.status === 'inprogress' ||
x?.indexStatus?.status === 'unknown'
)
.forEach(async (x) => {
const indexStatus = await cms.api.tina.waitForIndexStatus({
ref: x.name,
})
setBranchList((prev) => {
const newList = Array.from(prev)
const index = newList.findIndex((y) => y.name === x.name)
newList[index].indexStatus = indexStatus
return newList
})
})
}
}, [listState, branchList.length])

return (
<div className="w-full flex justify-center p-5">
<div className="w-full max-w-form">
Expand Down Expand Up @@ -179,6 +211,11 @@ const BranchSelector = ({
currentBranch,
onCreateBranch,
onChange,
}: {
branchList: Branch[]
currentBranch: string
onCreateBranch: (branchName: string) => void
onChange: (branchName: string) => void
}) => {
const [newBranchName, setNewBranchName] = React.useState('')
const [filter, setFilter] = React.useState('')
Expand Down Expand Up @@ -225,20 +262,52 @@ const BranchSelector = ({
<div className="min-w-[192px] max-h-[24rem] overflow-y-auto flex flex-col w-full h-full rounded-lg shadow-inner bg-white border border-gray-200">
{filteredBranchList.map((branch) => {
const isCurrentBranch = branch.name === currentBranch
// @ts-ignore
const indexingStatus = branch?.indexStatus?.status
return (
<div
className={`cursor-pointer relative text-base py-1.5 px-3 flex items-center gap-1.5 border-l-0 border-t-0 border-r-0 border-b border-gray-50 w-full outline-none transition-all ease-out duration-150 hover:text-blue-500 focus:text-blue-500 focus:bg-gray-50 hover:bg-gray-50 ${
isCurrentBranch
? 'bg-blue-50 text-blue-800 pointer-events-none'
: ''
className={`relative text-base py-1.5 px-3 flex items-center gap-1.5 border-l-0 border-t-0 border-r-0 border-b border-gray-50 w-full outline-none transition-all ease-out duration-150 ${
indexingStatus !== 'complete'
? 'bg-gray-50 text-gray-400 pointer-events-none'
: isCurrentBranch
? 'cursor-pointer bg-blue-50 text-blue-800 pointer-events-none hover:text-blue-500 focus:text-blue-500 focus:bg-gray-50 hover:bg-gray-50'
: 'cursor-pointer hover:text-blue-500 focus:text-blue-500 focus:bg-gray-50 hover:bg-gray-50'
}`}
key={branch}
onClick={() => onChange(branch.name)}
key={branch.name}
onClick={() => {
if (indexingStatus === 'complete') {
onChange(branch.name)
}
}}
>
{isCurrentBranch && (
<BiGitBranch className="w-5 h-auto text-blue-500/70" />
)}
{branch.name}
{indexingStatus === 'unknown' && (
<span className="flex-1 w-full flex justify-end items-center gap-2 text-blue-500">
<span className="opacity-50 italic">{`Unknown`}</span>
<GrCircleQuestion className="w-5 h-auto opacity-70" />
</span>
)}
{indexingStatus === 'inprogress' && (
<span className="flex-1 w-full flex justify-end items-center gap-2 text-blue-500">
<span className="opacity-50 italic">{`Indexing`}</span>
<FaSpinner className="w-5 h-auto opacity-70 animate-spin" />
</span>
)}
{indexingStatus === 'failed' && (
<span className="flex-1 w-full flex justify-end items-center gap-2 text-red-500">
<span className="opacity-50 italic">{`Indexing failed`}</span>
<BiError className="w-5 h-auto opacity-70" />
</span>
)}
{indexingStatus === 'timeout' && (
<span className="flex-1 w-full flex justify-end items-center gap-2 text-red-500">
<span className="opacity-50 italic">{`Indexing timed out`}</span>
<BiError className="w-5 h-auto opacity-70" />
</span>
)}
{isCurrentBranch && (
<span className="opacity-70 italic">{` (current)`}</span>
)}
Expand All @@ -247,21 +316,39 @@ const BranchSelector = ({
})}
</div>
)}
<div className="flex justify-between items-center w-full gap-3">
<BaseTextField
placeholder="Branch Name"
value={newBranchName}
onChange={(e) => setNewBranchName(e.target.value)}
/>
<Button
className="flex-0 flex items-center gap-2 whitespace-nowrap"
size="medium"
variant="primary"
onClick={() => onCreateBranch(newBranchName)}
>
<BiPlus className="w-5 h-auto opacity-70" /> Create New
</Button>
<div className="border-t border-gray-150 pt-4 mt-3 flex flex-col gap-3">
<div className="text-sm">
Create a new branch from <b>{currentBranch}</b>. Once created you will
need to wait for indexing to complete before you can switch branches.
</div>
<div className="flex justify-between items-center w-full gap-3">
<BaseTextField
placeholder="Branch Name"
value={newBranchName}
onChange={(e) => setNewBranchName(e.target.value)}
/>
<Button
className="flex-0 flex items-center gap-2 whitespace-nowrap"
size="medium"
variant="white"
disabled={newBranchName === ''}
onClick={() => onCreateBranch(stringToBranchName(newBranchName))}
>
<BiPlus className="w-5 h-auto opacity-70" /> Create Branch
</Button>
</div>
</div>
</div>
)
}

const stringToBranchName = (string) => {
return string
.toString()
.toLowerCase()
.replace(/\s+/g, '-')
.replace(/[^\w\-]+/g, '')
.replace(/\-\-+/g, '-')
.replace(/^-+/, '')
.replace(/-+$/, '')
}
14 changes: 9 additions & 5 deletions packages/@tinacms/toolkit/src/plugins/branch-switcher/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
/**
*/
import { TinaCMS } from '../../tina-cms'

export interface Branch {
indexStatus: {
status?: 'unknown' | 'complete' | 'failed' | 'inprogress' | 'timeout'
timestamp?: number
}
name: string
protected?: boolean
commit?: {
sha?: string
url?: string
}
}

export interface BranchSwitcherProps {
Expand Down
3 changes: 2 additions & 1 deletion packages/tinacms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
"prism-react-renderer": "^1.3.5",
"react-icons": "^4.3.1",
"react-router-dom": "6",
"yup": "^0.32.0"
"yup": "^0.32.0",
"zod": "^3.14.3"
},
"devDependencies": {
"@graphql-tools/utils": "^8.6.1",
Expand Down
Loading

0 comments on commit a612ee8

Please sign in to comment.