'use client' import React, { useState } from 'react' import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' import type { Item } from '@/app/components/base/select' import { PortalSelect } from '@/app/components/base/select' import type { GitHubRepoReleaseResponse } from '@/app/components/plugins/types' import { InstallStep } from '../../types' import Toast from '@/app/components/base/toast' type InstallFromGitHubProps = { onClose: () => void } type GitHubUrlInfo = { isValid: boolean owner?: string repo?: string } type InstallState = { step: InstallStep repoUrl: string selectedVersion: string selectedPackage: string releases: GitHubRepoReleaseResponse[] } const InstallFromGitHub: React.FC = ({ onClose }) => { const [state, setState] = useState({ step: InstallStep.url, repoUrl: '', selectedVersion: '', selectedPackage: '', releases: [], }) const versions: Item[] = state.releases.map(release => ({ value: release.tag_name, name: release.tag_name, })) const packages: Item[] = state.selectedVersion ? (state.releases .find(release => release.tag_name === state.selectedVersion) ?.assets .map(asset => ({ value: asset.browser_download_url, name: asset.name, })) || []) : [] const parseGitHubUrl = (url: string): GitHubUrlInfo => { const githubUrlRegex = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/?$/ const match = url.match(githubUrlRegex) if (match) { return { isValid: true, owner: match[1], repo: match[2], } } return { isValid: false } } const handleInstall = async () => { // try { // const response = await installPackageFromGitHub({ repo: state.repoUrl, version: state.selectedVersion, package: state.selectedPackage }) // if (response.plugin_unique_identifier) { // setState(prevState => ({...prevState, step: InstallStep.installed})) // console.log('Package installed:') // } // else { // console.error('Failed to install package:') // } // } // catch (error) { // console.error('Error installing package:') // } setState(prevState => ({ ...prevState, step: InstallStep.installed })) } const handleNext = async () => { switch (state.step) { case InstallStep.url: { const { isValid, owner, repo } = parseGitHubUrl(state.repoUrl) if (!isValid || !owner || !repo) { Toast.notify({ type: 'error', message: 'Invalid GitHub URL. Please enter a valid URL in the format: https://github.com/owner/repo', }) break } try { const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases`) if (!res.ok) throw new Error('Failed to fetch releases') const data = await res.json() const formattedReleases = data.map((release: any) => ({ tag_name: release.tag_name, assets: release.assets.map((asset: any) => ({ browser_download_url: asset.browser_download_url, id: asset.id, name: asset.name, })), })) setState(prevState => ({ ...prevState, releases: formattedReleases, step: InstallStep.version })) } catch (error) { Toast.notify({ type: 'error', message: 'Failed to fetch repository release', }) } break } case InstallStep.version: setState(prevState => ({ ...prevState, step: InstallStep.package })) break case InstallStep.package: handleInstall() break } } const handleBack = () => { setState((prevState) => { switch (prevState.step) { case InstallStep.version: return { ...prevState, step: InstallStep.url } case InstallStep.package: return { ...prevState, step: InstallStep.version } default: return prevState } }) } const isInputValid = () => { switch (state.step) { case InstallStep.url: return !!state.repoUrl.trim() case InstallStep.version: return !!state.selectedVersion case InstallStep.package: return !!state.selectedPackage default: return true } } const InfoRow = ({ label, value }: { label: string; value: string }) => (
{label}
{value}
) return (
Install plugin from GitHub
{state.step !== InstallStep.installed && 'Please make sure that you only install plugins from a trusted source.'}
{state.step === InstallStep.url && ( <> setState(prevState => ({ ...prevState, repoUrl: e.target.value }))} // TODO: needs to verify the url className='flex items-center self-stretch rounded-lg border border-components-input-border-active bg-components-input-bg-active shadows-shadow-xs p-2 gap-[2px] flex-grow overflow-hidden text-components-input-text-filled text-ellipsis system-sm-regular' placeholder='Please enter GitHub repo URL' /> )} {state.step === InstallStep.version && ( <> setState(prevState => ({ ...prevState, selectedVersion: item.value as string }))} items={versions} placeholder="Please select a version" popupClassName='w-[432px] z-[1001]' /> )} {state.step === InstallStep.package && ( <> setState(prevState => ({ ...prevState, selectedPackage: item.value as string }))} items={packages} placeholder="Please select a package" popupClassName='w-[432px] z-[1001]' /> )} {state.step === InstallStep.installed && ( <>
The plugin has been installed successfully.
{[ { label: 'Repository', value: state.repoUrl }, { label: 'Version', value: state.selectedVersion }, { label: 'Package', value: state.selectedPackage }, ].map(({ label, value }) => ( ))}
)}
{state.step === InstallStep.installed ? ( ) : ( <> )}
) } export default InstallFromGitHub