parent
6c72d7c231
commit
df103f9870
@ -0,0 +1,189 @@ |
|||||||
|
import React, { useEffect, useReducer, useState } from 'react' |
||||||
|
import { add, addall, checkout, checkoutfile, clone, commit, createBranch, remoteBranches, repositories, rm, getCommitChanges, diff, resolveRef, getBranchCommits, setUpstreamRemote, getGitHubUser, getBranches, getRemotes, remoteCommits } from '../lib/gitactions' |
||||||
|
import { loadFiles, setCallBacks } from '../lib/listeners' |
||||||
|
import { openDiff, openFile, saveToken, setModifiedDecorator, setPlugin, setUntrackedDecorator, statusChanged } from '../lib/pluginActions' |
||||||
|
import { gitActionsContext, pluginActionsContext } from '../state/context' |
||||||
|
import { gitReducer } from '../state/gitreducer' |
||||||
|
import { defaultGitState, defaultLoaderState, gitState, loaderState } from '../types' |
||||||
|
import { SourceControl } from './panels/sourcontrol' |
||||||
|
import { Accordion } from "react-bootstrap"; |
||||||
|
import { CommitMessage } from './panels/commitmessage' |
||||||
|
import { Commits } from './panels/commits' |
||||||
|
import { Branches } from './panels/branches' |
||||||
|
import { SourceControlNavigation } from './navigation/sourcecontrol' |
||||||
|
import { BranchesNavigation } from './navigation/branches' |
||||||
|
import { CommitslNavigation } from './navigation/commits' |
||||||
|
import '../style/index.css' |
||||||
|
import { CloneNavigation } from './navigation/clone' |
||||||
|
import { Clone } from './panels/clone' |
||||||
|
import { Commands } from './panels/commands' |
||||||
|
import { CommandsNavigation } from './navigation/commands' |
||||||
|
import { RemotesNavigation } from './navigation/remotes' |
||||||
|
import { Remotes } from './panels/remotes' |
||||||
|
import { ViewPlugin } from '@remixproject/engine-web' |
||||||
|
import { SettingsNavigation } from './navigation/settings' |
||||||
|
import { Settings } from './panels/settings' |
||||||
|
import { GitHubNavigation } from './navigation/github' |
||||||
|
import { GitHubAuth } from './panels/github' |
||||||
|
import { GitHubCredentials } from './panels/githubcredentials' |
||||||
|
import { loaderReducer } from '../state/loaderReducer' |
||||||
|
|
||||||
|
export const gitPluginContext = React.createContext<gitState>(defaultGitState) |
||||||
|
export const loaderContext = React.createContext<loaderState>(defaultLoaderState) |
||||||
|
|
||||||
|
interface IGitUi { |
||||||
|
plugin: ViewPlugin |
||||||
|
} |
||||||
|
|
||||||
|
export const GitUI = (props: IGitUi) => { |
||||||
|
const plugin = props.plugin |
||||||
|
const [gitState, gitDispatch] = useReducer(gitReducer, defaultGitState) |
||||||
|
const [loaderState, loaderDispatch] = useReducer(loaderReducer, defaultLoaderState) |
||||||
|
const [activePanel, setActivePanel] = useState<string>("0"); |
||||||
|
const [timeOut, setTimeOut] = useState<number>(null) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
setCallBacks(plugin, gitDispatch, loaderDispatch) |
||||||
|
setPlugin(plugin, gitDispatch, loaderDispatch) |
||||||
|
console.log(props) |
||||||
|
}, []) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
|
||||||
|
|
||||||
|
async function setDecorators(gitState: gitState) { |
||||||
|
await plugin.call('fileDecorator', 'clearFileDecorators') |
||||||
|
await setModifiedDecorator(gitState.modified) |
||||||
|
await setUntrackedDecorator(gitState.untracked) |
||||||
|
} |
||||||
|
|
||||||
|
console.log('gitState.fileStatusResult', gitState.fileStatusResult) |
||||||
|
|
||||||
|
setTimeout(() => { |
||||||
|
setDecorators(gitState) |
||||||
|
}) |
||||||
|
|
||||||
|
|
||||||
|
}, [gitState.fileStatusResult]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
|
||||||
|
|
||||||
|
async function updatestate(){ |
||||||
|
console.log('updatestate', gitState) |
||||||
|
if(gitState.currentBranch.remote.url){ |
||||||
|
remoteCommits(gitState.currentBranch.remote.url, gitState.currentBranch.name, 1) |
||||||
|
} |
||||||
|
} |
||||||
|
setTimeout(() => { |
||||||
|
updatestate() |
||||||
|
}) |
||||||
|
|
||||||
|
}, [gitState.gitHubUser, gitState.currentBranch, gitState.remotes]) |
||||||
|
|
||||||
|
|
||||||
|
const gitActionsProviderValue = { |
||||||
|
commit, |
||||||
|
addall, |
||||||
|
add, |
||||||
|
checkoutfile, |
||||||
|
rm, |
||||||
|
checkout, |
||||||
|
createBranch, |
||||||
|
clone, |
||||||
|
repositories, |
||||||
|
remoteBranches, |
||||||
|
getCommitChanges, |
||||||
|
getBranchCommits, |
||||||
|
diff, |
||||||
|
resolveRef, |
||||||
|
setUpstreamRemote, |
||||||
|
getGitHubUser, |
||||||
|
getBranches, |
||||||
|
getRemotes |
||||||
|
} |
||||||
|
|
||||||
|
const pluginActionsProviderValue = { |
||||||
|
statusChanged, |
||||||
|
loadFiles, |
||||||
|
openFile, |
||||||
|
openDiff, |
||||||
|
saveToken |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="m-1"> |
||||||
|
<gitPluginContext.Provider value={gitState}> |
||||||
|
<loaderContext.Provider value={loaderState}> |
||||||
|
<gitActionsContext.Provider value={gitActionsProviderValue}> |
||||||
|
<pluginActionsContext.Provider value={pluginActionsProviderValue}> |
||||||
|
{gitState.loading && <div className="text-center py-5"><i className="fas fa-spinner fa-pulse fa-2x"></i></div>} |
||||||
|
{!gitState.loading && |
||||||
|
<Accordion activeKey={activePanel} defaultActiveKey="0"> |
||||||
|
<SourceControlNavigation eventKey="0" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
|
||||||
|
<Accordion.Collapse className='bg-light' eventKey="0"> |
||||||
|
<> |
||||||
|
<CommitMessage /> |
||||||
|
<SourceControl /> |
||||||
|
</> |
||||||
|
</Accordion.Collapse> |
||||||
|
<hr></hr> |
||||||
|
<CommandsNavigation eventKey="1" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
<Accordion.Collapse className='bg-light' eventKey="1"> |
||||||
|
<> |
||||||
|
<Commands></Commands> |
||||||
|
</> |
||||||
|
</Accordion.Collapse> |
||||||
|
<hr></hr> |
||||||
|
<CommitslNavigation eventKey="3" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
<Accordion.Collapse className='bg-light' eventKey="3"> |
||||||
|
<> |
||||||
|
<Commits /> |
||||||
|
</> |
||||||
|
</Accordion.Collapse> |
||||||
|
<hr></hr> |
||||||
|
<BranchesNavigation eventKey="2" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
<Accordion.Collapse className='bg-light' eventKey="2"> |
||||||
|
<> |
||||||
|
<Branches /></> |
||||||
|
</Accordion.Collapse> |
||||||
|
<hr></hr> |
||||||
|
<CloneNavigation eventKey="4" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
<Accordion.Collapse className='bg-light' eventKey="4"> |
||||||
|
<> |
||||||
|
<Clone /></> |
||||||
|
</Accordion.Collapse> |
||||||
|
<hr></hr> |
||||||
|
<RemotesNavigation eventKey="5" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
<Accordion.Collapse className='bg-light' eventKey="5"> |
||||||
|
<> |
||||||
|
<Remotes></Remotes> |
||||||
|
</> |
||||||
|
</Accordion.Collapse> |
||||||
|
<hr></hr> |
||||||
|
<SettingsNavigation eventKey="6" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
<Accordion.Collapse className='bg-light' eventKey="6"> |
||||||
|
<> |
||||||
|
<Settings></Settings> |
||||||
|
</> |
||||||
|
</Accordion.Collapse> |
||||||
|
<hr></hr> |
||||||
|
<GitHubNavigation eventKey="7" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
<Accordion.Collapse className='bg-light' eventKey="7"> |
||||||
|
<> |
||||||
|
<GitHubAuth></GitHubAuth> |
||||||
|
<GitHubCredentials></GitHubCredentials> |
||||||
|
</> |
||||||
|
</Accordion.Collapse> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</Accordion>} |
||||||
|
</pluginActionsContext.Provider> |
||||||
|
</gitActionsContext.Provider> |
||||||
|
</loaderContext.Provider> |
||||||
|
</gitPluginContext.Provider> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
import { faCaretUp, faCaretDown, faCaretRight, faArrowUp, faArrowDown, faArrowRotateRight, faArrowsUpDown, faGlobe, faCheckCircle, faToggleOff, faToggleOn } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import React, { useContext, useEffect } from "react"; |
||||||
|
import { branch } from "../../types"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
|
||||||
|
interface BrancheDetailsNavigationProps { |
||||||
|
eventKey: string; |
||||||
|
activePanel: string; |
||||||
|
callback: (eventKey: string) => void; |
||||||
|
branch: branch; |
||||||
|
checkout: (branch: branch) => void; |
||||||
|
} |
||||||
|
|
||||||
|
export const BrancheDetailsNavigation = (props: BrancheDetailsNavigationProps) => { |
||||||
|
const { eventKey, activePanel, callback, branch, checkout } = props; |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const openRemote = () => { |
||||||
|
window.open(`${branch.remote.url}/tree/${branch.name}`, '_blank'); |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className="d-flex flex-row w-100 mb-2 mt-2"> |
||||||
|
<div onClick={() => handleClick()} role={'button'} className='pointer d-flex flex-row w-100 commit-navigation'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<i className="fa fa-code-branch ml-1"></i> |
||||||
|
<div className={`ml-1 ${context.currentBranch.name === branch.name ? 'text-success' : ''}`}>{branch.name} {branch.remote ? `on ${branch.remote.remote}` : ''}</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
{context.currentBranch.name === branch.name ? |
||||||
|
<FontAwesomeIcon className='ml-auto mr-1 pointer text-success' icon={faToggleOff} onClick={() => checkout(branch)}></FontAwesomeIcon> |
||||||
|
: |
||||||
|
<FontAwesomeIcon className='ml-auto mr-1 pointer' icon={faToggleOn} onClick={() => checkout(branch)}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<FontAwesomeIcon className='ml-auto pointer' icon={faArrowsUpDown} onClick={() => checkout(branch)}></FontAwesomeIcon> |
||||||
|
{branch.remote?.url && <FontAwesomeIcon className='ml-2 pointer' icon={faGlobe} onClick={() => openRemote()}></FontAwesomeIcon>} |
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
import { faCaretDown, faCaretRight } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import React, { } from "react"; |
||||||
|
import { gitActionsContext, pluginActionsContext } from "../../state/context"; |
||||||
|
|
||||||
|
export const BranchesNavigation = ({ eventKey, activePanel, callback }) => { |
||||||
|
const pluginactions = React.useContext(pluginActionsContext) |
||||||
|
const context = React.useContext(gitActionsContext) |
||||||
|
|
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className={'d-flex justify-content-between pt-1 ' + (activePanel === eventKey? 'bg-light': '')}> |
||||||
|
<span onClick={()=>handleClick()} role={'button'} className='nav d-flex justify-content-start align-items-center w-75'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<label className="pl-1 nav form-check-label">BRANCHES</label> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
import { faCaretUp, faCaretDown, faArrowUp, faArrowDown, faArrowRotateRight, faCaretRight } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import React, { useContext, useEffect } from "react"; |
||||||
|
|
||||||
|
export const CloneNavigation = ({ eventKey, activePanel, callback }) => { |
||||||
|
|
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className={'d-flex justify-content-between pb-1 pt-1 ' + (activePanel === eventKey? 'bg-light': '')}> |
||||||
|
<span onClick={()=>handleClick()} role={'button'} className='nav d-flex justify-content-start align-items-center w-75'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<label className="pl-1 nav form-check-label">CLONE</label> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
import { faCaretUp, faCaretDown, faArrowUp, faArrowDown, faArrowRotateRight, faCaretRight, faCircleCheck, faArrowsUpDown } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import { CustomTooltip } from "@remix-ui/helper"; |
||||||
|
import React, { useContext, useEffect } from "react"; |
||||||
|
import { FormattedMessage } from "react-intl"; |
||||||
|
import { pluginActionsContext } from "../../state/context"; |
||||||
|
import { SourceControlMenu } from "./menu/sourcecontrolmenu"; |
||||||
|
|
||||||
|
export const CommandsNavigation = ({ eventKey, activePanel, callback }) => { |
||||||
|
const pluginactions = React.useContext(pluginActionsContext) |
||||||
|
|
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className={'d-flex justify-content-between ' + (activePanel === eventKey ? 'bg-light' : '')}> |
||||||
|
<span onClick={() => handleClick()} role={'button'} className='nav d-flex justify-content-start align-items-center w-75'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<label className="pl-1 nav form-check-label">COMMANDS</label> |
||||||
|
|
||||||
|
|
||||||
|
</span> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? |
||||||
|
<span className='d-flex justify-content-end align-items-center w-25'> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Pull" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faArrowDown} className="" /></button> |
||||||
|
</CustomTooltip> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Push" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faArrowUp} className="" /></button> |
||||||
|
</CustomTooltip> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Sync changes" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faArrowsUpDown} className="" /></button> |
||||||
|
</CustomTooltip> |
||||||
|
</span> : null |
||||||
|
} |
||||||
|
|
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
import { faCaretUp, faCaretDown, faCaretRight, faArrowUp, faArrowDown, faArrowRotateRight } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import React, { useContext, useEffect } from "react"; |
||||||
|
import { CommitSummary } from "../panels/commits/commitsummary"; |
||||||
|
|
||||||
|
export const CommitDetailsNavigation = ({ eventKey, activePanel, callback, commit, checkout }) => { |
||||||
|
|
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div onClick={() => handleClick()} role={'button'} className='pointer mb-2 mt-2 w-100 d-flex flex-row commit-navigation'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
|
||||||
|
<CommitSummary commit={commit} checkout={checkout}></CommitSummary> |
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
import { faCaretDown, faArrowUp, faArrowDown, faArrowRotateRight, faCaretRight, faArrowsUpDown } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import { CustomTooltip } from "@remix-ui/helper"; |
||||||
|
import React, { } from "react"; |
||||||
|
import { FormattedMessage } from "react-intl"; |
||||||
|
import { pluginActionsContext } from "../../state/context"; |
||||||
|
|
||||||
|
export const CommitslNavigation = ({ eventKey, activePanel, callback }) => { |
||||||
|
const pluginactions = React.useContext(pluginActionsContext) |
||||||
|
|
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className={'d-flex justify-content-between ' + (activePanel === eventKey ? 'bg-light' : '')}> |
||||||
|
<span onClick={() => handleClick()} role={'button'} className='nav d-flex justify-content-start align-items-center w-75'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<label className="pl-1 nav form-check-label">COMMITS</label> |
||||||
|
|
||||||
|
|
||||||
|
</span> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? |
||||||
|
<span className='d-flex justify-content-end align-items-center w-25'> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Pull" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faArrowDown} className="" /></button> |
||||||
|
</CustomTooltip> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Push" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faArrowUp} className="" /></button> |
||||||
|
</CustomTooltip> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Sync changes" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faArrowsUpDown} className="" /></button> |
||||||
|
</CustomTooltip>
|
||||||
|
</span> : null |
||||||
|
} |
||||||
|
|
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
import { faCaretDown, faCaretRight } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import React, { } from "react"; |
||||||
|
import { pluginActionsContext } from "../../state/context"; |
||||||
|
|
||||||
|
export const GitHubNavigation = ({ eventKey, activePanel, callback }) => { |
||||||
|
const pluginactions = React.useContext(pluginActionsContext) |
||||||
|
|
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className={'d-flex justify-content-between pt-1 pb-1 ' + (activePanel === eventKey? 'bg-light': '')}> |
||||||
|
<span onClick={()=>handleClick()} role={'button'} className='nav d-flex justify-content-start align-items-center w-75'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<label className="pl-1 nav form-check-label">GITHUB</label> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
import { count } from "console" |
||||||
|
import { CustomIconsToggle, CustomMenu, CustomTooltip } from "@remix-ui/helper" |
||||||
|
import React, { useState } from "react" |
||||||
|
import { Dropdown } from "react-bootstrap" |
||||||
|
import { FormattedMessage } from "react-intl" |
||||||
|
|
||||||
|
export const SourceControlMenu = () => { |
||||||
|
const [showIconsMenu, hideIconsMenu] = useState<boolean>(false) |
||||||
|
return ( |
||||||
|
<Dropdown id="workspacesMenuDropdown" data-id="sourceControlMenuDropdown" onToggle={() => hideIconsMenu(!showIconsMenu)} show={showIconsMenu}> |
||||||
|
<Dropdown.Toggle |
||||||
|
onClick={() => { |
||||||
|
hideIconsMenu(!showIconsMenu) |
||||||
|
}} |
||||||
|
as={CustomIconsToggle} |
||||||
|
icon={'fas fa-bars'} |
||||||
|
></Dropdown.Toggle> |
||||||
|
<Dropdown.Menu as={CustomMenu} data-id="wsdropdownMenu" className='custom-dropdown-items remixui_menuwidth' rootCloseEvent="click"> |
||||||
|
<Dropdown.Item key={0}> |
||||||
|
<CustomTooltip |
||||||
|
placement="right-start" |
||||||
|
tooltipId="cloneWorkspaceTooltip" |
||||||
|
tooltipClasses="text-nowrap" |
||||||
|
tooltipText={<FormattedMessage id='filePanel.workspace.clone' defaultMessage='Clone Git Repository' />} |
||||||
|
> |
||||||
|
<div |
||||||
|
data-id='cloneGitRepository' |
||||||
|
onClick={() => { |
||||||
|
hideIconsMenu(!showIconsMenu) |
||||||
|
}} |
||||||
|
key={`cloneGitRepository-fe-ws`} |
||||||
|
> |
||||||
|
<span |
||||||
|
id='cloneGitRepository' |
||||||
|
data-id='cloneGitRepository' |
||||||
|
onClick={() => { |
||||||
|
hideIconsMenu(!showIconsMenu) |
||||||
|
}} |
||||||
|
className='fab fa-github pl-2' |
||||||
|
> |
||||||
|
</span> |
||||||
|
<span className="pl-3"><FormattedMessage id='filePanel.clone' defaultMessage='Clone' /></span> |
||||||
|
</div> |
||||||
|
</CustomTooltip> |
||||||
|
</Dropdown.Item> |
||||||
|
</Dropdown.Menu> |
||||||
|
</Dropdown> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
import { faCaretDown, faCaretRight } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import React, { } from "react"; |
||||||
|
import { gitActionsContext, pluginActionsContext } from "../../state/context"; |
||||||
|
|
||||||
|
export const RemotesNavigation = ({ eventKey, activePanel, callback }) => { |
||||||
|
const pluginactions = React.useContext(pluginActionsContext) |
||||||
|
const context = React.useContext(gitActionsContext) |
||||||
|
|
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className={'d-flex justify-content-between pt-1 pb-1 ' + (activePanel === eventKey? 'bg-light': '')}> |
||||||
|
<span onClick={()=>handleClick()} role={'button'} className='nav d-flex justify-content-start align-items-center w-75'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<label className="pl-1 nav form-check-label">REMOTES</label> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
import { faCaretDown, faCaretRight, faArrowRightArrowLeft, faGlobe } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import React, { useContext, useEffect } from "react"; |
||||||
|
import { branch, remote } from "../../types"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
|
||||||
|
interface RemotesDetailsNavigationProps { |
||||||
|
eventKey: string; |
||||||
|
activePanel: string; |
||||||
|
callback: (eventKey: string) => void; |
||||||
|
remote: remote; |
||||||
|
} |
||||||
|
|
||||||
|
export const RemotesDetailsNavigation = (props: RemotesDetailsNavigationProps) => { |
||||||
|
const { eventKey, activePanel, callback, remote } = props; |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const openRemote = () => { |
||||||
|
window.open(`${remote.url}`, '_blank'); |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className="d-flex flex-row w-100 mb-2 mt-2"> |
||||||
|
<div onClick={() => handleClick()} role={'button'} className='pointer d-flex flex-row w-100 commit-navigation'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<div className={`ml-1`}>{remote.remote} <FontAwesomeIcon className='' icon={faArrowRightArrowLeft}></FontAwesomeIcon> {remote.url}</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
{remote?.url && <FontAwesomeIcon className='ml-2 pointer' icon={faGlobe} onClick={() => openRemote()}></FontAwesomeIcon>} |
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
import { faCaretUp, faCaretDown, faArrowUp, faArrowDown, faArrowRotateRight, faCaretRight, faArrowsUpDown, faTriangleExclamation } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import { CustomTooltip } from "@remix-ui/helper"; |
||||||
|
import React, { useContext, useEffect } from "react"; |
||||||
|
import { FormattedMessage } from "react-intl"; |
||||||
|
import { pluginActionsContext } from "../../state/context"; |
||||||
|
|
||||||
|
export const SettingsNavigation = ({ eventKey, activePanel, callback }) => { |
||||||
|
const pluginactions = React.useContext(pluginActionsContext) |
||||||
|
|
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className={'d-flex justify-content-between ' + (activePanel === eventKey ? 'bg-light' : '')}> |
||||||
|
<span onClick={() => handleClick()} role={'button'} className='nav d-flex justify-content-start align-items-center w-75'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<label className="nav pl-1 form-check-label">SETTINGS</label> |
||||||
|
|
||||||
|
|
||||||
|
</span> |
||||||
|
|
||||||
|
<span className='d-flex justify-content-end align-items-center w-25'> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Missing values" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm text-warning'><FontAwesomeIcon icon={faTriangleExclamation} className="" /></button> |
||||||
|
</CustomTooltip> |
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
|
|
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
import { faCaretUp, faCaretDown, faArrowUp, faArrowDown, faArrowRotateRight, faCaretRight, faArrowsUpDown } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import { CustomTooltip } from "@remix-ui/helper"; |
||||||
|
import React, { useContext, useEffect } from "react"; |
||||||
|
import { FormattedMessage } from "react-intl"; |
||||||
|
import { pluginActionsContext } from "../../state/context"; |
||||||
|
import { SourceControlMenu } from "./menu/sourcecontrolmenu"; |
||||||
|
|
||||||
|
export const SourceControlNavigation = ({ eventKey, activePanel, callback }) => { |
||||||
|
const pluginactions = React.useContext(pluginActionsContext) |
||||||
|
|
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className={'d-flex justify-content-between ' + (activePanel === eventKey ? 'bg-light' : '')}> |
||||||
|
<span onClick={() => handleClick()} role={'button'} className='nav d-flex justify-content-start align-items-center w-75'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<label className="nav pl-1 form-check-label">SOURCE CONTROL</label> |
||||||
|
|
||||||
|
|
||||||
|
</span> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? |
||||||
|
<span className='d-flex justify-content-end align-items-center w-25'> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Push" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faArrowUp} className="" /></button> |
||||||
|
</CustomTooltip> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Pull" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faArrowDown} className="" /></button> |
||||||
|
</CustomTooltip> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Sync changes" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faArrowsUpDown} className="" /></button> |
||||||
|
</CustomTooltip> |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Refresh" />}> |
||||||
|
<button onClick={async () => { await pluginactions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faArrowRotateRight} className="" /></button> |
||||||
|
</CustomTooltip> |
||||||
|
|
||||||
|
</span> : null |
||||||
|
} |
||||||
|
|
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
import { faCaretUp, faCaretDown, faArrowUp, faArrowDown, faArrowRotateRight, faCaretRight, faArrowsUpDown, faPlus, faMinus } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import { CustomTooltip } from "@remix-ui/helper"; |
||||||
|
import React, { useContext, useEffect } from "react"; |
||||||
|
import { FormattedMessage } from "react-intl"; |
||||||
|
import { gitActionsContext, pluginActionsContext } from "../../state/context"; |
||||||
|
import { sourceControlGroup } from "../../types"; |
||||||
|
|
||||||
|
interface SourceControlGroupNavigationProps { |
||||||
|
eventKey: string; |
||||||
|
activePanel: string; |
||||||
|
callback: (eventKey: string) => void; |
||||||
|
group: sourceControlGroup |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export const SourceControlGroupNavigation = (props: SourceControlGroupNavigationProps) => { |
||||||
|
const { eventKey, activePanel, callback, group } = props; |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const pluginActions = React.useContext(pluginActionsContext) |
||||||
|
const handleClick = () => { |
||||||
|
if (!callback) return |
||||||
|
if (activePanel === eventKey) { |
||||||
|
callback('') |
||||||
|
} else { |
||||||
|
callback(eventKey) |
||||||
|
} |
||||||
|
} |
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className={'d-flex justify-content-between pt-1 ' + (activePanel === eventKey? 'bg-light': '')}> |
||||||
|
<span onClick={()=>handleClick()} role={'button'} className='nav d-flex justify-content-start align-items-center w-75'> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? <FontAwesomeIcon className='' icon={faCaretDown}></FontAwesomeIcon> : <FontAwesomeIcon className='' icon={faCaretRight}></FontAwesomeIcon> |
||||||
|
} |
||||||
|
<label className="pl-1 nav form-check-label">{group.name}</label> |
||||||
|
</span> |
||||||
|
{ |
||||||
|
activePanel === eventKey ? |
||||||
|
<span className='d-flex justify-content-end align-items-center w-25'> |
||||||
|
{group.name === 'Changes' ? |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Stage All Changes" />}> |
||||||
|
<button onClick={async () => { await actions.addall() }} className='btn btn-sm'><FontAwesomeIcon icon={faPlus} className="" /></button> |
||||||
|
</CustomTooltip>: null} |
||||||
|
{group.name === 'Staged' ? |
||||||
|
<CustomTooltip tooltipText={<FormattedMessage id="Unstage All Changes" />}> |
||||||
|
<button onClick={async () => { await pluginActions.loadFiles() }} className='btn btn-sm'><FontAwesomeIcon icon={faMinus} className="" /></button> |
||||||
|
</CustomTooltip>: null} |
||||||
|
|
||||||
|
</span> : null |
||||||
|
} |
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
import React, { useState } from "react"; |
||||||
|
import { Alert } from "react-bootstrap"; |
||||||
|
import { gitActionsContext } from "../../state/context"; |
||||||
|
import { remote } from "../../types"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
import { BranchDetails } from "./branches/branchedetails"; |
||||||
|
|
||||||
|
export const Branches = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const [newBranch, setNewBranch] = useState({ value: "" }); |
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
||||||
|
setNewBranch({ value: e.currentTarget.value }); |
||||||
|
}; |
||||||
|
|
||||||
|
const checkout = async (oid: string, remote: remote) => { |
||||||
|
try { |
||||||
|
actions.checkout({ ref: oid, remote: remote.remote }); |
||||||
|
} catch (e) { |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
}; |
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className="pt-1"> |
||||||
|
{context.branches && context.branches.length ? |
||||||
|
<div> |
||||||
|
{context.branches && context.branches.map((branch, index) => { |
||||||
|
return ( |
||||||
|
<BranchDetails key={index} branch={branch}></BranchDetails> |
||||||
|
); |
||||||
|
})} |
||||||
|
<hr /> |
||||||
|
<label>create branch</label> |
||||||
|
<div className="form-group"> |
||||||
|
|
||||||
|
<input |
||||||
|
placeholder="branch name" |
||||||
|
onChange={handleChange} |
||||||
|
className="form-control w-md-25 w-100" |
||||||
|
type="text" |
||||||
|
id="newbranchname" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
<button |
||||||
|
onClick={async () => actions.createBranch(newBranch.value)} |
||||||
|
className="btn w-md-25 w-100 btn-primary" |
||||||
|
id="createbranch-btn" |
||||||
|
> |
||||||
|
create new branch |
||||||
|
</button> |
||||||
|
</div> : <div className="text-muted">No branches</div>} |
||||||
|
</div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
import { ReadCommitResult } from "isomorphic-git" |
||||||
|
import React, { useEffect, useState } from "react"; |
||||||
|
import { Accordion } from "react-bootstrap"; |
||||||
|
import { CommitDetailsNavigation } from "../../navigation/commitdetails"; |
||||||
|
import { gitActionsContext } from "../../../state/context"; |
||||||
|
import { gitPluginContext } from "../../gitui"; |
||||||
|
import { branch } from "../../../types"; |
||||||
|
import { BrancheDetailsNavigation } from "../../navigation/branchedetails"; |
||||||
|
import { CommitDetailsItems } from "../commits/commitdetailsitem"; |
||||||
|
import { CommitDetails } from "../commits/commitdetails"; |
||||||
|
|
||||||
|
export interface BrancheDetailsProps { |
||||||
|
branch: branch; |
||||||
|
} |
||||||
|
|
||||||
|
export const BranchDetails = (props: BrancheDetailsProps) => { |
||||||
|
const { branch } = props; |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const [activePanel, setActivePanel] = useState<string>(""); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (activePanel === "0") { |
||||||
|
console.log('GET BRANCH COMMITS', branch) |
||||||
|
actions.getBranchCommits(branch) |
||||||
|
} |
||||||
|
}, [activePanel]) |
||||||
|
|
||||||
|
const checkout = (branch: branch) => { |
||||||
|
actions.checkout({ |
||||||
|
ref: branch.name, |
||||||
|
remote: branch.remote && branch.remote.remote || null |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
const checkoutCommit = async (oid: string) => { |
||||||
|
try { |
||||||
|
//await ModalRef.current?.show();
|
||||||
|
actions.checkout({ ref: oid }) |
||||||
|
//Utils.log("yes");
|
||||||
|
} catch (e) { |
||||||
|
//Utils.log("no");
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
return (<Accordion activeKey={activePanel} defaultActiveKey=""> |
||||||
|
<BrancheDetailsNavigation checkout={checkout} branch={branch} eventKey="0" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
<Accordion.Collapse className="pl-2 border-left ml-1" eventKey="0"> |
||||||
|
<div className="ml-1"> |
||||||
|
{context.branchCommits && Object.entries(context.branchCommits).map(([key, value]) => { |
||||||
|
if(key == branch.name){ |
||||||
|
return value.map((commit, index) => { |
||||||
|
return(<CommitDetails key={index} checkout={checkoutCommit} commit={commit}></CommitDetails>) |
||||||
|
}) |
||||||
|
} |
||||||
|
})} |
||||||
|
|
||||||
|
</div> |
||||||
|
</Accordion.Collapse> |
||||||
|
</Accordion>) |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
|
||||||
|
import React, { useState } from "react"; |
||||||
|
import { Alert, Form, FormControl, InputGroup } from "react-bootstrap"; |
||||||
|
import { useLocalStorage } from "../../hooks/useLocalStorage"; |
||||||
|
import { gitActionsContext } from "../../state/context"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
import { Repositories } from "./repositories"; |
||||||
|
import { RemixUiCheckbox } from "@remix-ui/checkbox"; |
||||||
|
|
||||||
|
export const Clone = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const [cloneUrl, setCloneUrl] = useLocalStorage( |
||||||
|
"CLONE_URL", |
||||||
|
'' |
||||||
|
); |
||||||
|
|
||||||
|
const [cloneDepth, setCloneDepth] = useLocalStorage( |
||||||
|
"CLONE_DEPTH", |
||||||
|
1 |
||||||
|
); |
||||||
|
|
||||||
|
const [cloneBranch, setCloneBranch] = useLocalStorage( |
||||||
|
"CLONE_BRANCH", |
||||||
|
'' |
||||||
|
); |
||||||
|
|
||||||
|
const [url, setUrl] = useLocalStorage( |
||||||
|
"GITHUB_URL", |
||||||
|
'' |
||||||
|
); |
||||||
|
|
||||||
|
|
||||||
|
const clone = async () => { |
||||||
|
try { |
||||||
|
setTimeout(() => actions.clone(cloneUrl, cloneBranch, cloneDepth, !cloneAllBranches), 1500) |
||||||
|
} catch (e) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const onCloneBranchChange = (value: string) => { |
||||||
|
setCloneBranch(value) |
||||||
|
} |
||||||
|
|
||||||
|
const onGitHubCloneUrlChange = (value: string) => { |
||||||
|
setCloneUrl(value) |
||||||
|
} |
||||||
|
|
||||||
|
const onDepthChange = (value: number) => { |
||||||
|
setCloneDepth(value) |
||||||
|
} |
||||||
|
|
||||||
|
const [cloneAllBranches, setcloneAllBranches] = useLocalStorage( |
||||||
|
"GITHUB_CLONE_ALL_BRANCES", |
||||||
|
false |
||||||
|
); |
||||||
|
|
||||||
|
const onAllBranchChange = () => { |
||||||
|
setcloneAllBranches((e: any) => !e) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<InputGroup className="mb-1"> |
||||||
|
<FormControl id="cloneulr" placeholder="url" name='cloneurl' value={cloneUrl} onChange={e => onGitHubCloneUrlChange(e.target.value)} aria-describedby="urlprepend" /> |
||||||
|
</InputGroup> |
||||||
|
|
||||||
|
|
||||||
|
<input name='clonebranch' onChange={e => onCloneBranchChange(e.target.value)} value={cloneBranch} className="form-control mb-1 mt-2" placeholder="branch" type="text" id="clonebranch" /> |
||||||
|
<button disabled={!cloneUrl || !cloneBranch} data-id='clonebtn' className='btn btn-primary mt-1 w-100' onClick={async () => { |
||||||
|
clone() |
||||||
|
}}>clone</button> |
||||||
|
<hr /> |
||||||
|
<Repositories cloneAllBranches={cloneAllBranches} cloneDepth={cloneDepth} /> |
||||||
|
<hr /> |
||||||
|
<label>options</label> |
||||||
|
<InputGroup className="mt-1 mb-1"> |
||||||
|
<InputGroup.Prepend> |
||||||
|
<InputGroup.Text id="clonedepthprepend"> |
||||||
|
--depth |
||||||
|
</InputGroup.Text> |
||||||
|
</InputGroup.Prepend> |
||||||
|
<FormControl id="clonedepth" type="number" value={cloneDepth} onChange={e => onDepthChange(parseInt(e.target.value))} aria-describedby="clonedepthprepend" /> |
||||||
|
</InputGroup> |
||||||
|
|
||||||
|
<RemixUiCheckbox |
||||||
|
id={`cloneAllBranches`} |
||||||
|
inputType="checkbox" |
||||||
|
name="cloneAllBranches" |
||||||
|
label={`Clone all branches`} |
||||||
|
onClick={() => onAllBranchChange()} |
||||||
|
checked={cloneAllBranches} |
||||||
|
onChange={() => { }} |
||||||
|
/> |
||||||
|
|
||||||
|
|
||||||
|
<hr></hr> |
||||||
|
</>) |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
import React, { useEffect, useState } from "react"; |
||||||
|
import { PushPull } from "./commands/pushpull"; |
||||||
|
import { Fetch } from "./commands/fetch"; |
||||||
|
import { Merge } from "./commands/merge"; |
||||||
|
|
||||||
|
export const Commands = () => { |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<PushPull></PushPull> |
||||||
|
<hr></hr> |
||||||
|
<Fetch></Fetch> |
||||||
|
<hr></hr> |
||||||
|
<Merge></Merge> |
||||||
|
</>) |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
import React, { useEffect, useState } from "react"; |
||||||
|
|
||||||
|
|
||||||
|
export const Fetch = () => { |
||||||
|
|
||||||
|
const fetch = async () => { |
||||||
|
//gitservice.fetch(currentRemote, '', '')
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<button type="button" onClick={async () => fetch()} className="btn btn-primary w-100">Fetch</button> |
||||||
|
</>) |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
import React, { useEffect, useState } from "react"; |
||||||
|
import { gitActionsContext } from "../../../state/context"; |
||||||
|
import { gitPluginContext } from "../../gitui"; |
||||||
|
import { selectStyles, selectTheme } from "../../../types/styles"; |
||||||
|
import Select from 'react-select' |
||||||
|
|
||||||
|
export const Merge = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const [localBranch, setLocalBranch] = useState('') |
||||||
|
const [localBranchOptions, setLocalBranchOptions] = useState<any>([]); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
setLocalBranch(context.currentBranch.name) |
||||||
|
}, [context.currentBranch]) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const onLocalBranchChange = (value: any) => { |
||||||
|
console.log('onLocalBranchChange', value) |
||||||
|
setLocalBranch(value) |
||||||
|
} |
||||||
|
|
||||||
|
const merge = async () => { |
||||||
|
//gitservice.push(currentRemote, branch || '', remoteBranch, force)
|
||||||
|
} |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('context', context.repositories) |
||||||
|
// map context.repositories to options
|
||||||
|
const localBranches = context.branches && context.branches.length > 0 && context.branches |
||||||
|
.filter(branch => !branch.remote) |
||||||
|
.map(repo => { |
||||||
|
return { value: repo.name, label: repo.name } |
||||||
|
}) |
||||||
|
setLocalBranchOptions(localBranches) |
||||||
|
|
||||||
|
|
||||||
|
}, [context.branches]) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className="btn-group w-100" role="group" aria-label="Basic example"> |
||||||
|
<button type="button" onClick={async () => merge()} className="btn btn-primary mr-1">Merge</button> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
|
||||||
|
<label>Merge from Branch</label> |
||||||
|
<Select |
||||||
|
options={localBranchOptions} |
||||||
|
onChange={(e: any) => e && onLocalBranchChange(e.value)} |
||||||
|
theme={selectTheme} |
||||||
|
styles={selectStyles} |
||||||
|
isClearable={true} |
||||||
|
value={{ value: localBranch, label: localBranch }} |
||||||
|
placeholder="Type to search for a branch..." |
||||||
|
/> |
||||||
|
|
||||||
|
|
||||||
|
</>) |
||||||
|
} |
@ -0,0 +1,146 @@ |
|||||||
|
import React, { useEffect, useState } from "react"; |
||||||
|
import { gitActionsContext } from "../../../state/context"; |
||||||
|
import { gitPluginContext } from "../../gitui"; |
||||||
|
import { selectStyles, selectTheme } from "../../../types/styles"; |
||||||
|
import Select, { Options, OptionsOrGroups } from 'react-select' |
||||||
|
import { setUpstream } from "../../../state/gitpayload"; |
||||||
|
|
||||||
|
export const PushPull = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const [remoteBranch, setRemoteBranch] = useState('') |
||||||
|
const [localBranch, setLocalBranch] = useState('') |
||||||
|
const [localBranchOptions, setLocalBranchOptions] = useState<any>([]); |
||||||
|
const [remoteBranchOptions, setRemoteBranchOptions] = useState<any>([]); |
||||||
|
const [localRemotesOptions, setLocalRemotesOptions] = useState<any>([]); |
||||||
|
const [force, setForce] = useState(false) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
setRemoteBranch(context.currentBranch.name) |
||||||
|
setLocalBranch(context.currentBranch.name) |
||||||
|
if ((!context.upstream || context.upstream === '') && context.currentBranch && context.currentBranch.remote && context.currentBranch.remote.remote) { |
||||||
|
actions.setUpstreamRemote(context.currentBranch.remote.remote) |
||||||
|
} |
||||||
|
}, [context.currentBranch]) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const onRemoteBranchChange = (value: string) => { |
||||||
|
setRemoteBranch(value) |
||||||
|
} |
||||||
|
|
||||||
|
const onLocalBranchChange = (value: any) => { |
||||||
|
console.log('onLocalBranchChange', value) |
||||||
|
setLocalBranch(value) |
||||||
|
} |
||||||
|
|
||||||
|
const onRemoteChange = (value: any) => { |
||||||
|
console.log('onRemoteChange', value) |
||||||
|
actions.setUpstreamRemote(value) |
||||||
|
} |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('UPSTREAM', context.upstream) |
||||||
|
}, [context.upstream]) |
||||||
|
|
||||||
|
const onForceChange = (event: any) => { |
||||||
|
const target = event.target; |
||||||
|
const value = target.checked; |
||||||
|
setForce(value) |
||||||
|
} |
||||||
|
|
||||||
|
const push = async () => { |
||||||
|
//gitservice.push(currentRemote, branch || '', remoteBranch, force)
|
||||||
|
} |
||||||
|
|
||||||
|
const pull = async () => { |
||||||
|
//gitservice.pull(currentRemote, branch || '', remoteBranch)
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('context', context.repositories) |
||||||
|
// map context.repositories to options
|
||||||
|
const localBranches = context.branches && context.branches.length > 0 && context.branches |
||||||
|
.filter(branch => !branch.remote) |
||||||
|
.map(repo => { |
||||||
|
return { value: repo.name, label: repo.name } |
||||||
|
}) |
||||||
|
setLocalBranchOptions(localBranches) |
||||||
|
|
||||||
|
const remoteBranches = context.branches && context.branches.length > 0 && context.branches |
||||||
|
.filter(branch => branch.remote) |
||||||
|
.map(repo => { |
||||||
|
return { value: repo.name, label: repo.name } |
||||||
|
} |
||||||
|
) |
||||||
|
setRemoteBranchOptions(remoteBranches) |
||||||
|
|
||||||
|
}, [context.branches]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('context', context.remotes) |
||||||
|
// map context.repositories to options
|
||||||
|
const options = context.remotes && context.remotes.length > 0 && context.remotes |
||||||
|
.map(repo => { |
||||||
|
return { value: repo.remote, label: repo.remote } |
||||||
|
}) |
||||||
|
|
||||||
|
setLocalRemotesOptions(options) |
||||||
|
|
||||||
|
}, [context.remotes]) |
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className="btn-group w-100" role="group" aria-label="Basic example"> |
||||||
|
<button type="button" onClick={async () => push()} className="btn btn-primary mr-1">Push</button> |
||||||
|
<button type="button" onClick={async () => pull()} className="btn btn-primary">Pull</button> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
|
||||||
|
<label>Local Branch</label> |
||||||
|
<Select |
||||||
|
options={localBranchOptions} |
||||||
|
onChange={(e: any) => e && onLocalBranchChange(e.value)} |
||||||
|
theme={selectTheme} |
||||||
|
styles={selectStyles} |
||||||
|
isClearable={true} |
||||||
|
value={{ value: localBranch, label: localBranch }} |
||||||
|
placeholder="Type to search for a branch..." |
||||||
|
/> |
||||||
|
|
||||||
|
<label>Remote Branch</label> |
||||||
|
<Select |
||||||
|
options={remoteBranchOptions} |
||||||
|
onChange={(e: any) => e && onRemoteBranchChange(e.value)} |
||||||
|
theme={selectTheme} |
||||||
|
styles={selectStyles} |
||||||
|
isClearable={true} |
||||||
|
value={{ value: remoteBranch, label: remoteBranch }} |
||||||
|
placeholder="Type to search for a branch..." |
||||||
|
/> |
||||||
|
|
||||||
|
<label>Upstream</label> |
||||||
|
<Select |
||||||
|
options={localRemotesOptions} |
||||||
|
onChange={(e: any) => e && onRemoteChange(e.value)} |
||||||
|
theme={selectTheme} |
||||||
|
styles={selectStyles} |
||||||
|
isClearable={true} |
||||||
|
value={{ value: context.upstream, label: context.upstream }} |
||||||
|
placeholder="Type to search for a branch..." |
||||||
|
/> |
||||||
|
|
||||||
|
|
||||||
|
<div className="mt-2 remixui_compilerConfig custom-control custom-checkbox"> |
||||||
|
<input checked={force} onChange={e => onForceChange(e)} className="remixui_autocompile custom-control-input" type="checkbox" data-id="compilerContainerAutoCompile" id="forcepush" title="Force Push" /> |
||||||
|
<label className="form-check-label custom-control-label" htmlFor="forcepush">Force push</label> |
||||||
|
</div> |
||||||
|
|
||||||
|
</>) |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
import React, { useEffect } from "react" |
||||||
|
import { useState } from "react" |
||||||
|
import { gitActionsContext } from "../../state/context" |
||||||
|
import { gitPluginContext } from "../gitui" |
||||||
|
import { faCheck } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
|
||||||
|
export const CommitMessage = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
|
||||||
|
const [message, setMessage] = useState({ value: '' }) |
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { |
||||||
|
setMessage({ value: e.currentTarget.value }) |
||||||
|
} |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (context.fileStatusResult) { |
||||||
|
console.log(context.staged.length + ' staged') |
||||||
|
} |
||||||
|
}, [context.fileStatusResult]) |
||||||
|
|
||||||
|
const commit = async() => { |
||||||
|
if(context.staged.length === 0 && context.allchangesnotstaged.length == 0) return |
||||||
|
if(context.staged.length === 0) |
||||||
|
await actions.addall() |
||||||
|
await actions.commit(message.value) |
||||||
|
} |
||||||
|
|
||||||
|
const commitAllowed = () => { |
||||||
|
return context.canCommit === false || message.value === "" || ( context.staged.length === 0 && context.allchangesnotstaged.length == 0 ) |
||||||
|
} |
||||||
|
|
||||||
|
const commitMessagePlaceholder = () => { |
||||||
|
if(context.currentBranch === undefined || context.currentBranch.name === "") |
||||||
|
return `message` |
||||||
|
return `message ( commit on ${context.currentBranch.name} )` |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className="form-group"> |
||||||
|
<input placeholder={commitMessagePlaceholder()} data-id='commitMessage' className="form-control" type="text" onChange={handleChange} value={message.value} /> |
||||||
|
</div> |
||||||
|
{context.canCommit ? <></> : <div className='alert alert-warning'>Cannot commit in detached state! Create a new branch and check it out first or checkout main.<br></br></div>} |
||||||
|
<button data-id='commitButton' className="btn btn-primary w-100" disabled={commitAllowed()} onClick={async () => await commit()} > |
||||||
|
<FontAwesomeIcon icon={faCheck} className="mr-1" /> |
||||||
|
Commit |
||||||
|
</button> |
||||||
|
<hr></hr> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
import { checkout, ReadCommitResult } from "isomorphic-git"; |
||||||
|
import React from "react"; |
||||||
|
import { gitActionsContext } from "../../state/context"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
import { CommitDetails } from "./commits/commitdetails"; |
||||||
|
import { CommitSummary } from "./commits/commitsummary"; |
||||||
|
|
||||||
|
|
||||||
|
export const Commits = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
|
||||||
|
const checkout = async (oid: string) => { |
||||||
|
try { |
||||||
|
//await ModalRef.current?.show();
|
||||||
|
actions.checkout({ ref: oid }) |
||||||
|
//Utils.log("yes");
|
||||||
|
} catch (e) { |
||||||
|
//Utils.log("no");
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
{context.commits && context.commits.length ? |
||||||
|
<div> |
||||||
|
|
||||||
|
<div className="pt-1"> |
||||||
|
{context.commits && context.commits.map((commit, index) => { |
||||||
|
return ( |
||||||
|
|
||||||
|
<CommitDetails key={index} checkout={checkout} commit={commit}></CommitDetails> |
||||||
|
|
||||||
|
); |
||||||
|
})} |
||||||
|
|
||||||
|
<div |
||||||
|
onClick={async () => await checkout("main")} |
||||||
|
className="btn btn-primary btn-sm checkout-btn mt-2 d-none" |
||||||
|
data-oid="main" |
||||||
|
> |
||||||
|
git checkout main |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
: <div className="text-muted">No commits</div>} |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
import { ReadCommitResult } from "isomorphic-git" |
||||||
|
import React, { useEffect, useState } from "react"; |
||||||
|
import { Accordion } from "react-bootstrap"; |
||||||
|
import { CommitDetailsNavigation } from "../../navigation/commitdetails"; |
||||||
|
import { gitActionsContext } from "../../../state/context"; |
||||||
|
import { gitPluginContext } from "../../gitui"; |
||||||
|
import { CommitDetailsItems } from "./commitdetailsitem"; |
||||||
|
|
||||||
|
export interface CommitDetailsProps { |
||||||
|
commit: ReadCommitResult; |
||||||
|
checkout: (oid: string) => void; |
||||||
|
} |
||||||
|
|
||||||
|
export const CommitDetails = (props: CommitDetailsProps) => { |
||||||
|
const { commit, checkout } = props; |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const [activePanel, setActivePanel] = useState<string>(""); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (activePanel === "0") { |
||||||
|
console.log(commit.oid, commit.commit.parent) |
||||||
|
actions.getCommitChanges(commit.oid, commit.commit.parent[0]) |
||||||
|
} |
||||||
|
}, [activePanel]) |
||||||
|
|
||||||
|
return (<Accordion activeKey={activePanel} defaultActiveKey=""> |
||||||
|
<CommitDetailsNavigation commit={commit} checkout={checkout} eventKey="0" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
<Accordion.Collapse className="pl-2 border-left ml-1" eventKey="0"> |
||||||
|
<> |
||||||
|
{context.commitChanges && context.commitChanges.filter( |
||||||
|
(change) => change.hashModified === commit.oid && change.hashOriginal === commit.commit.parent[0] |
||||||
|
).map((change, index) => { |
||||||
|
return(<CommitDetailsItems key={index} commitChange={change}></CommitDetailsItems>) |
||||||
|
})} |
||||||
|
|
||||||
|
</> |
||||||
|
</Accordion.Collapse> |
||||||
|
</Accordion>) |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
import { commitChange } from "../../../types"; |
||||||
|
import React from "react"; |
||||||
|
import path from "path"; |
||||||
|
import { gitActionsContext, pluginActionsContext } from "../../../state/context"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import { faGlobe } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
|
||||||
|
export interface CCommitDetailsItemsProps { |
||||||
|
commitChange: commitChange; |
||||||
|
} |
||||||
|
|
||||||
|
export const CommitDetailsItems = (props: CCommitDetailsItemsProps) => { |
||||||
|
const { commitChange } = props; |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const pluginActions = React.useContext(pluginActionsContext) |
||||||
|
|
||||||
|
const openChanges = async (change: commitChange) => { |
||||||
|
console.log("open changes", change); |
||||||
|
await actions.diff(change) |
||||||
|
console.log("open changes", change); |
||||||
|
await pluginActions.openDiff(change) |
||||||
|
} |
||||||
|
|
||||||
|
function FunctionStatusIcons() { |
||||||
|
const status = commitChange.type |
||||||
|
return (<> |
||||||
|
|
||||||
|
{status && status.indexOf("modified") === -1 ? <></> : <span>M</span>} |
||||||
|
{status && status.indexOf("deleted") === -1 ? <></> : <span>D</span>} |
||||||
|
{status && status.indexOf("added") === -1 ? <></> : <span>A</span>} |
||||||
|
|
||||||
|
|
||||||
|
</>) |
||||||
|
} |
||||||
|
return (<> |
||||||
|
<div className="d-flex w-100 d-flex flex-row commitdetailsitem"> |
||||||
|
<div className='pointer gitfile long-and-truncated' onClick={async () => await openChanges(commitChange)}> |
||||||
|
<span className='font-weight-bold long-and-truncated'>{path.basename(commitChange.path)}</span> |
||||||
|
<div className='text-secondary long-and-truncated'> {commitChange.path}</div> |
||||||
|
</div> |
||||||
|
<div className="d-flex align-items-end"> |
||||||
|
<FontAwesomeIcon icon={faGlobe} className="mr-1 align-self-center" /> |
||||||
|
<FunctionStatusIcons></FunctionStatusIcons> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</>) |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
import { ReadCommitResult } from "isomorphic-git" |
||||||
|
import { default as dateFormat } from "dateformat"; |
||||||
|
import React from "react"; |
||||||
|
|
||||||
|
export interface CommitSummaryProps { |
||||||
|
commit: ReadCommitResult; |
||||||
|
checkout: (oid: string) => void; |
||||||
|
} |
||||||
|
|
||||||
|
export const CommitSummary = (props: CommitSummaryProps) => { |
||||||
|
const { commit, checkout } = props; |
||||||
|
|
||||||
|
const getDate = (commit: ReadCommitResult) => { |
||||||
|
const timestamp = commit.commit.author.timestamp; |
||||||
|
|
||||||
|
if (timestamp) { |
||||||
|
// calculate the difference between the current time and the commit time in days or hours or minutes
|
||||||
|
const diff = Math.floor((Date.now() - timestamp * 1000) / 1000 / 60 / 60 / 24); |
||||||
|
|
||||||
|
if (diff == 0) { |
||||||
|
return "today at " + dateFormat(timestamp * 1000, "HH:MM"); |
||||||
|
} else |
||||||
|
|
||||||
|
if (diff < 1) { |
||||||
|
// return how many hours ago
|
||||||
|
return `${Math.floor(diff * 24)} hour(s) ago`; |
||||||
|
} |
||||||
|
|
||||||
|
if (diff < 7) { |
||||||
|
// return how many days ago
|
||||||
|
return `${diff} day(s) ago`; |
||||||
|
} |
||||||
|
if (diff < 365) { |
||||||
|
return dateFormat(timestamp * 1000, "mmm dd"); |
||||||
|
} |
||||||
|
return dateFormat(timestamp * 1000, "mmm dd yyyy"); |
||||||
|
} |
||||||
|
return ""; |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className="long-and-truncated ml-2"> |
||||||
|
{commit.commit.message} |
||||||
|
</div> |
||||||
|
{commit.commit.author.name || ""} |
||||||
|
<span className="ml-1">{getDate(commit)}</span> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,143 @@ |
|||||||
|
import React, { useEffect } from "react"; |
||||||
|
import { gitActionsContext, pluginActionsContext } from "../../state/context"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
import axios from "axios"; |
||||||
|
import { CopyToClipboard } from "@remix-ui/clipboard"; |
||||||
|
import { Card } from "react-bootstrap"; |
||||||
|
|
||||||
|
|
||||||
|
export const GitHubAuth = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const pluginActions = React.useContext(pluginActionsContext) |
||||||
|
const [gitHubResponse, setGitHubResponse] = React.useState<any>(null) |
||||||
|
const [authorized, setAuthorized] = React.useState<boolean>(false) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const getDeviceCodeFromGitHub = async () => { |
||||||
|
|
||||||
|
setAuthorized(false) |
||||||
|
// Send a POST request
|
||||||
|
const response = await axios({ |
||||||
|
method: 'post', |
||||||
|
url: 'http://0.0.0.0:3000/github.com/login/device/code', |
||||||
|
data: { |
||||||
|
client_id: 'Iv1.12fc02c69c512462' |
||||||
|
}, |
||||||
|
headers: { |
||||||
|
'Content-Type': 'application/json', |
||||||
|
'Accept': 'application/json' |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
// convert response to json
|
||||||
|
const githubrespone = await response.data; |
||||||
|
console.log('json', githubrespone) |
||||||
|
|
||||||
|
setGitHubResponse(githubrespone) |
||||||
|
} |
||||||
|
|
||||||
|
const connectApp = async () => { |
||||||
|
// poll https://github.com/login/oauth/access_token
|
||||||
|
const accestokenresponse = await axios({ |
||||||
|
method: 'post', |
||||||
|
url: 'http://0.0.0.0:3000/github.com/login/oauth/access_token', |
||||||
|
data: { |
||||||
|
client_id: 'Iv1.12fc02c69c512462', |
||||||
|
device_code: gitHubResponse.device_code, |
||||||
|
grant_type: 'urn:ietf:params:oauth:grant-type:device_code' |
||||||
|
}, |
||||||
|
headers: { |
||||||
|
'Content-Type': 'application/json', |
||||||
|
'Accept': 'application/json' |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
// convert response to json
|
||||||
|
const response = await accestokenresponse.data; |
||||||
|
console.log('json2', response) |
||||||
|
|
||||||
|
if (response.error) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
if (response.access_token) { |
||||||
|
setAuthorized(true) |
||||||
|
await pluginActions.saveToken(response.access_token) |
||||||
|
await actions.getGitHubUser() |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
const disconnect = async () => { |
||||||
|
setAuthorized(false) |
||||||
|
setGitHubResponse(null) |
||||||
|
} |
||||||
|
|
||||||
|
const checkConnection = async () => { |
||||||
|
//await actions.getGitHubUser()
|
||||||
|
} |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
|
||||||
|
},[]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('context.rateLimit', context.rateLimit) |
||||||
|
}, [context.rateLimit]) |
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
{(context.gitHubUser && context.gitHubUser.login) ? null : |
||||||
|
<button className='btn btn-primary mt-1 w-100' onClick={async () => { |
||||||
|
getDeviceCodeFromGitHub(); |
||||||
|
}}>Login in with github</button> |
||||||
|
} |
||||||
|
{gitHubResponse && !authorized && |
||||||
|
<div className="pt-2"> |
||||||
|
|
||||||
|
Step 1: Copy this code: |
||||||
|
<div className="input-group text-secondary mb-0 h6"> |
||||||
|
<input disabled type="text" className="form-control" value={gitHubResponse.user_code} /> |
||||||
|
<div className="input-group-append"> |
||||||
|
<CopyToClipboard content={gitHubResponse.user_code} data-id='copyToClipboardCopyIcon' className='far fa-copy ml-1 p-2 mt-1' direction={"top"} /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<br></br> |
||||||
|
Step 2: Authorize the app here |
||||||
|
<br></br><a target="_blank" href={gitHubResponse.verification_uri}>{gitHubResponse.verification_uri}</a> |
||||||
|
<br /><br></br> |
||||||
|
Step 3: When you are done, click on the button below: |
||||||
|
<button className='btn btn-primary mt-1 w-100' onClick={async () => { |
||||||
|
connectApp() |
||||||
|
}}>Connect</button> |
||||||
|
</div> |
||||||
|
} |
||||||
|
{ |
||||||
|
(context.gitHubUser && context.gitHubUser.login) ? |
||||||
|
<div className="pt-2"> |
||||||
|
<button className='btn btn-primary mt-1 w-100' onClick={async () => { |
||||||
|
disconnect() |
||||||
|
}}>Disconnect</button> |
||||||
|
</div>: null |
||||||
|
} |
||||||
|
{ |
||||||
|
(context.gitHubUser && context.gitHubUser.login) ? |
||||||
|
<div className="pt-2"> |
||||||
|
<Card> |
||||||
|
<Card.Body> |
||||||
|
<Card.Title>Connected as {context.gitHubUser.login}</Card.Title> |
||||||
|
<Card.Text> |
||||||
|
<img src={context.gitHubUser.avatar_url} className="w-100" /> |
||||||
|
<a target="_blank" href={context.gitHubUser.html_url}>{context.gitHubUser.html_url}</a> |
||||||
|
</Card.Text> |
||||||
|
</Card.Body> |
||||||
|
</Card> |
||||||
|
</div>: null |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
</>) |
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
import { checkout, clone, ReadCommitResult } from "isomorphic-git"; |
||||||
|
import React from "react"; |
||||||
|
import { gitActionsContext } from "../../state/context"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
import { CustomTooltip } from "@remix-ui/helper"; |
||||||
|
|
||||||
|
import { useIntl, FormattedMessage } from "react-intl"; |
||||||
|
import { CopyToClipboard } from "@remix-ui/clipboard"; |
||||||
|
import { FormControl, InputGroup } from "react-bootstrap"; |
||||||
|
|
||||||
|
|
||||||
|
export const GitHubCredentials = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const [githubToken, setGithubToken] = React.useState('') |
||||||
|
const [githubUsername, setGithubUsername] = React.useState('') |
||||||
|
const [githubEmail, setGithubEmail] = React.useState('') |
||||||
|
const intl = useIntl() |
||||||
|
|
||||||
|
const gitAccessTokenLink = 'https://github.com/settings/tokens/new?scopes=gist,repo&description=Remix%20IDE%20Token' |
||||||
|
|
||||||
|
function handleChangeTokenState(e: string): void { |
||||||
|
throw new Error("Function not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
function handleChangeUserNameState(e: string): void { |
||||||
|
throw new Error("Function not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
function handleChangeEmailState(e: string): void { |
||||||
|
throw new Error("Function not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
function saveGithubToken(): void { |
||||||
|
throw new Error("Function not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
function removeToken(): void { |
||||||
|
throw new Error("Function not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div className="input-group text-secondary mb-0 h6"> |
||||||
|
<input type="text" className="form-control" name='githubToken' /> |
||||||
|
<div className="input-group-append"> |
||||||
|
<CopyToClipboard content={''} data-id='copyToClipboardCopyIcon' className='far fa-copy ml-1 p-2 mt-1' direction={"top"} /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<input name='githubUsername' onChange={e => handleChangeUserNameState(e.target.value)} value={githubUsername} className="form-control mb-2" placeholder="GitHub username" type="text" id="githubUsername" /> |
||||||
|
<input name='githubEmail' onChange={e => handleChangeEmailState(e.target.value)} value={githubEmail} className="form-control mb-1" placeholder="GitHub email" type="text" id="githubEmail" /> |
||||||
|
<hr /> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
import React, { useEffect } from "react"; |
||||||
|
import { gitActionsContext } from "../../state/context"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
import { Remoteselect } from "./remoteselect"; |
||||||
|
import { RemotesImport } from "./remotesimport"; |
||||||
|
|
||||||
|
export const Remotes = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const [remoteName, setRemoteName] = React.useState<string>('') |
||||||
|
const [url, setUrl] = React.useState<string>('') |
||||||
|
|
||||||
|
const onRemoteNameChange = (value: string) => { |
||||||
|
setRemoteName(value) |
||||||
|
} |
||||||
|
const onUrlChange = (value: string) => { |
||||||
|
setUrl(value) |
||||||
|
} |
||||||
|
|
||||||
|
const addRemote = async () => { |
||||||
|
//await gitservice.addRemote(remoteName, url)
|
||||||
|
//setCurrentRemote(remoteName)
|
||||||
|
//await gitservice.getRemotes()
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('SHOW REMOTES', context.remotes) |
||||||
|
}, [context.remotes]) |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
|
||||||
|
|
||||||
|
{context.remotes && context.remotes.length ? |
||||||
|
<> |
||||||
|
|
||||||
|
{context.remotes && context.remotes.map((remote, index) => { |
||||||
|
|
||||||
|
return ( |
||||||
|
<Remoteselect key={index} remote={remote}></Remoteselect> |
||||||
|
); |
||||||
|
})} |
||||||
|
</> : <>No remotes</>} |
||||||
|
<hr></hr> |
||||||
|
|
||||||
|
<input placeholder="remote name" name='remotename' onChange={e => onRemoteNameChange(e.target.value)} value={remoteName} className="form-control mb-2" type="text" id="remotename" /> |
||||||
|
<input placeholder="remote url" name='remoteurl' onChange={e => onUrlChange(e.target.value)} value={url} className="form-control" type="text" id="remoteurl" /> |
||||||
|
|
||||||
|
<button className='btn btn-primary mt-1 w-100' onClick={async () => { |
||||||
|
addRemote(); |
||||||
|
}}>add remote</button> |
||||||
|
<hr /> |
||||||
|
<RemotesImport /> |
||||||
|
<hr /> |
||||||
|
|
||||||
|
</>) |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
import { branch, checkout, ReadCommitResult } from "isomorphic-git"; |
||||||
|
import React, { useEffect, useState } from "react"; |
||||||
|
import { gitActionsContext } from "../../state/context"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
import { default as dateFormat } from "dateformat"; |
||||||
|
import { RemotesDetailsNavigation } from "../navigation/remotesdetails"; |
||||||
|
import { Accordion } from "react-bootstrap"; |
||||||
|
import { remote } from "../../types"; |
||||||
|
import { BranchDetails } from "./branches/branchedetails"; |
||||||
|
|
||||||
|
export interface RemoteSelectProps { |
||||||
|
remote: remote |
||||||
|
} |
||||||
|
|
||||||
|
export const Remoteselect = (props: RemoteSelectProps) => { |
||||||
|
const { remote } = props; |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const [activePanel, setActivePanel] = useState<string>(""); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (activePanel === "0") { |
||||||
|
console.log('fetching', remote) |
||||||
|
} |
||||||
|
}, [activePanel]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('remote branches', context.branches) |
||||||
|
}, [context.branches]) |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Accordion activeKey={activePanel} defaultActiveKey=""> |
||||||
|
<RemotesDetailsNavigation callback={setActivePanel} eventKey="0" activePanel={activePanel} remote={remote} /> |
||||||
|
<Accordion.Collapse className="pl-2 border-left ml-1" eventKey="0"> |
||||||
|
<> |
||||||
|
{context.branches && context.branches.filter((branch, index) => branch.remote && branch.remote.remote === remote.remote ).map((branch, index) => { |
||||||
|
return ( |
||||||
|
<BranchDetails key={index} branch={branch}></BranchDetails> |
||||||
|
); |
||||||
|
})}</> |
||||||
|
|
||||||
|
</Accordion.Collapse> |
||||||
|
</Accordion> |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
import React, { useEffect, useState } from "react"; |
||||||
|
import { Alert, Button } from "react-bootstrap"; |
||||||
|
import { gitActionsContext } from "../../state/context"; |
||||||
|
import { repository } from "../../types"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
import Select from 'react-select' |
||||||
|
import { selectStyles, selectTheme } from "../../types/styles"; |
||||||
|
|
||||||
|
|
||||||
|
export const RemotesImport = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const [repo, setRepo] = useState<repository>(null); |
||||||
|
const [repoOtions, setRepoOptions] = useState<any>([]); |
||||||
|
const [loading, setLoading] = useState(false) |
||||||
|
const [show, setShow] = useState(false) |
||||||
|
const [remoteName, setRemoteName] = useState('') |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('context', context.repositories) |
||||||
|
// map context.repositories to options
|
||||||
|
const options = context.repositories && context.repositories.length > 0 && context.repositories.map(repo => { |
||||||
|
return { value: repo.id, label: repo.full_name } |
||||||
|
}) |
||||||
|
setLoading(false) |
||||||
|
setRepoOptions(options) |
||||||
|
|
||||||
|
}, [context.repositories]) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const fetchRepositories = async () => { |
||||||
|
try { |
||||||
|
setShow(true) |
||||||
|
setLoading(true) |
||||||
|
setRepoOptions([]) |
||||||
|
console.log(await actions.repositories()) |
||||||
|
} catch (e) { |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const selectRepo = async (value: number | string) => { |
||||||
|
// find repo
|
||||||
|
console.log('setRepo', value, context.repositories) |
||||||
|
|
||||||
|
const repo = context.repositories.find(repo => { |
||||||
|
return repo.id.toString() === value.toString() |
||||||
|
}) |
||||||
|
console.log('repo', repo) |
||||||
|
if (repo) { |
||||||
|
setRepo(repo) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const addRemote = async () => { |
||||||
|
try { |
||||||
|
|
||||||
|
} catch (e) { |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
const onRemoteNameChange = (value: string) => { |
||||||
|
setRemoteName(value) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Button onClick={fetchRepositories} className="w-100 mt-1"> |
||||||
|
<i className="fab fa-github mr-1"></i>Fetch Remotes from GitHub |
||||||
|
</Button> |
||||||
|
{show ? |
||||||
|
<Select |
||||||
|
options={repoOtions} |
||||||
|
className="mt-1" |
||||||
|
onChange={(e: any) => e && selectRepo(e.value)} |
||||||
|
theme={selectTheme} |
||||||
|
styles={selectStyles} |
||||||
|
isClearable={true} |
||||||
|
placeholder="Type to search for a repository..." |
||||||
|
isLoading={loading} |
||||||
|
/> : null} |
||||||
|
|
||||||
|
{repo ? |
||||||
|
<input placeholder="remote name" name='remotename' onChange={e => onRemoteNameChange(e.target.value)} value={remoteName} className="form-control mb-2" type="text" id="remotename" /> |
||||||
|
: null} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{repo && remoteName ? |
||||||
|
<button data-id='clonebtn' className='btn btn-primary mt-1 w-100' onClick={async () => { |
||||||
|
await addRemote() |
||||||
|
}}>add {remoteName}:{repo.full_name}</button> : null} |
||||||
|
|
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,131 @@ |
|||||||
|
import React, { useEffect, useState } from "react"; |
||||||
|
import { Alert, Button } from "react-bootstrap"; |
||||||
|
import { gitActionsContext } from "../../state/context"; |
||||||
|
import { repository } from "../../types"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
import Select from 'react-select' |
||||||
|
import { selectStyles, selectTheme } from "../../types/styles"; |
||||||
|
|
||||||
|
interface RepositoriesProps { |
||||||
|
cloneDepth?: number |
||||||
|
cloneAllBranches?: boolean |
||||||
|
} |
||||||
|
|
||||||
|
export const Repositories = (props: RepositoriesProps) => { |
||||||
|
const { cloneDepth, cloneAllBranches } = props |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const [branch, setBranch] = useState({ name: "" }); |
||||||
|
const [repo, setRepo] = useState<repository>(null); |
||||||
|
const [repoOtions, setRepoOptions] = useState<any>([]); |
||||||
|
const [branchOptions, setBranchOptions] = useState<any>([]); |
||||||
|
const [loading, setLoading] = useState(false) |
||||||
|
const [show, setShow] = useState(false) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('context', context.repositories) |
||||||
|
// map context.repositories to options
|
||||||
|
const options = context.repositories && context.repositories.length > 0 && context.repositories.map(repo => { |
||||||
|
return { value: repo.id, label: repo.full_name } |
||||||
|
}) |
||||||
|
setLoading(false) |
||||||
|
setRepoOptions(options) |
||||||
|
|
||||||
|
}, [context.repositories]) |
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
// map context.branches to options
|
||||||
|
const options = context.remoteBranches && context.remoteBranches.length > 0 && context.remoteBranches.map(branch => { |
||||||
|
return { value: branch.name, label: branch.name } |
||||||
|
} |
||||||
|
) |
||||||
|
setBranchOptions(options) |
||||||
|
}, [context.remoteBranches]) |
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('show', show) |
||||||
|
},[show]) |
||||||
|
|
||||||
|
const fetchRepositories = async () => { |
||||||
|
try { |
||||||
|
setShow(true) |
||||||
|
setLoading(true) |
||||||
|
setRepoOptions([]) |
||||||
|
setBranchOptions([]) |
||||||
|
console.log(await actions.repositories()) |
||||||
|
} catch (e) { |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const selectRepo = async (value: number | string) => { |
||||||
|
// find repo
|
||||||
|
console.log('setRepo', value, context.repositories) |
||||||
|
|
||||||
|
const repo = context.repositories.find(repo => { |
||||||
|
return repo.id.toString() === value.toString() |
||||||
|
}) |
||||||
|
console.log('repo', repo) |
||||||
|
if (repo) { |
||||||
|
setBranchOptions([]) |
||||||
|
setBranch({ name: "" }) |
||||||
|
setRepo(repo) |
||||||
|
await actions.remoteBranches(repo.owner.login, repo.name) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const selectRemoteBranch = async (value: number | string) => { |
||||||
|
console.log('setRemoteBranch', value) |
||||||
|
setBranch({ name: value.toString() }) |
||||||
|
} |
||||||
|
|
||||||
|
const clone = async () => { |
||||||
|
try { |
||||||
|
console.log('clone', repo, branch) |
||||||
|
actions.clone(repo.html_url, branch.name, cloneDepth, !cloneAllBranches) |
||||||
|
} catch (e) { |
||||||
|
// do nothing
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Button onClick={fetchRepositories} className="w-100 mt-1"> |
||||||
|
<i className="fab fa-github mr-1"></i>Fetch Repositories from GitHub |
||||||
|
</Button> |
||||||
|
{show? |
||||||
|
<Select |
||||||
|
options={repoOtions} |
||||||
|
className="mt-1" |
||||||
|
onChange={(e: any) => e && selectRepo(e.value)} |
||||||
|
theme={selectTheme} |
||||||
|
styles={selectStyles} |
||||||
|
isClearable={true} |
||||||
|
placeholder="Type to search for a repository..." |
||||||
|
isLoading={loading} |
||||||
|
/>:null} |
||||||
|
|
||||||
|
{branchOptions && branchOptions.length ? |
||||||
|
<Select |
||||||
|
options={branchOptions} |
||||||
|
className="mt-1" |
||||||
|
onChange={(e: any) => e && selectRemoteBranch(e.value)} |
||||||
|
theme={selectTheme} |
||||||
|
styles={selectStyles} |
||||||
|
isClearable={true} |
||||||
|
placeholder="Type to search for a branch..." |
||||||
|
/> : null} |
||||||
|
|
||||||
|
{repo && branch.name && branch.name !== '0' ? |
||||||
|
<button data-id='clonebtn' className='btn btn-primary mt-1 w-100' onClick={async () => { |
||||||
|
await clone() |
||||||
|
}}>clone {repo.full_name}:{branch.name}</button> : null} |
||||||
|
|
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,52 @@ |
|||||||
|
import { checkout, clone, ReadCommitResult } from "isomorphic-git"; |
||||||
|
import React from "react"; |
||||||
|
import { gitActionsContext } from "../../state/context"; |
||||||
|
import { gitPluginContext } from "../gitui"; |
||||||
|
import { CustomTooltip } from "@remix-ui/helper"; |
||||||
|
|
||||||
|
import { useIntl, FormattedMessage } from "react-intl"; |
||||||
|
import { CopyToClipboard } from "@remix-ui/clipboard"; |
||||||
|
import { FormControl, InputGroup } from "react-bootstrap"; |
||||||
|
|
||||||
|
|
||||||
|
export const Settings = () => { |
||||||
|
|
||||||
|
const [githubToken, setGithubToken] = React.useState('') |
||||||
|
const [githubUsername, setGithubUsername] = React.useState('') |
||||||
|
const [githubEmail, setGithubEmail] = React.useState('') |
||||||
|
const intl = useIntl() |
||||||
|
|
||||||
|
const gitAccessTokenLink = 'https://github.com/settings/tokens/new?scopes=gist,repo&description=Remix%20IDE%20Token' |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function handleChangeTokenState(e: string): void { |
||||||
|
throw new Error("Function not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
function handleChangeUserNameState(e: string): void { |
||||||
|
throw new Error("Function not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
function handleChangeEmailState(e: string): void { |
||||||
|
throw new Error("Function not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
function saveGithubToken(): void { |
||||||
|
throw new Error("Function not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
function removeToken(): void { |
||||||
|
throw new Error("Function not implemented."); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<input name='githubToken' onChange={e => handleChangeUserNameState(e.target.value)} value={githubToken} className="form-control mb-2" placeholder="GitHub token" type="text" id="githubToken" /> |
||||||
|
<input name='githubUsername' onChange={e => handleChangeUserNameState(e.target.value)} value={githubUsername} className="form-control mb-2" placeholder="GitHub username" type="text" id="githubUsername" /> |
||||||
|
<input name='githubEmail' onChange={e => handleChangeEmailState(e.target.value)} value={githubEmail} className="form-control mb-1" placeholder="GitHub email" type="text" id="githubEmail" /> |
||||||
|
<hr /> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
import { ReadCommitResult } from "isomorphic-git" |
||||||
|
import React, { useEffect, useState } from "react"; |
||||||
|
import { Accordion } from "react-bootstrap"; |
||||||
|
import { gitActionsContext } from "../../../state/context"; |
||||||
|
import { gitPluginContext } from "../../gitui"; |
||||||
|
import { sourceControlGroup } from "../../../types"; |
||||||
|
import { SourceControlGroupNavigation } from "../../navigation/sourcecontrolgroup"; |
||||||
|
import { SourceControlItem } from "./sourcecontrolitem"; |
||||||
|
|
||||||
|
export interface SourceControGroupProps { |
||||||
|
group: sourceControlGroup |
||||||
|
} |
||||||
|
|
||||||
|
export const SourceControGroup = (props: SourceControGroupProps) => { |
||||||
|
const { group } = props; |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const [activePanel, setActivePanel] = useState<string>("0"); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (activePanel === "0") { |
||||||
|
} |
||||||
|
}, [activePanel]) |
||||||
|
|
||||||
|
return (<> |
||||||
|
{group.group.length > 0 ? |
||||||
|
<Accordion activeKey={activePanel} defaultActiveKey=""> |
||||||
|
<SourceControlGroupNavigation group={group} eventKey="0" activePanel={activePanel} callback={setActivePanel} /> |
||||||
|
<Accordion.Collapse className="pl-2 border-left ml-1" eventKey="0"> |
||||||
|
<> |
||||||
|
{group.group.map((file, index) => { |
||||||
|
return (<SourceControlItem key={index} group={group} file={file}></SourceControlItem>) |
||||||
|
})} |
||||||
|
</> |
||||||
|
</Accordion.Collapse> |
||||||
|
</Accordion> : <></>} |
||||||
|
</>) |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
import { commitChange, fileStatusResult, sourceControlGroup } from "../../../types"; |
||||||
|
import React from "react"; |
||||||
|
import path from "path"; |
||||||
|
import { gitActionsContext, pluginActionsContext } from "../../../state/context"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import { faGlobe } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
import { SourceControlItemButtons } from "./sourcontrolitembuttons"; |
||||||
|
import { removeSlash } from "../../../utils"; |
||||||
|
|
||||||
|
export interface SourceControlItemProps { |
||||||
|
file: fileStatusResult; |
||||||
|
group: sourceControlGroup; |
||||||
|
} |
||||||
|
|
||||||
|
export const SourceControlItem = (props: SourceControlItemProps) => { |
||||||
|
const { file, group } = props; |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const pluginActions = React.useContext(pluginActionsContext) |
||||||
|
|
||||||
|
async function fileClick(file: fileStatusResult) { |
||||||
|
console.log(file) |
||||||
|
//let status = fileservice.getFileStatusForFile(file.filename || "");
|
||||||
|
if (file.statusNames && file.statusNames.indexOf("modified") !== -1) { |
||||||
|
const headHash = await actions.resolveRef("HEAD") |
||||||
|
const change: commitChange = { |
||||||
|
path: removeSlash(file.filename), |
||||||
|
type: "modified", |
||||||
|
hashOriginal: headHash, |
||||||
|
hashModified: '', |
||||||
|
readonly: false, |
||||||
|
} |
||||||
|
await actions.diff(change) |
||||||
|
await pluginActions.openDiff(change) |
||||||
|
console.log("diff", change) |
||||||
|
|
||||||
|
} else { |
||||||
|
await pluginActions.openFile(file.filename) |
||||||
|
//await client.call('fileManager', 'open', file.filename)
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function FunctionStatusIcons() { |
||||||
|
|
||||||
|
const status = file.statusNames |
||||||
|
|
||||||
|
return (<> |
||||||
|
|
||||||
|
{status && status.indexOf("modified") === -1 ? <></> : <div>M</div>} |
||||||
|
{status && status.indexOf("deleted") === -1 ? <></> : <span>D</span>} |
||||||
|
{status && status.indexOf("added") === -1 ? <></> : <span>A</span>} |
||||||
|
{status && status.indexOf("untracked") === -1 ? <></> : <span>U</span>} |
||||||
|
</>) |
||||||
|
|
||||||
|
} |
||||||
|
return (<> |
||||||
|
<div className="d-flex w-100 d-flex flex-row align-items-center"> |
||||||
|
<div className='pointer gitfile long-and-truncated' onClick={async () => await fileClick(file)}> |
||||||
|
<span className='font-weight-bold long-and-truncated'>{path.basename(file.filename)}</span> |
||||||
|
<div className='text-secondary long-and-truncated'> {file.filename}</div> |
||||||
|
</div> |
||||||
|
<div className="d-flex align-items-center ml-1"> |
||||||
|
<SourceControlItemButtons group={group} file={file}></SourceControlItemButtons> |
||||||
|
<FunctionStatusIcons></FunctionStatusIcons> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</>) |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
import { commitChange, fileStatusResult, sourceControlGroup } from "../../../types"; |
||||||
|
import React from "react"; |
||||||
|
import path from "path"; |
||||||
|
import { gitActionsContext, pluginActionsContext } from "../../../state/context"; |
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
||||||
|
import { faGlobe, faMinus, faPlus, faUndo } from "@fortawesome/free-solid-svg-icons"; |
||||||
|
|
||||||
|
export interface SourceControlItemButtonsProps { |
||||||
|
file: fileStatusResult, |
||||||
|
group: sourceControlGroup |
||||||
|
} |
||||||
|
|
||||||
|
export const SourceControlItemButtons = (props: SourceControlItemButtonsProps) => { |
||||||
|
const { file, group } = props; |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const pluginActions = React.useContext(pluginActionsContext) |
||||||
|
|
||||||
|
function RenderButtons() { |
||||||
|
const status = file.statusNames |
||||||
|
|
||||||
|
if (group.name === 'Staged') { |
||||||
|
return <> |
||||||
|
|
||||||
|
{status && status.indexOf("modified") === -1 ? <></> : <button onClick={async () => await actions.checkoutfile(file.filename)} className='btn btn-sm btn-secondary mr-1 '><FontAwesomeIcon icon={faUndo} className="" /></button>} |
||||||
|
{status && status.indexOf("deleted") === -1 ? <></> : <button onClick={async () => await actions.checkoutfile(file.filename)} className='btn btn-sm btn-secondary mr-1 '><FontAwesomeIcon icon={faUndo} className="" /></button>} |
||||||
|
{status && status.indexOf("deleted") !== -1 ? <></> : <button data-id={`unStage${group.name}${path.basename(file.filename)}`} onClick={async () => await actions.rm(file.filename)} className='btn btn-sm btn-secondary mr-1 '><FontAwesomeIcon icon={faMinus} className="" /></button>} |
||||||
|
|
||||||
|
</> |
||||||
|
} |
||||||
|
if (group.name === 'Changes') { |
||||||
|
return <> |
||||||
|
|
||||||
|
{status && status.indexOf("deleted") === -1 ? <></> : <><button onClick={async () => await actions.checkoutfile(file.filename)} data-id={`undo${group.name}${path.basename(file.filename)}`} className='btn btn-sm btn-secondary mr-1 '><FontAwesomeIcon icon={faUndo} className="" /></button><button data-id={`addToGit${group.name}${path.basename(file.filename)}`} onClick={async () => await actions.rm(file.filename)} className='btn btn-sm btn-secondary mr-1 '><FontAwesomeIcon icon={faPlus} className="" /></button></>} |
||||||
|
{status && status.indexOf("modified") === -1 ? <></> : <button onClick={async () => await actions.checkoutfile(file.filename)} data-id={`undo${group.name}${path.basename(file.filename)}`} className='btn btn-sm btn-secondary mr-1 '><FontAwesomeIcon icon={faUndo} className="" /></button>} |
||||||
|
{(status && status.indexOf("unstaged") !== -1 || status && status.indexOf("deleted") !== -1) ? <></> : <button data-id={`addToGit${group.name}${path.basename(file.filename)}`} onClick={async () => await actions.add(file.filename)} className='btn btn-sm btn-secondary mr-1 '><FontAwesomeIcon icon={faPlus} className="" /></button>} |
||||||
|
{(status && status.indexOf("unstaged") !== -1 && status && status.indexOf("modified") !== -1) ? <button data-id={`addToGit${group.name}${path.basename(file.filename)}`} onClick={async () => await actions.add(file.filename)} className='btn btn-sm btn-secondary mr-1 '><FontAwesomeIcon icon={faPlus} className="" /></button> : <></>} |
||||||
|
</> |
||||||
|
} |
||||||
|
return <></> |
||||||
|
} |
||||||
|
|
||||||
|
return <RenderButtons /> |
||||||
|
|
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
import React, { useEffect, useState } from 'react' |
||||||
|
import { gitActionsContext, pluginActionsContext } from '../../state/context' |
||||||
|
import { gitPluginContext } from '../gitui' |
||||||
|
import { sourceControlGroup } from '../../types' |
||||||
|
import { SourceControGroup } from './sourcecontrol/sourcecontrolgroup' |
||||||
|
|
||||||
|
export const SourceControl = () => { |
||||||
|
const context = React.useContext(gitPluginContext) |
||||||
|
const actions = React.useContext(gitActionsContext) |
||||||
|
const pluginactions = React.useContext(pluginActionsContext) |
||||||
|
const [show, setShow] = useState(false) |
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (context.fileStatusResult) { |
||||||
|
console.log(context) |
||||||
|
const total = context.allchangesnotstaged.length |
||||||
|
const badges = total + context.staged.length |
||||||
|
pluginactions.statusChanged(badges) |
||||||
|
//console.log("allchangesnotstaged", context.allchangesnotstaged, context.fileStatusResult)
|
||||||
|
setShow((context.deleted.length > 0 || context.staged.length > 0 || context.untracked.length > 0 || context.modified.length > 0)) |
||||||
|
} |
||||||
|
}, [context.fileStatusResult, context.modified, context.allchangesnotstaged, context.untracked, context.deleted]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (context.commits) { |
||||||
|
console.log("SC commits", context.localCommitCount, context.currentBranch) |
||||||
|
} |
||||||
|
}, [context.localCommitCount, context.currentBranch]) |
||||||
|
|
||||||
|
function RenderGroups() { |
||||||
|
const groups: sourceControlGroup[] = [{ name: 'Staged', group: context.staged }, { name: 'Changes', group: context.allchangesnotstaged }] |
||||||
|
return (<> |
||||||
|
{ |
||||||
|
groups.map((ob: sourceControlGroup, index: number) => { |
||||||
|
return ( |
||||||
|
<SourceControGroup key={index} group={ob}></SourceControGroup> |
||||||
|
) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
</>) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
{show ? |
||||||
|
<> |
||||||
|
<div> |
||||||
|
<RenderGroups></RenderGroups> |
||||||
|
</div></> |
||||||
|
: <> |
||||||
|
|
||||||
|
</>} |
||||||
|
</> |
||||||
|
); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
import { useState } from "react" |
||||||
|
|
||||||
|
export function useLocalStorage(key: string, initialValue: any) { |
||||||
|
// State to store our value
|
||||||
|
// Pass initial state function to useState so logic is only executed once
|
||||||
|
const [storedValue, setStoredValue] = useState(() => { |
||||||
|
try { |
||||||
|
// Get from local storage by key
|
||||||
|
const item = window.localStorage.getItem(key) |
||||||
|
// Parse stored json or if none return initialValue
|
||||||
|
return item ? JSON.parse(item) : initialValue |
||||||
|
} catch (error) { |
||||||
|
// If error also return initialValue
|
||||||
|
console.error(error) |
||||||
|
return initialValue |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
// Return a wrapped version of useState's setter function that ...
|
||||||
|
// ... persists the new value to localStorage.
|
||||||
|
const setValue = (value: any) => { |
||||||
|
try { |
||||||
|
// Allow value to be a function so we have same API as useState
|
||||||
|
const valueToStore = |
||||||
|
value instanceof Function ? value(storedValue) : value |
||||||
|
// Save state
|
||||||
|
setStoredValue(valueToStore) |
||||||
|
// Save to local storage
|
||||||
|
window.localStorage.setItem(key, JSON.stringify(valueToStore)) |
||||||
|
} catch (error) { |
||||||
|
// A more advanced implementation would handle the error case
|
||||||
|
console.error(error) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return [storedValue, setValue] |
||||||
|
} |
@ -0,0 +1,4 @@ |
|||||||
|
export * from './types' |
||||||
|
export { GitUI } from './components/gitui' |
||||||
|
export { commitChange, commitChangeType, remote, branch } from './types' |
||||||
|
export * from './types/styles' |
@ -0,0 +1,59 @@ |
|||||||
|
import { fileStatusResult, gitState } from "../types"; |
||||||
|
|
||||||
|
export const getFilesCountByStatus = (status: string, files: fileStatusResult[]) => { |
||||||
|
let count = 0; |
||||||
|
|
||||||
|
files.map((m) => { |
||||||
|
if (m.statusNames !== undefined) { |
||||||
|
if (m.statusNames && m.statusNames.indexOf(status) > -1) { |
||||||
|
count++; |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
export const getFilesByStatus = (status: string, files: fileStatusResult[]): fileStatusResult[] => { |
||||||
|
const result: fileStatusResult[] = [] |
||||||
|
files.map((m) => { |
||||||
|
if (m.statusNames !== undefined) { |
||||||
|
if (m.statusNames && m.statusNames.indexOf(status) > -1) { |
||||||
|
result.push(m) |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
export const getFilesWithNotModifiedStatus = (files: fileStatusResult[]): fileStatusResult[] => { |
||||||
|
const result: fileStatusResult[] = [] |
||||||
|
|
||||||
|
|
||||||
|
files.map((m) => { |
||||||
|
|
||||||
|
|
||||||
|
if (m.statusNames !== undefined) { |
||||||
|
if (m.statusNames && m.statusNames.indexOf("unmodified") === -1) { |
||||||
|
result.push(m) |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
export const allChangedButNotStagedFiles = (files: fileStatusResult[]): fileStatusResult[] => { |
||||||
|
let allfiles = getFilesWithNotModifiedStatus(files) |
||||||
|
const staged = getFilesByStatus("staged", files) |
||||||
|
allfiles = allfiles.filter((trackedFile) => { |
||||||
|
return staged.findIndex((stagedFile) => stagedFile.filename === trackedFile.filename) === -1 |
||||||
|
}) |
||||||
|
return allfiles; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export const getFileStatusForFile = (filename: string, files: fileStatusResult[]) => { |
||||||
|
for (let i: number = 0; i < files.length; i++) { |
||||||
|
if (files[i].filename === filename) |
||||||
|
return files[i].statusNames; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,619 @@ |
|||||||
|
import { ViewPlugin } from "@remixproject/engine-web"; |
||||||
|
import { ReadBlobResult, ReadCommitResult } from "isomorphic-git"; |
||||||
|
import React from "react"; |
||||||
|
import { fileStatus, fileStatusMerge, setBranchCommits, setBranches, setCanCommit, setCommitChanges, setCommits, setCurrentBranch, setGitHubUser, setLoading, setRateLimit, setRemoteBranches, setRemotes, setRepos, setUpstream } from "../state/gitpayload"; |
||||||
|
import { GitHubUser, RateLimit, branch, commitChange, gitActionDispatch, statusMatrixType } from '../types'; |
||||||
|
import { removeSlash } from "../utils"; |
||||||
|
import { disableCallBacks, enableCallBacks } from "./listeners"; |
||||||
|
import { AlertModal, ModalTypes } from "@remix-ui/app"; |
||||||
|
import { gitActionsContext } from "../state/context"; |
||||||
|
import { gitPluginContext } from "../components/gitui"; |
||||||
|
import { setFileDecorators } from "./pluginActions"; |
||||||
|
|
||||||
|
export const fileStatuses = [ |
||||||
|
["new,untracked", 0, 2, 0], // new, untracked
|
||||||
|
["added,staged", 0, 2, 2], //
|
||||||
|
["added,staged,with unstaged changes", 0, 2, 3], // added, staged, with unstaged changes
|
||||||
|
["unmodified", 1, 1, 1], // unmodified
|
||||||
|
["modified,unstaged", 1, 2, 1], // modified, unstaged
|
||||||
|
["modified,staged", 1, 2, 2], // modified, staged
|
||||||
|
["modified,staged,with unstaged changes", 1, 2, 3], // modified, staged, with unstaged changes
|
||||||
|
["deleted,unstaged", 1, 0, 1], // deleted, unstaged
|
||||||
|
["deleted,staged", 1, 0, 0], |
||||||
|
//["deleted", 1, 1, 0], // deleted, staged
|
||||||
|
["unmodified", 1, 1, 3], |
||||||
|
["deleted,not in git", 0, 0, 3], |
||||||
|
["unstaged,modified", 1, 2, 0] |
||||||
|
]; |
||||||
|
|
||||||
|
const statusmatrix: statusMatrixType[] = fileStatuses.map((x: any) => { |
||||||
|
return { |
||||||
|
matrix: x.shift().split(","), |
||||||
|
status: x, |
||||||
|
}; |
||||||
|
}); |
||||||
|
|
||||||
|
let plugin: ViewPlugin, dispatch: React.Dispatch<gitActionDispatch> |
||||||
|
|
||||||
|
|
||||||
|
export const setPlugin = (p: ViewPlugin, dispatcher: React.Dispatch<gitActionDispatch>) => { |
||||||
|
plugin = p |
||||||
|
dispatch = dispatcher |
||||||
|
} |
||||||
|
|
||||||
|
export const getBranches = async () => { |
||||||
|
console.log('getBranches') |
||||||
|
const branches = await plugin.call("dGitProvider", "branches"); |
||||||
|
console.log('branches :>>', branches) |
||||||
|
dispatch(setBranches(branches)); |
||||||
|
} |
||||||
|
export const getRemotes = async () => { |
||||||
|
console.log('getRemotes') |
||||||
|
const remotes = await plugin.call("dGitProvider", "remotes" as any); |
||||||
|
console.log('remotes :>>', remotes) |
||||||
|
dispatch(setRemotes(remotes)); |
||||||
|
} |
||||||
|
|
||||||
|
export const setUpstreamRemote = async (remote: string) => { |
||||||
|
dispatch(setUpstream(remote)); |
||||||
|
} |
||||||
|
|
||||||
|
export const getFileStatusMatrix = async (filepaths: string[]) => { |
||||||
|
const fileStatusResult = await statusMatrix(filepaths); |
||||||
|
fileStatusResult.map((m) => { |
||||||
|
statusmatrix.map((sm) => { |
||||||
|
if (JSON.stringify(sm.status) === JSON.stringify(m.status)) { |
||||||
|
//Utils.log(m, sm);
|
||||||
|
m.statusNames = sm.matrix; |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
//console.log(fileStatusResult);
|
||||||
|
if (!filepaths) { |
||||||
|
dispatch(fileStatus(fileStatusResult)) |
||||||
|
} else { |
||||||
|
dispatch(fileStatusMerge(fileStatusResult)) |
||||||
|
setFileDecorators(fileStatusResult) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const getCommits = async () => { |
||||||
|
//Utils.log("get commits");
|
||||||
|
console.log('getCommits') |
||||||
|
try { |
||||||
|
const commits: ReadCommitResult[] = await plugin.call( |
||||||
|
"dGitProvider", |
||||||
|
"log", |
||||||
|
{ ref: "HEAD" } |
||||||
|
); |
||||||
|
console.log('commits :>>', commits) |
||||||
|
return commits; |
||||||
|
} catch (e) { |
||||||
|
return []; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const gitlog = async () => { |
||||||
|
let commits = [] |
||||||
|
try { |
||||||
|
commits = await getCommits() |
||||||
|
} catch (e) { |
||||||
|
} |
||||||
|
dispatch(setCommits(commits)) |
||||||
|
await showCurrentBranch() |
||||||
|
await getGitHubUser() |
||||||
|
} |
||||||
|
|
||||||
|
export const showCurrentBranch = async () => { |
||||||
|
try { |
||||||
|
const branch = await currentBranch(); |
||||||
|
const currentcommitoid = await getCommitFromRef("HEAD"); |
||||||
|
|
||||||
|
|
||||||
|
if (typeof branch === "undefined" || branch.name === "") { |
||||||
|
//toast.warn(`You are in a detached state`);
|
||||||
|
plugin.call('notification', 'alert', { |
||||||
|
type: 'warning', |
||||||
|
title: 'You are in a detached state', |
||||||
|
}) |
||||||
|
branch.name = `HEAD detached at ${currentcommitoid}`; |
||||||
|
//canCommit = false;
|
||||||
|
dispatch(setCanCommit(false)); |
||||||
|
} else { |
||||||
|
//canCommit = true;
|
||||||
|
dispatch(setCanCommit(true)); |
||||||
|
dispatch(setCurrentBranch(branch)); |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
// show empty branch
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const currentBranch = async () => { |
||||||
|
// eslint-disable-next-line no-useless-catch
|
||||||
|
try { |
||||||
|
const branch: branch = |
||||||
|
(await plugin.call("dGitProvider", "currentbranch")) || ""; |
||||||
|
return branch; |
||||||
|
} catch (e) { |
||||||
|
throw e; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const createBranch = async (name: string = "") => { |
||||||
|
if (name) { |
||||||
|
await plugin.call("dGitProvider", "branch", { ref: name }); |
||||||
|
await plugin.call("dGitProvider", "checkout", { ref: name }); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const getCommitFromRef = async (ref: string) => { |
||||||
|
const commitOid = await plugin.call("dGitProvider", "resolveref", { |
||||||
|
ref: ref, |
||||||
|
}); |
||||||
|
return commitOid; |
||||||
|
} |
||||||
|
|
||||||
|
const settingsWarning = async () => { |
||||||
|
const username = await plugin.call('config' as any, 'getAppParameter', 'settings/github-user-name') |
||||||
|
const email = await plugin.call('config' as any, 'getAppParameter', 'settings/github-email') |
||||||
|
if (!username || !email) { |
||||||
|
plugin.call('notification', 'toast', 'Please set your github username and email in the settings') |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
return { |
||||||
|
username, |
||||||
|
email |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const commit = async (message: string = "") => { |
||||||
|
|
||||||
|
try { |
||||||
|
const credentials = await settingsWarning() |
||||||
|
if (!credentials) { |
||||||
|
dispatch(setLoading(false)) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
const sha = await plugin.call("dGitProvider", "commit", { |
||||||
|
author: { |
||||||
|
name: credentials.username, |
||||||
|
email: credentials.email, |
||||||
|
}, |
||||||
|
message: message, |
||||||
|
}); |
||||||
|
plugin.call('notification', 'toast', `Commited: ${sha}`) |
||||||
|
|
||||||
|
} catch (err) { |
||||||
|
plugin.call('notification', 'toast', `${err}`) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
export const addall = async () => { |
||||||
|
try { |
||||||
|
await plugin |
||||||
|
.call("dGitProvider", "status", { ref: "HEAD" }) |
||||||
|
.then((status) => |
||||||
|
Promise.all( |
||||||
|
status.map(([filepath, , worktreeStatus]) => |
||||||
|
worktreeStatus |
||||||
|
? plugin.call("dGitProvider", "add", { |
||||||
|
filepath: removeSlash(filepath), |
||||||
|
}) |
||||||
|
: plugin.call("dGitProvider", "rm", { |
||||||
|
filepath: removeSlash(filepath), |
||||||
|
}) |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
plugin.call('notification', 'toast', `Added all files to git`) |
||||||
|
|
||||||
|
} catch (e) { |
||||||
|
plugin.call('notification', 'toast', `${e}`) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const add = async (args: string | undefined) => { |
||||||
|
if (args !== undefined) { |
||||||
|
let filename = args; // $(args[0].currentTarget).data('file')
|
||||||
|
let stagingfiles; |
||||||
|
if (filename !== "/") { |
||||||
|
filename = removeSlash(filename); |
||||||
|
stagingfiles = [filename]; |
||||||
|
} else { |
||||||
|
await addall(); |
||||||
|
return; |
||||||
|
} |
||||||
|
try { |
||||||
|
for (const filepath of stagingfiles) { |
||||||
|
try { |
||||||
|
await plugin.call("dGitProvider", "add", { |
||||||
|
filepath: removeSlash(filepath), |
||||||
|
}); |
||||||
|
} catch (e) { } |
||||||
|
} |
||||||
|
plugin.call('notification', 'toast', `Added ${filename} to git`); |
||||||
|
} catch (e) { |
||||||
|
plugin.call('notification', 'toast', `${e}`) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
const getLastCommmit = async () => { |
||||||
|
try { |
||||||
|
let currentcommitoid = ""; |
||||||
|
currentcommitoid = await getCommitFromRef("HEAD"); |
||||||
|
return currentcommitoid; |
||||||
|
} catch (e) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export const rm = async (args: any) => { |
||||||
|
const filename = args; |
||||||
|
await plugin.call("dGitProvider", "rm", { |
||||||
|
filepath: removeSlash(filename), |
||||||
|
}); |
||||||
|
plugin.call('notification', 'toast', `Removed ${filename} from git`) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export const checkoutfile = async (filename: string) => { |
||||||
|
const oid = await getLastCommmit(); |
||||||
|
if (oid) |
||||||
|
try { |
||||||
|
const commitOid = await plugin.call("dGitProvider", "resolveref", { |
||||||
|
ref: oid, |
||||||
|
}); |
||||||
|
const { blob } = await plugin.call("dGitProvider", "readblob", { |
||||||
|
oid: commitOid, |
||||||
|
filepath: removeSlash(filename), |
||||||
|
}); |
||||||
|
const original = Buffer.from(blob).toString("utf8"); |
||||||
|
//(original, filename);
|
||||||
|
await disableCallBacks(); |
||||||
|
await plugin.call( |
||||||
|
"fileManager", |
||||||
|
"setFile", |
||||||
|
removeSlash(filename), |
||||||
|
original |
||||||
|
); |
||||||
|
await enableCallBacks(); |
||||||
|
} catch (e) { |
||||||
|
plugin.call('notification', 'toast', `No such file`) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const checkout = async (cmd: any) => { |
||||||
|
await disableCallBacks(); |
||||||
|
await plugin.call('fileManager', 'closeAllFiles') |
||||||
|
try { |
||||||
|
await plugin.call("dGitProvider", "checkout", cmd); |
||||||
|
gitlog(); |
||||||
|
} catch (e) { |
||||||
|
plugin.call('notification', 'toast', `${e}`) |
||||||
|
} |
||||||
|
await enableCallBacks(); |
||||||
|
} |
||||||
|
|
||||||
|
export const clone = async (url: string, branch: string, depth: number, singleBranch: boolean) => { |
||||||
|
console.log(url, branch, depth, singleBranch) |
||||||
|
dispatch(setLoading(true)) |
||||||
|
try { |
||||||
|
await disableCallBacks() |
||||||
|
// get last part of url
|
||||||
|
const urlParts = url.split("/"); |
||||||
|
const lastPart = urlParts[urlParts.length - 1]; |
||||||
|
const repoName = lastPart.split(".")[0]; |
||||||
|
// add timestamp to repo name
|
||||||
|
const timestamp = new Date().getTime(); |
||||||
|
const repoNameWithTimestamp = `${repoName}-${timestamp}`; |
||||||
|
//const token = await tokenWarning();
|
||||||
|
const token = await plugin.call('config' as any, 'getAppParameter', 'settings/gist-access-token') |
||||||
|
//if (!token) {
|
||||||
|
// dispatch(setLoading(false))
|
||||||
|
// return
|
||||||
|
//} else {
|
||||||
|
await plugin.call('dGitProvider' as any, 'clone', { url, branch, token, depth, singleBranch }, repoNameWithTimestamp); |
||||||
|
await enableCallBacks() |
||||||
|
plugin.call('notification', 'toast', `Cloned ${url} to ${repoNameWithTimestamp}`) |
||||||
|
//}
|
||||||
|
} catch (e: any) { |
||||||
|
await parseError(e) |
||||||
|
} |
||||||
|
dispatch(setLoading(false)) |
||||||
|
} |
||||||
|
|
||||||
|
const tokenWarning = async () => { |
||||||
|
const token = await plugin.call('config' as any, 'getAppParameter', 'settings/gist-access-token') |
||||||
|
if (!token) { |
||||||
|
const modalContent: AlertModal = { |
||||||
|
message: 'Please set a token first in the GitHub settings of REMIX', |
||||||
|
title: 'No GitHub token set', |
||||||
|
id: 'no-token-set', |
||||||
|
} |
||||||
|
plugin.call('notification', 'alert', modalContent) |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
return token; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
const parseError = async (e: any) => { |
||||||
|
// if message conttains 401 Unauthorized, show token warning
|
||||||
|
if (e.message.includes('401')) { |
||||||
|
const result = await plugin.call('notification', 'modal', { |
||||||
|
title: 'The GitHub token may be missing or invalid', |
||||||
|
message: 'Please check the GitHub token and try again. Error: 401 Unauthorized', |
||||||
|
okLabel: 'Go to settings', |
||||||
|
cancelLabel: 'Close', |
||||||
|
type: ModalTypes.confirm |
||||||
|
}) |
||||||
|
console.log(result) |
||||||
|
} |
||||||
|
// if message contains 404 Not Found, show repo not found
|
||||||
|
else if (e.message.includes('404')) { |
||||||
|
await plugin.call('notification', 'modal', { |
||||||
|
title: 'Repository not found', |
||||||
|
message: 'Please check the URL and try again.', |
||||||
|
okLabel: 'Go to settings', |
||||||
|
cancelLabel: 'Close', |
||||||
|
type: ModalTypes.confirm |
||||||
|
}) |
||||||
|
} |
||||||
|
// if message contains 403 Forbidden
|
||||||
|
else if (e.message.includes('403')) { |
||||||
|
await plugin.call('notification', 'modal', { |
||||||
|
title: 'The GitHub token may be missing or invalid', |
||||||
|
message: 'Please check the GitHub token and try again. Error: 403 Forbidden', |
||||||
|
okLabel: 'Go to settings', |
||||||
|
cancelLabel: 'Close', |
||||||
|
type: ModalTypes.confirm |
||||||
|
}) |
||||||
|
} else { |
||||||
|
await plugin.call('notification', 'alert', { |
||||||
|
title: 'Error', |
||||||
|
message: e.message |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const repositories = async () => { |
||||||
|
try { |
||||||
|
const token = await tokenWarning(); |
||||||
|
if (token) { |
||||||
|
const repos = await plugin.call('dGitProvider' as any, 'repositories', { token }); |
||||||
|
dispatch(setRepos(repos)) |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
plugin.call('notification', 'alert', { |
||||||
|
title: 'Error getting repositories', |
||||||
|
message: `${e.message}: Please check your GitHub token in the GitHub settings.` |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const remoteBranches = async (owner: string, repo: string) => { |
||||||
|
try { |
||||||
|
const token = await tokenWarning(); |
||||||
|
if (token) { |
||||||
|
const branches = await plugin.call('dGitProvider' as any, 'remotebranches', { token, owner, repo }); |
||||||
|
dispatch(setRemoteBranches(branches)) |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
plugin.call('notification', 'alert', { |
||||||
|
title: 'Error', |
||||||
|
message: e.message |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const remoteCommits = async (url: string, branch: string, length: number) => { |
||||||
|
const urlParts = url.split("/"); |
||||||
|
|
||||||
|
console.log(urlParts, 'urlParts') |
||||||
|
// check if it's github
|
||||||
|
if(!urlParts[urlParts.length - 3].includes('github')) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
const owner = urlParts[urlParts.length - 2]; |
||||||
|
const repo = urlParts[urlParts.length - 1].split(".")[0]; |
||||||
|
|
||||||
|
try { |
||||||
|
const token = await tokenWarning(); |
||||||
|
if (token) { |
||||||
|
console.log(token, owner, repo, branch, length) |
||||||
|
const commits = await plugin.call('dGitProvider' as any, 'remotecommits', { token, owner, repo, branch, length }); |
||||||
|
console.log(commits, 'remote commits') |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
plugin.call('notification', 'alert', { |
||||||
|
title: 'Error', |
||||||
|
message: e.message |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const getGitHubUser = async () => { |
||||||
|
try { |
||||||
|
const token = await tokenWarning(); |
||||||
|
if (token) { |
||||||
|
const data: { |
||||||
|
user: GitHubUser, |
||||||
|
ratelimit: RateLimit |
||||||
|
} = await plugin.call('dGitProvider' as any, 'getGitHubUser', { token }); |
||||||
|
|
||||||
|
console.log('GET USER"', data) |
||||||
|
|
||||||
|
dispatch(setGitHubUser(data.user)) |
||||||
|
dispatch(setRateLimit(data.ratelimit)) |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const statusMatrix = async (filepaths: string[]) => { |
||||||
|
const matrix = await plugin.call("dGitProvider", "status", { ref: "HEAD", filepaths: filepaths || ['.'] }); |
||||||
|
const result = (matrix || []).map((x) => { |
||||||
|
return { |
||||||
|
filename: `/${x.shift()}`, |
||||||
|
status: x, |
||||||
|
}; |
||||||
|
}); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
export const diffFiles = async (filename: string | undefined) => { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
export const resolveRef = async (ref: string) => { |
||||||
|
const oid = await plugin.call("dGitProvider", "resolveref", { |
||||||
|
ref, |
||||||
|
}); |
||||||
|
return oid; |
||||||
|
} |
||||||
|
|
||||||
|
export const diff = async (commitChange: commitChange) => { |
||||||
|
|
||||||
|
if (!commitChange.hashModified) { |
||||||
|
const newcontent = await plugin.call( |
||||||
|
"fileManager", |
||||||
|
"readFile",//
|
||||||
|
removeSlash(commitChange.path) |
||||||
|
); |
||||||
|
commitChange.modified = newcontent; |
||||||
|
commitChange.readonly = false; |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
try { |
||||||
|
const modifiedContentReadBlobResult: ReadBlobResult = await plugin.call("dGitProvider", "readblob", { |
||||||
|
oid: commitChange.hashModified, |
||||||
|
filepath: removeSlash(commitChange.path), |
||||||
|
}); |
||||||
|
|
||||||
|
const modifiedContent = Buffer.from(modifiedContentReadBlobResult.blob).toString("utf8"); |
||||||
|
console.log(modifiedContent) |
||||||
|
commitChange.modified = modifiedContent; |
||||||
|
commitChange.readonly = true; |
||||||
|
} catch (e) { |
||||||
|
commitChange.modified = ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
const originalContentReadBlobResult: ReadBlobResult = await plugin.call("dGitProvider", "readblob", { |
||||||
|
oid: commitChange.hashOriginal, |
||||||
|
filepath: removeSlash(commitChange.path), |
||||||
|
}); |
||||||
|
|
||||||
|
const originalContent = Buffer.from(originalContentReadBlobResult.blob).toString("utf8"); |
||||||
|
console.log(originalContent) |
||||||
|
commitChange.original = originalContent; |
||||||
|
} catch (e) { |
||||||
|
commitChange.original = ""; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* |
||||||
|
const fullfilename = args; // $(args[0].currentTarget).data('file')
|
||||||
|
try { |
||||||
|
const commitOid = await client.call( |
||||||
|
"dGitProvider", |
||||||
|
"resolveref", |
||||||
|
{ ref: "HEAD" } |
||||||
|
); |
||||||
|
|
||||||
|
const { blob } = await client.call("dGitProvider", "readblob", { |
||||||
|
oid: commitOid, |
||||||
|
filepath: removeSlash(fullfilename), |
||||||
|
}); |
||||||
|
|
||||||
|
const newcontent = await client.call( |
||||||
|
"fileManager", |
||||||
|
"readFile",//
|
||||||
|
removeSlash(fullfilename) |
||||||
|
); |
||||||
|
|
||||||
|
|
||||||
|
// Utils.log(original);
|
||||||
|
//Utils.log(newcontent);
|
||||||
|
//const filediff = createPatch(filename, original, newcontent); // diffLines(original,newcontent)
|
||||||
|
////Utils.log(filediff)
|
||||||
|
const filediff: diffObject = { |
||||||
|
originalFileName: fullfilename, |
||||||
|
updatedFileName: fullfilename, |
||||||
|
current: newcontent, |
||||||
|
past: original, |
||||||
|
}; |
||||||
|
|
||||||
|
return filediff; |
||||||
|
} catch (e) { |
||||||
|
|
||||||
|
|
||||||
|
const filediff: diffObject = { |
||||||
|
originalFileName: "", |
||||||
|
updatedFileName: "", |
||||||
|
current: "", |
||||||
|
past: "", |
||||||
|
}; |
||||||
|
return filediff; |
||||||
|
} |
||||||
|
*/ |
||||||
|
} |
||||||
|
|
||||||
|
export const getCommitChanges = async (oid1: string, oid2: string) => { |
||||||
|
const result: commitChange[] = await plugin.call('dGitProvider', 'getCommitChanges', oid1, oid2) |
||||||
|
dispatch(setCommitChanges(result)) |
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
export const fetchBranch = async (branch: branch) => { |
||||||
|
const r = await plugin.call('dGitProvider', 'fetch', { |
||||||
|
ref: branch.name, |
||||||
|
remoteRef: branch.name, |
||||||
|
singleBranch: true, |
||||||
|
remote: branch.remote.remote, |
||||||
|
depth: 10 |
||||||
|
}) |
||||||
|
const commits: ReadCommitResult[] = await plugin.call('dGitProvider', 'log', { |
||||||
|
ref: r.fetchHead |
||||||
|
}) |
||||||
|
console.log(r, commits) |
||||||
|
dispatch(setBranchCommits({ branch, commits })) |
||||||
|
} |
||||||
|
|
||||||
|
export const getBranchCommits = async (branch: branch) => { |
||||||
|
try { |
||||||
|
console.log(branch) |
||||||
|
const commits: ReadCommitResult[] = await plugin.call('dGitProvider', 'log', { |
||||||
|
ref: branch.name, |
||||||
|
}) |
||||||
|
console.log(commits) |
||||||
|
dispatch(setBranchCommits({ branch, commits })) |
||||||
|
return commits |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
await fetchBranch(branch) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,192 @@ |
|||||||
|
|
||||||
|
import { ViewPlugin } from "@remixproject/engine-web"; |
||||||
|
import React from "react"; |
||||||
|
import { setCanUseApp, setLoading, setRepoName } from "../state/gitpayload"; |
||||||
|
import { gitActionDispatch } from "../types"; |
||||||
|
import { diffFiles, getBranches, getFileStatusMatrix, getGitHubUser, getRemotes, gitlog, setPlugin } from "./gitactions"; |
||||||
|
|
||||||
|
let plugin: ViewPlugin, gitDispatch: React.Dispatch<gitActionDispatch>, loaderDispatch: React.Dispatch<any> |
||||||
|
let callBackEnabled: boolean = false |
||||||
|
let syncTimer: NodeJS.Timer = null |
||||||
|
|
||||||
|
export const setCallBacks = (viewPlugin: ViewPlugin, gitDispatcher: React.Dispatch<gitActionDispatch>, loaderDispatcher: React.Dispatch<any>) => { |
||||||
|
plugin = viewPlugin |
||||||
|
gitDispatch = gitDispatcher |
||||||
|
loaderDispatch = loaderDispatcher |
||||||
|
|
||||||
|
setPlugin(viewPlugin, gitDispatcher) |
||||||
|
|
||||||
|
plugin.on("fileManager", "fileSaved", async (file: string) => { |
||||||
|
console.log(file) |
||||||
|
loadFiles([file]) |
||||||
|
//await synTimerStart();
|
||||||
|
}); |
||||||
|
|
||||||
|
plugin.on('dGitProvider', 'checkout' as any, async () => { |
||||||
|
await synTimerStart(); |
||||||
|
}) |
||||||
|
plugin.on('dGitProvider', 'branch' as any, async () => { |
||||||
|
await synTimerStart(); |
||||||
|
}) |
||||||
|
|
||||||
|
plugin.on("fileManager", "fileAdded", async (e) => { |
||||||
|
await synTimerStart(); |
||||||
|
}); |
||||||
|
|
||||||
|
plugin.on("fileManager", "fileRemoved", async (e) => { |
||||||
|
await synTimerStart(); |
||||||
|
}); |
||||||
|
|
||||||
|
plugin.on("fileManager", "currentFileChanged", async (e) => { |
||||||
|
console.log('current file change', e) |
||||||
|
//await synTimerStart();
|
||||||
|
}); |
||||||
|
|
||||||
|
plugin.on("fileManager", "fileRenamed", async (oldfile, newfile) => { |
||||||
|
await synTimerStart(); |
||||||
|
}); |
||||||
|
|
||||||
|
plugin.on("filePanel", "setWorkspace", async (x: any) => { |
||||||
|
await synTimerStart(); |
||||||
|
}); |
||||||
|
|
||||||
|
plugin.on("filePanel", "deleteWorkspace" as any, async (x: any) => { |
||||||
|
await synTimerStart(); |
||||||
|
}); |
||||||
|
|
||||||
|
plugin.on("filePanel", "renameWorkspace" as any, async (x: any) => { |
||||||
|
await synTimerStart(); |
||||||
|
}); |
||||||
|
|
||||||
|
plugin.on('dGitProvider', 'checkout', async () => { |
||||||
|
await loadFiles(); |
||||||
|
}) |
||||||
|
plugin.on('dGitProvider', 'init', async () => { |
||||||
|
await loadFiles(); |
||||||
|
}) |
||||||
|
plugin.on('dGitProvider', 'add', async () => { |
||||||
|
await loadFiles(); |
||||||
|
}) |
||||||
|
plugin.on('dGitProvider', 'rm', async () => { |
||||||
|
await loadFiles(); |
||||||
|
}) |
||||||
|
plugin.on('dGitProvider', 'commit', async () => { |
||||||
|
await loadFiles(); |
||||||
|
}) |
||||||
|
plugin.on('dGitProvider', 'branch', async () => { |
||||||
|
await loadFiles(); |
||||||
|
}) |
||||||
|
plugin.on('dGitProvider', 'clone', async () => { |
||||||
|
await loadFiles(); |
||||||
|
}) |
||||||
|
plugin.on('manager', 'pluginActivated', async (p: Plugin) => { |
||||||
|
if (p.name === 'dGitProvider') { |
||||||
|
getGitHubUser(); |
||||||
|
plugin.off('manager', 'pluginActivated'); |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
plugin.on('config', 'configChanged', async () => { |
||||||
|
await getGitConfig() |
||||||
|
}) |
||||||
|
plugin.on('settings', 'configChanged', async () => { |
||||||
|
await getGitConfig() |
||||||
|
}) |
||||||
|
|
||||||
|
|
||||||
|
callBackEnabled = true; |
||||||
|
} |
||||||
|
|
||||||
|
export const getGitConfig = async () => { |
||||||
|
const username = await plugin.call('settings', 'get', 'settings/github-user-name') |
||||||
|
const email = await plugin.call('settings', 'get', 'settings/github-email') |
||||||
|
const token = await plugin.call('settings', 'get', 'settings/gist-access-token') |
||||||
|
const config = { username, email, token } |
||||||
|
//dispatch(setGitConfig(config))
|
||||||
|
return config |
||||||
|
} |
||||||
|
|
||||||
|
const syncFromWorkspace = async (isLocalhost = false) => { |
||||||
|
//gitDispatch(setLoading(true));
|
||||||
|
await disableCallBacks(); |
||||||
|
if (isLocalhost) { |
||||||
|
gitDispatch(setCanUseApp(false)); |
||||||
|
gitDispatch(setLoading(false)); |
||||||
|
await enableCallBacks(); |
||||||
|
return; |
||||||
|
} |
||||||
|
try { |
||||||
|
const workspace = await plugin.call( |
||||||
|
"filePanel", |
||||||
|
"getCurrentWorkspace" |
||||||
|
); |
||||||
|
if (workspace.isLocalhost) { |
||||||
|
gitDispatch(setCanUseApp(false)); |
||||||
|
await enableCallBacks(); |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
gitDispatch(setRepoName(workspace.name)); |
||||||
|
gitDispatch(setCanUseApp(true)); |
||||||
|
} catch (e) { |
||||||
|
gitDispatch(setCanUseApp(false)); |
||||||
|
} |
||||||
|
await loadFiles(); |
||||||
|
await enableCallBacks(); |
||||||
|
} |
||||||
|
|
||||||
|
export const loadFiles = async (filepaths: string[] = null) => { |
||||||
|
//gitDispatch(setLoading(true));
|
||||||
|
|
||||||
|
try { |
||||||
|
await getFileStatusMatrix(filepaths); |
||||||
|
} catch (e) { |
||||||
|
// TODO: handle error
|
||||||
|
console.error(e); |
||||||
|
} |
||||||
|
try { |
||||||
|
await gitlog(); |
||||||
|
} catch (e) { } |
||||||
|
try { |
||||||
|
await getBranches(); |
||||||
|
} catch (e) { } |
||||||
|
try { |
||||||
|
await getRemotes(); |
||||||
|
} catch (e) { } |
||||||
|
try { |
||||||
|
//await getStorageUsed();
|
||||||
|
} catch (e) { } |
||||||
|
try { |
||||||
|
//await diffFiles('');
|
||||||
|
} catch (e) { } |
||||||
|
//gitDispatch(setLoading(false));
|
||||||
|
} |
||||||
|
|
||||||
|
const getStorageUsed = async () => { |
||||||
|
try { |
||||||
|
const storageUsed = await plugin.call("storage" as any, "getStorage" as any); |
||||||
|
} catch (e) { |
||||||
|
const storage: string = await plugin.call("dGitProvider", "localStorageUsed" as any); |
||||||
|
const storageUsed = { |
||||||
|
usage: parseFloat(storage) * 1000, |
||||||
|
quota: 10000000, |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const disableCallBacks = async () => { |
||||||
|
callBackEnabled = false; |
||||||
|
} |
||||||
|
export const enableCallBacks = async () => { |
||||||
|
callBackEnabled = true; |
||||||
|
} |
||||||
|
|
||||||
|
const synTimerStart = async () => { |
||||||
|
if (!callBackEnabled) return |
||||||
|
clearTimeout(syncTimer) |
||||||
|
syncTimer = setTimeout(async () => { |
||||||
|
await syncFromWorkspace(); |
||||||
|
}, 1000) |
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
import { ViewPlugin } from "@remixproject/engine-web" |
||||||
|
import { commitChange, fileStatusResult, gitActionDispatch, gitState } from "../types" |
||||||
|
import { fileDecoration, fileDecorationType } from "@remix-ui/file-decorators" |
||||||
|
import { removeSlash } from "../utils" |
||||||
|
import path from "path" |
||||||
|
import { getFilesByStatus, getFilesWithNotModifiedStatus } from "./fileHelpers" |
||||||
|
|
||||||
|
let plugin: ViewPlugin, gitDispatch: React.Dispatch<gitActionDispatch>, loaderDispatch: React.Dispatch<any> |
||||||
|
|
||||||
|
export const setPlugin = (p: ViewPlugin, gitDispatcher: React.Dispatch<gitActionDispatch>, loaderDispatcher: React.Dispatch<any>) => { |
||||||
|
plugin = p |
||||||
|
gitDispatch = gitDispatcher |
||||||
|
loaderDispatch = loaderDispatcher |
||||||
|
} |
||||||
|
|
||||||
|
export const statusChanged = (badges: number) => { |
||||||
|
if(!plugin) return |
||||||
|
plugin.emit('statusChanged', { |
||||||
|
key: badges === 0 ? 'none' : badges, |
||||||
|
type: badges === 0 ? '' : 'success', |
||||||
|
title: 'Git changes' |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export const openFile = async (path: string) => { |
||||||
|
if(!plugin) return |
||||||
|
await plugin.call('fileManager', 'open', path) |
||||||
|
} |
||||||
|
|
||||||
|
export const openDiff = async (change: commitChange) => { |
||||||
|
console.log('openDiff', change) |
||||||
|
if(!plugin) return |
||||||
|
plugin.call('fileManager', 'diff', change) |
||||||
|
} |
||||||
|
|
||||||
|
export const saveToken = async (token: string) => { |
||||||
|
if(!plugin) return |
||||||
|
await plugin.call('config', 'setAppParameter', 'settings/gist-access-token', token) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export const setFileDecorators = async (files: fileStatusResult[]) => { |
||||||
|
|
||||||
|
if(!plugin) return |
||||||
|
const modified = getFilesByStatus('modified', files) |
||||||
|
const untracked = getFilesByStatus('untracked', files) |
||||||
|
const unmodified = getFilesByStatus('unmodified', files) |
||||||
|
|
||||||
|
|
||||||
|
await setModifiedDecorator(modified) |
||||||
|
await setUntrackedDecorator(untracked) |
||||||
|
unmodified.forEach((file) => { |
||||||
|
clearFileDecorator(removeSlash(file.filename)) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export const setModifiedDecorator = async (files: fileStatusResult[]) => { |
||||||
|
const decorators: fileDecoration[] = [] |
||||||
|
for (const file of files) { |
||||||
|
const decorator: fileDecoration = { |
||||||
|
path: removeSlash(file.filename), |
||||||
|
isDirectory: false, |
||||||
|
fileStateType: fileDecorationType.Custom, |
||||||
|
fileStateLabelClass: 'text-warning', |
||||||
|
fileStateIconClass: 'text-warning', |
||||||
|
fileStateIcon: '', |
||||||
|
text: 'M', |
||||||
|
owner: 'git', |
||||||
|
bubble: true, |
||||||
|
comment: 'Modified' |
||||||
|
} |
||||||
|
decorators.push(decorator) |
||||||
|
} |
||||||
|
|
||||||
|
await plugin.call('fileDecorator', 'setFileDecorators', decorators) |
||||||
|
} |
||||||
|
|
||||||
|
export const setUntrackedDecorator = async (files: fileStatusResult[]) => { |
||||||
|
const decorators: fileDecoration[] = [] |
||||||
|
for (const file of files) { |
||||||
|
const decorator: fileDecoration = { |
||||||
|
path: removeSlash(file.filename), |
||||||
|
isDirectory: false, |
||||||
|
fileStateType: fileDecorationType.Custom, |
||||||
|
fileStateLabelClass: 'text-success', |
||||||
|
fileStateIconClass: 'text-success', |
||||||
|
fileStateIcon: '', |
||||||
|
text: 'U', |
||||||
|
owner: 'git', |
||||||
|
bubble: true, |
||||||
|
comment: 'Untracked' |
||||||
|
} |
||||||
|
decorators.push(decorator) |
||||||
|
} |
||||||
|
|
||||||
|
await plugin.call('fileDecorator', 'setFileDecorators', decorators) |
||||||
|
} |
||||||
|
|
||||||
|
export const clearFileDecorator = async(path: string) => { |
||||||
|
await plugin.call('fileDecorator', 'clearFileDecorators', path) |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,39 @@ |
|||||||
|
import { ReadCommitResult } from "isomorphic-git" |
||||||
|
import React from "react" |
||||||
|
import { branch, commitChange } from "../types" |
||||||
|
|
||||||
|
export interface gitActions { |
||||||
|
clone(url: string, path: string, depth: number, singleBranch: boolean): Promise<void> |
||||||
|
add(path: string): Promise<void> |
||||||
|
rm(path: string): Promise<void> |
||||||
|
commit(message: string): Promise<any> |
||||||
|
addall(): Promise<void> |
||||||
|
//push(): Promise<void>
|
||||||
|
//pull(): Promise<void>
|
||||||
|
//fetch(): Promise<void>
|
||||||
|
repositories(): Promise<any> |
||||||
|
checkoutfile(file: string): Promise<void> |
||||||
|
checkout(cmd: any): Promise<void> |
||||||
|
createBranch(branch: string): Promise<void> |
||||||
|
remoteBranches(owner: string, repo: string): Promise<any> |
||||||
|
getCommitChanges(oid1: string, oid2: string): Promise<commitChange[]> |
||||||
|
getBranchCommits(branch: branch): Promise<ReadCommitResult[]> |
||||||
|
getGitHubUser(): Promise<any> |
||||||
|
diff(commitChange: commitChange): Promise<void> |
||||||
|
resolveRef(ref: string): Promise<string> |
||||||
|
setUpstreamRemote(upstream: string): Promise<void> |
||||||
|
getBranches: () => Promise<void> |
||||||
|
getRemotes: () => Promise<void> |
||||||
|
} |
||||||
|
|
||||||
|
export const gitActionsContext = React.createContext<gitActions>(null) |
||||||
|
|
||||||
|
export interface pluginActions { |
||||||
|
statusChanged(data: any): void |
||||||
|
loadFiles(): void |
||||||
|
openFile(path: string): Promise<void> |
||||||
|
openDiff(change: commitChange): Promise<void> |
||||||
|
saveToken(token: string): Promise<void> |
||||||
|
} |
||||||
|
|
||||||
|
export const pluginActionsContext = React.createContext<pluginActions>(null) |
@ -0,0 +1,130 @@ |
|||||||
|
import { ReadCommitResult } from "isomorphic-git" |
||||||
|
import { GitHubUser, branch, commitChange, fileStatusResult, remote } from "../types" |
||||||
|
import { Endpoints } from "@octokit/types" |
||||||
|
|
||||||
|
export const fileStatus = (files: fileStatusResult[]) => { |
||||||
|
return { |
||||||
|
type: 'FILE_STATUS', |
||||||
|
payload: files |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const fileStatusMerge = (files: fileStatusResult[]) => { |
||||||
|
return { |
||||||
|
type: 'FILE_STATUS_MERGE', |
||||||
|
payload: files |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setCommits = (commits: ReadCommitResult[]) => { |
||||||
|
return { |
||||||
|
type: 'SET_COMMITS', |
||||||
|
payload: commits |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setBranches = (branches: any[]) => { |
||||||
|
return { |
||||||
|
type: 'SET_BRANCHES', |
||||||
|
payload: branches |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setRepos = (repos: any[]) => { |
||||||
|
return { |
||||||
|
type: 'SET_REPOS', |
||||||
|
payload: repos |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setRemoteBranches = (branches: any[]) => { |
||||||
|
return { |
||||||
|
type: 'SET_REMOTE_BRANCHES', |
||||||
|
payload: branches |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setGitHubUser = (user: any) => { |
||||||
|
return { |
||||||
|
type: 'SET_GITHUB_USER', |
||||||
|
payload: user |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setRateLimit = (rateLimit: any) => { |
||||||
|
return { |
||||||
|
type: 'SET_RATE_LIMIT', |
||||||
|
payload: rateLimit |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setGitHubAccessToken = (token: string) => { |
||||||
|
return { |
||||||
|
type: 'SET_GITHUB_ACCESS_TOKEN', |
||||||
|
payload: token |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export const setLoading = (loading: boolean) => { |
||||||
|
return { |
||||||
|
type: 'SET_LOADING', |
||||||
|
payload: loading |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setCanUseApp = (canUseApp: boolean) => { |
||||||
|
return { |
||||||
|
type: 'SET_CAN_USE_APP', |
||||||
|
payload: canUseApp |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setRepoName = (reponame: string) => { |
||||||
|
return { |
||||||
|
type: 'SET_REPO_NAME', |
||||||
|
payload: reponame |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setCurrentBranch = (currentBranch: branch) => { |
||||||
|
return { |
||||||
|
type: 'SET_CURRENT_BRANCH', |
||||||
|
payload: currentBranch |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setCanCommit = (canCommit: boolean) => { |
||||||
|
return { |
||||||
|
type: 'SET_CAN_COMMIT', |
||||||
|
payload: canCommit |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setRemotes = (remotes: remote[]) => { |
||||||
|
return { |
||||||
|
type: 'SET_REMOTES', |
||||||
|
payload: remotes |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setUpstream = (upstream: string) => { |
||||||
|
return { |
||||||
|
type: 'SET_UPSTREAM', |
||||||
|
payload: upstream |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setCommitChanges = (commitChanges: commitChange[]) => { |
||||||
|
return { |
||||||
|
type: 'SET_COMMIT_CHANGES', |
||||||
|
payload: commitChanges |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const setBranchCommits =({branch, commits}) => { |
||||||
|
return { |
||||||
|
type: 'SET_BRANCH_COMMITS', |
||||||
|
payload: { branch, commits } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,147 @@ |
|||||||
|
import { ReadCommitResult } from "isomorphic-git" |
||||||
|
import { allChangedButNotStagedFiles, getFilesByStatus, getFilesWithNotModifiedStatus } from "../lib/fileHelpers" |
||||||
|
import { branch, commitChange, defaultGitState, fileStatusResult, gitState, setBranchCommitsAction } from "../types" |
||||||
|
|
||||||
|
interface Action { |
||||||
|
type: string |
||||||
|
payload: any |
||||||
|
} |
||||||
|
|
||||||
|
export const gitReducer = (state: gitState = defaultGitState, action: Action): gitState => { |
||||||
|
///console.log(action, state)
|
||||||
|
switch (action.type) { |
||||||
|
|
||||||
|
case 'FILE_STATUS': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
fileStatusResult: action.payload, |
||||||
|
staged: getFilesByStatus("staged", action.payload), |
||||||
|
modified: getFilesByStatus("modified", action.payload), |
||||||
|
untracked: getFilesByStatus("untracked", action.payload), |
||||||
|
deleted: getFilesByStatus("deleted", action.payload), |
||||||
|
allchangesnotstaged: allChangedButNotStagedFiles(action.payload) |
||||||
|
} |
||||||
|
|
||||||
|
case 'FILE_STATUS_MERGE': |
||||||
|
const filesStatusResults: fileStatusResult[] = action.payload |
||||||
|
filesStatusResults.map((fileStatusResult: fileStatusResult) => { |
||||||
|
const file = state.fileStatusResult.find( stateFile => { |
||||||
|
return stateFile.filename === fileStatusResult.filename |
||||||
|
}) |
||||||
|
if(file){ |
||||||
|
file.status = fileStatusResult.status |
||||||
|
file.statusNames = fileStatusResult.statusNames |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
return {...state, |
||||||
|
staged: getFilesByStatus("staged", state.fileStatusResult), |
||||||
|
modified: getFilesByStatus("modified", state.fileStatusResult), |
||||||
|
untracked: getFilesByStatus("untracked", state.fileStatusResult), |
||||||
|
deleted: getFilesByStatus("deleted", state.fileStatusResult), |
||||||
|
allchangesnotstaged: allChangedButNotStagedFiles(state.fileStatusResult) |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_COMMITS': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
commits: action.payload, |
||||||
|
localCommitCount: action.payload.length |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_BRANCHES': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
branches: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_CURRENT_BRANCH': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
currentBranch: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_CAN_USE_APP': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
canUseApp: action.payload |
||||||
|
} |
||||||
|
case 'SET_REPO_NAME': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
reponame: action.payload |
||||||
|
} |
||||||
|
case 'SET_LOADING': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
loading: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_REPOS': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
repositories: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_REMOTE_BRANCHES': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
remoteBranches: action.payload |
||||||
|
}
|
||||||
|
|
||||||
|
case 'SET_CAN_COMMIT': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
canCommit: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_REMOTES': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
remotes: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_UPSTREAM': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
upstream: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_COMMIT_CHANGES': |
||||||
|
|
||||||
|
action.payload.forEach((change: commitChange) => { |
||||||
|
state.commitChanges.find((c) => c.hashModified === change.hashModified && c.hashOriginal === change.hashOriginal && c.path === change.path) ? null : state.commitChanges.push(change) |
||||||
|
}) |
||||||
|
|
||||||
|
return { |
||||||
|
...state, |
||||||
|
commitChanges: [...state.commitChanges] |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_BRANCH_COMMITS': |
||||||
|
state.branchCommits[(action as setBranchCommitsAction).payload.branch.name] = (action as setBranchCommitsAction).payload.commits |
||||||
|
return { |
||||||
|
...state, |
||||||
|
branchCommits: {...state.branchCommits} |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_GITHUB_USER': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
gitHubUser: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_RATE_LIMIT': |
||||||
|
console.log("rate limit", action.payload) |
||||||
|
return { |
||||||
|
...state, |
||||||
|
rateLimit: action.payload |
||||||
|
} |
||||||
|
|
||||||
|
case 'SET_GITHUB_ACCESS_TOKEN': |
||||||
|
return { |
||||||
|
...state, |
||||||
|
gitHubAccessToken: action.payload |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
import { defaultLoaderState, loaderState } from "../types"; |
||||||
|
|
||||||
|
interface Action { |
||||||
|
type: string |
||||||
|
payload: any |
||||||
|
} |
||||||
|
|
||||||
|
export const loaderReducer = (state: loaderState = defaultLoaderState, action: Action): loaderState => { |
||||||
|
state[action.type] = action.payload |
||||||
|
return state |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
.nav { |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.pointer { |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.long-and-truncated { |
||||||
|
flex: 1; |
||||||
|
white-space: nowrap; |
||||||
|
overflow: hidden; |
||||||
|
text-overflow: ellipsis; |
||||||
|
} |
||||||
|
|
||||||
|
.commit-navigation{ |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
|
||||||
|
.commit-navigation:hover { |
||||||
|
background-color: var(--custom-select); |
||||||
|
} |
||||||
|
|
||||||
|
.commitdetailsitem { |
||||||
|
align-items: baseline; |
||||||
|
} |
||||||
|
|
||||||
|
.gitfile:hover { |
||||||
|
background-color : var(--custom-select); |
||||||
|
} |
||||||
|
|
||||||
|
hr { |
||||||
|
background-color: var(--custom-select); |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,214 @@ |
|||||||
|
import { Endpoints } from "@octokit/types" |
||||||
|
import { CommitObject, ReadCommitResult } from "isomorphic-git" |
||||||
|
|
||||||
|
export type GitHubUser = Endpoints["GET /user"]["response"]['data'] |
||||||
|
export type RateLimit = Endpoints["GET /rate_limit"]["response"]["data"] |
||||||
|
|
||||||
|
export type gitState = { |
||||||
|
currentBranch: branch |
||||||
|
commits: ReadCommitResult[] |
||||||
|
branch: string |
||||||
|
canCommit: boolean |
||||||
|
branches: branch[] |
||||||
|
remotes: remote[] |
||||||
|
fileStatusResult: fileStatusResult[] |
||||||
|
canUseApp: boolean |
||||||
|
loading: boolean |
||||||
|
storageUsed: any |
||||||
|
reponame: string |
||||||
|
staged: fileStatusResult[] |
||||||
|
untracked: fileStatusResult[] |
||||||
|
deleted: fileStatusResult[] |
||||||
|
modified: fileStatusResult[] |
||||||
|
allchangesnotstaged: fileStatusResult[], |
||||||
|
repositories: repository[] |
||||||
|
remoteBranches: remoteBranch[] |
||||||
|
commitChanges: commitChange[] |
||||||
|
branchCommits: Record<string, ReadCommitResult[]> |
||||||
|
syncStatus: syncStatus, |
||||||
|
localCommitCount: number |
||||||
|
remoteCommitCount: number |
||||||
|
upstream: string |
||||||
|
gitHubUser: GitHubUser |
||||||
|
rateLimit: RateLimit |
||||||
|
gitHubAccessToken: string |
||||||
|
} |
||||||
|
|
||||||
|
export type loaderState = { |
||||||
|
branches: boolean |
||||||
|
remotes: boolean |
||||||
|
commits: boolean |
||||||
|
sourcecontrol: boolean |
||||||
|
} |
||||||
|
|
||||||
|
export type commitChangeTypes = {
|
||||||
|
"deleted": "D" |
||||||
|
"modified": "M" |
||||||
|
"added": "A", |
||||||
|
"unknown": "?" |
||||||
|
} |
||||||
|
|
||||||
|
export enum syncStatus { |
||||||
|
"sync" = "sync", |
||||||
|
"publishBranch" = "publishBranch", |
||||||
|
"none" = "none", |
||||||
|
} |
||||||
|
|
||||||
|
export type commitChangeType = keyof commitChangeTypes |
||||||
|
|
||||||
|
export type commitChange = { |
||||||
|
type: commitChangeType |
||||||
|
path: string, |
||||||
|
hashModified : string, |
||||||
|
hashOriginal : string, |
||||||
|
original?: string, |
||||||
|
modified?: string, |
||||||
|
readonly?: boolean |
||||||
|
} |
||||||
|
|
||||||
|
export type repository = { |
||||||
|
name: string |
||||||
|
html_url: string |
||||||
|
owner: { |
||||||
|
login: string |
||||||
|
}, |
||||||
|
full_name: string |
||||||
|
default_branch: string |
||||||
|
id: number |
||||||
|
} |
||||||
|
|
||||||
|
export type branch = { |
||||||
|
name: string |
||||||
|
remote: remote |
||||||
|
} |
||||||
|
|
||||||
|
export type remote = { |
||||||
|
remote: string |
||||||
|
url: string |
||||||
|
} |
||||||
|
|
||||||
|
export type remoteBranch = { |
||||||
|
name: string |
||||||
|
} |
||||||
|
|
||||||
|
export const defaultGitState: gitState = { |
||||||
|
currentBranch: { name: "", remote: { remote: "", url: "" } }, |
||||||
|
commits: [], |
||||||
|
branch: "", |
||||||
|
canCommit: true, |
||||||
|
branches: [], |
||||||
|
remotes: [], |
||||||
|
fileStatusResult: [], |
||||||
|
staged: [], |
||||||
|
untracked: [], |
||||||
|
deleted: [], |
||||||
|
modified: [], |
||||||
|
allchangesnotstaged: [], |
||||||
|
canUseApp: false, |
||||||
|
loading: false, |
||||||
|
storageUsed: {}, |
||||||
|
reponame: "", |
||||||
|
repositories: [], |
||||||
|
remoteBranches: [], |
||||||
|
commitChanges: [], |
||||||
|
branchCommits: {}, |
||||||
|
syncStatus: syncStatus.none, |
||||||
|
localCommitCount: 0, |
||||||
|
remoteCommitCount: 0, |
||||||
|
upstream: "", |
||||||
|
gitHubUser: {} as GitHubUser, |
||||||
|
rateLimit: {} as RateLimit, |
||||||
|
gitHubAccessToken: "" |
||||||
|
} |
||||||
|
|
||||||
|
export const defaultLoaderState: loaderState = { |
||||||
|
branches: false, |
||||||
|
commits: false, |
||||||
|
sourcecontrol: false, |
||||||
|
remotes: false |
||||||
|
} |
||||||
|
|
||||||
|
export type fileStatusResult = { |
||||||
|
filename:string, |
||||||
|
status?: fileStatus |
||||||
|
statusNames?:string[] |
||||||
|
} |
||||||
|
|
||||||
|
export type fileStatus =[string, 0 | 1, 0 | 1 | 2, 0 | 1 | 2 | 3] |
||||||
|
|
||||||
|
export type statusMatrixType = { matrix: string[] | undefined; status: string[] } |
||||||
|
|
||||||
|
export type sourceControlGroup = { |
||||||
|
group: fileStatusResult[], |
||||||
|
name: string |
||||||
|
} |
||||||
|
|
||||||
|
export interface fileStatusAction { |
||||||
|
type: string, |
||||||
|
payload: fileStatusResult[] |
||||||
|
} |
||||||
|
|
||||||
|
export interface setCommitsAction { |
||||||
|
type: string, |
||||||
|
payload: ReadCommitResult[] |
||||||
|
} |
||||||
|
|
||||||
|
export interface setBranchesAction { |
||||||
|
type: string, |
||||||
|
payload: any[] |
||||||
|
} |
||||||
|
|
||||||
|
export interface setReposAction { |
||||||
|
type: string, |
||||||
|
payload: any[] |
||||||
|
} |
||||||
|
|
||||||
|
export interface setRemoteBranchesAction { |
||||||
|
type: string, |
||||||
|
payload: any[] |
||||||
|
} |
||||||
|
|
||||||
|
export interface setGitHubUserAction { |
||||||
|
type: string, |
||||||
|
payload: any |
||||||
|
} |
||||||
|
|
||||||
|
export interface setLoadingAction { |
||||||
|
type: string, |
||||||
|
payload: boolean |
||||||
|
} |
||||||
|
|
||||||
|
export interface setCanUseAppAction { |
||||||
|
type: string, |
||||||
|
payload: boolean |
||||||
|
} |
||||||
|
|
||||||
|
export interface setRepoNameAction { |
||||||
|
type: string, |
||||||
|
payload: string |
||||||
|
} |
||||||
|
|
||||||
|
export interface setCurrentBranchAction { |
||||||
|
type: string, |
||||||
|
payload: branch |
||||||
|
} |
||||||
|
|
||||||
|
export interface setRemotesAction { |
||||||
|
type: string, |
||||||
|
payload: remote[] |
||||||
|
} |
||||||
|
|
||||||
|
export interface setUpstreamAction { |
||||||
|
type: string, |
||||||
|
payload: string |
||||||
|
} |
||||||
|
|
||||||
|
export interface setBranchCommitsAction { |
||||||
|
type: string, |
||||||
|
payload: { |
||||||
|
branch: branch, |
||||||
|
commits: ReadCommitResult[] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export type gitActionDispatch = setUpstreamAction | setBranchCommitsAction | setRemotesAction | setCurrentBranchAction | fileStatusAction | setLoadingAction | setCanUseAppAction | setRepoNameAction | setCommitsAction | setBranchesAction | setReposAction | setRemoteBranchesAction |
@ -0,0 +1,53 @@ |
|||||||
|
import { StylesConfig } from 'react-select' |
||||||
|
export const selectStyles: StylesConfig = { |
||||||
|
option: (baseStyles, state) => { |
||||||
|
return { |
||||||
|
...baseStyles, |
||||||
|
color: 'var(--text)', |
||||||
|
} |
||||||
|
}, |
||||||
|
input(base, props) { |
||||||
|
return { |
||||||
|
...base, |
||||||
|
color: 'var(--text)', |
||||||
|
} |
||||||
|
}, |
||||||
|
singleValue: (baseStyles, state) => { |
||||||
|
return { |
||||||
|
...baseStyles, |
||||||
|
color: 'var(--text)', |
||||||
|
} |
||||||
|
}, |
||||||
|
control: (baseStyles, state) => ({ |
||||||
|
...baseStyles, |
||||||
|
color: 'var(--text)', |
||||||
|
backgroundColor: 'var(--custom-select)', |
||||||
|
border: 'none', |
||||||
|
}), |
||||||
|
menu: (baseStyles, state) => { |
||||||
|
return { |
||||||
|
...baseStyles, |
||||||
|
backgroundColor: 'var(--custom-select)', |
||||||
|
color: 'var(--text)', |
||||||
|
} |
||||||
|
}, |
||||||
|
menuList: (baseStyles, props) => { |
||||||
|
return { |
||||||
|
...baseStyles, |
||||||
|
backgroundColor: 'var(--custom-select)', |
||||||
|
color: 'var(--text)', |
||||||
|
} |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
export const selectTheme = (theme) => ({ |
||||||
|
...theme, |
||||||
|
borderRadius: 0, |
||||||
|
colors: { |
||||||
|
...theme.colors, |
||||||
|
primary25: 'var(--primary)', |
||||||
|
primary: 'var(--primary)', |
||||||
|
primary50: 'var(--primary)', |
||||||
|
primary75: 'var(--primary)', |
||||||
|
}, |
||||||
|
}) |
@ -0,0 +1,3 @@ |
|||||||
|
export const removeSlash = (s: string) => { |
||||||
|
return s.replace(/^\/+/, ""); |
||||||
|
}; |
Loading…
Reference in new issue