import React, { Fragment, PureComponent } from 'react'; import { autoBindMethodsForReact } from 'class-autobind-decorator'; import { AUTOBIND_CFG } from '../../../common/constants'; import Modal from '../base/modal'; import ModalBody from '../base/modal-body'; import ModalHeader from '../base/modal-header'; import { GitVCS } from '../../../sync/git/git-vcs'; import classnames from 'classnames'; import PromptButton from '../base/prompt-button'; import { database as db } from '../../../common/database'; import type { GitRepository } from '../../../models/git-repository'; import ModalFooter from '../base/modal-footer'; interface Props { vcs: GitVCS; gitRepository: GitRepository; handleInitializeEntities: () => Promise; handleGitBranchChanged: (branch: string) => void; } interface State { error: string; branches: string[]; remoteBranches: string[]; branch: string; newBranchName: string; } @autoBindMethodsForReact(AUTOBIND_CFG) class GitBranchesModal extends PureComponent { modal: Modal | null = null; input: HTMLInputElement | null = null; _onHide: (() => void) | null; state: State = { error: '', branch: '??', branches: [], remoteBranches: [], newBranchName: '', } _setModalRef(ref: Modal) { this.modal = ref; } _setInputRef(ref: HTMLInputElement) { this.input = ref; } async show( options: { onHide?: () => void; } = {}, ) { await this._refreshState({ newBranchName: '', }); this._onHide = options.onHide || null; this.modal && this.modal.show(); // Focus input when modal shows setTimeout(() => { this.input && this.input.focus(); }, 100); // Do a fetch of remotes and refresh again. NOTE: we're doing this // last because it's super slow const { vcs, gitRepository } = this.props; await vcs.fetch(false, 1, gitRepository.credentials); await this._refreshState(); } async _refreshState(newState?: Record) { const { vcs, handleGitBranchChanged } = this.props; const branch = await vcs.getBranch(); const branches = await vcs.listBranches(); const remoteBranches = await vcs.listRemoteBranches(); this.setState({ branch, branches, remoteBranches, ...newState, }); handleGitBranchChanged(branch); } _handleClearError() { this.setState({ error: '', }); } _handleHide() { if (this._onHide) { this._onHide(); } } async _errorHandler(cb: () => Promise) { try { return await cb(); } catch (err) { this.setState({ error: err.message, }); } } async _handleCreate(e: React.SyntheticEvent) { e.preventDefault(); await this._errorHandler(async () => { const { vcs } = this.props; const { newBranchName } = this.state; await vcs.checkout(newBranchName); await this._refreshState({ newBranchName: '', }); }); } async _handleMerge(branch: string) { await this._errorHandler(async () => { const { vcs } = this.props; await vcs.merge(branch); // Apparently merge doesn't update the working dir so need to checkout too await this._handleCheckout(branch); }); } async _handleDelete(branch: string) { await this._errorHandler(async () => { const { vcs } = this.props; await vcs.deleteBranch(branch); await this._refreshState(); }); } async _handleRemoteCheckout(branch: string) { await this._errorHandler(async () => { const { vcs, gitRepository } = this.props; // First fetch more history to make sure we have lots await vcs.fetch(true, 20, gitRepository.credentials); await this._handleCheckout(branch); }); } async _handleCheckout(branch: string) { await this._errorHandler(async () => { const { vcs, handleInitializeEntities } = this.props; const bufferId = await db.bufferChanges(); await vcs.checkout(branch); await db.flushChanges(bufferId, true); await handleInitializeEntities(); await this._refreshState(); }); } _updateNewBranchName(e: React.SyntheticEvent) { this.setState({ newBranchName: e.currentTarget.value, }); } hide() { this.modal && this.modal.hide(); } render() { const { branch: currentBranch, branches, remoteBranches, newBranchName, error } = this.state; const remoteOnlyBranches = remoteBranches.filter(b => !branches.includes(b)); return ( Local Branches {error && (

{error}

)}
{branches.map(name => ( ))}
Branches  
{name} {name === currentBranch ? ( (current) ) : null} {name !== currentBranch && ( this._handleMerge(name)}> Merge this._handleDelete(name)}> Delete )}
{remoteOnlyBranches.length > 0 && (
{remoteOnlyBranches.map(name => ( ))}
Remote Branches  
{name}
)}
{currentBranch}
); } } export default GitBranchesModal;