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