Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions apps/remix-ide-e2e/src/tests/gist.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,18 @@ module.exports = {
.waitForElementVisible('*[data-shared="tooltipPopup"]', 5000)
.assert.containsText('*[data-shared="tooltipPopup"]', 'Credentials removed')
.click('*[data-id="github-dropdown-toggle"]')
.click('*[data-id="github-dropdown-item-publish-to-gist"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.waitForElementNotPresent('*[data-id="github-dropdown-item-publish-to-gist"]')
// .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
// .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
.perform((done) => {
browser.getText('[data-id="fileSystemModalDialogModalBody-react"]', (result) => {
console.log('result.value: ', result.value)
browser.assert.ok(result.value === 'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.', 'Assert failed. Gist token error message not displayed.')
done()
})
})
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
.click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
// .perform((done) => {
// browser.getText('[data-id="fileSystemModalDialogModalBody-react"]', (result) => {
// console.log('result.value: ', result.value)
// browser.assert.ok(result.value === 'Remix requires an access token (which includes gists creation permission). Please go to the settings tab to create one.', 'Assert failed. Gist token error message not displayed.')
// done()
// })
// })
// .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
// .click('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
},

'Import From Gist For Valid Gist ID #group2': '' + function (browser: NightwatchBrowser) {
Expand Down
10 changes: 9 additions & 1 deletion apps/remix-ide/src/app/plugins/git.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const profile = {
name: 'dgit',
displayName: 'Git',
description: 'Git plugin for Remix',
methods: ['open', 'init'],
methods: ['open', 'init', 'login', 'logOut'],
events: [''],
version: packageJson.version,
maintainedBy: 'Remix',
Expand Down Expand Up @@ -41,4 +41,12 @@ export class GitPlugin extends ViewPlugin {
return <div id='gitTab'><GitUI plugin={this} /></div>
}

async login(){
this.emit('login')
}

async logOut(){
this.emit('disconnect')
}

}
152 changes: 41 additions & 111 deletions libs/remix-ui/git/src/components/github/devicecode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import { sendToMatomo } from "../../lib/pluginActions";
import { gitMatomoEventTypes } from "../../types";
import { endpointUrls } from "@remix-endpoints-helper";
import isElectron from "is-electron";
import { set } from "lodash";
import { use } from "chai";
import { startGitHubLogin, getDeviceCodeFromGitHub, connectWithDeviceCode, disconnectFromGitHub } from "../../lib/gitLoginActions";

export const GetDeviceCode = () => {
export const ConnectToGitHub = () => {
const context = React.useContext(gitPluginContext)
const actions = React.useContext(gitActionsContext)
const pluginActions = React.useContext(pluginActionsContext)
Expand All @@ -21,119 +20,48 @@ export const GetDeviceCode = () => {

const popupRef = useRef<Window | null>(null)

// Dynamically select the GitHub OAuth client ID based on the hostname
const getClientId = async () => {
const host = isElectron() ? 'desktop' : window.location.hostname
// fetch it with axios from `${endpointUrls.gitHubLoginProxy}/client-id?host=${host}`
try {
const response = await axios.get(`${endpointUrls.gitHubLoginProxy}/client/${host}`, {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
return response.data.client_id
}
catch (error) {
throw new Error('Failed to fetch GitHub client ID')
}

}

const openPopupLogin = useCallback(async () => {

if (isElectron()) {
setDesktopIsLoading(true)
pluginActions.loginWithGitHub()
return
}

const clientId = await getClientId()
const redirectUri = `${window.location.origin}/?source=github`
const scope = 'repo gist user:email read:user'

const url = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${encodeURIComponent(scope)}&response_type=code`

const popup = window.open(url, '_blank', 'width=600,height=700')
if (!popup) {
console.warn('Popup blocked or failed to open, falling back to device code flow.')
await getDeviceCodeFromGitHub()
return
}
popupRef.current = popup

const messageListener = async (event: MessageEvent) => {
if (event.origin !== window.location.origin) return

if (event.data.type === 'GITHUB_AUTH_SUCCESS') {
const token = event.data.token
await pluginActions.saveToken(token)
await actions.loadGitHubUserFromToken()
await sendToMatomo(gitMatomoEventTypes.CONNECTTOGITHUBBUTTON)
try {
if (isElectron()) {
setDesktopIsLoading(true)
}
await startGitHubLogin()
if (!isElectron()) {
setAuthorized(true)
await sendToMatomo(gitMatomoEventTypes.CONNECTTOGITHUBSUCCESS)
window.removeEventListener('message', messageListener)
popup?.close()
} else if (event.data.type === 'GITHUB_AUTH_FAILURE') {
}
} catch (error) {
console.error('GitHub login failed:', error)
if (isElectron()) {
setDesktopIsLoading(false)
} else {
setPopupError(true)
window.removeEventListener('message', messageListener)
popup?.close()
// Fallback to device code flow
await handleGetDeviceCode()
}
}
}, [])

window.addEventListener('message', messageListener)
}, [actions, pluginActions])

const getDeviceCodeFromGitHub = async () => {
const handleGetDeviceCode = async () => {
setDesktopIsLoading(false)
setPopupError(false)
await sendToMatomo(gitMatomoEventTypes.GETGITHUBDEVICECODE)
setAuthorized(false)
// Send a POST request
const response = await axios({
method: 'post',
url: `${endpointUrls.github}/login/device/code`,
data: {
client_id: '2795b4e41e7197d6ea11',
scope: 'repo gist user:email read:user'
},
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
});

// convert response to json
const githubrespone = await response.data;

setGitHubResponse(githubrespone)
try {
const githubResponse = await getDeviceCodeFromGitHub()
setGitHubResponse(githubResponse)
} catch (error) {
console.error('Failed to get device code:', error)
await sendToMatomo(gitMatomoEventTypes.CONNECTTOGITHUBFAIL)
}
}

const connectApp = async () => {
await sendToMatomo(gitMatomoEventTypes.CONNECTTOGITHUB)
// poll https://github.com/login/oauth/access_token
const accestokenresponse = await axios({
method: 'post',
url: `${endpointUrls.github}/login/oauth/access_token`,
data: {
client_id: '2795b4e41e7197d6ea11',
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;

if (response.access_token) {
try {
await connectWithDeviceCode(gitHubResponse.device_code)
setAuthorized(true)
await sendToMatomo(gitMatomoEventTypes.CONNECTTOGITHUBSUCCESS)
await pluginActions.saveToken(response.access_token)
await actions.loadGitHubUserFromToken()
} else {
} catch (error) {
console.error('Failed to connect with device code:', error)
await sendToMatomo(gitMatomoEventTypes.CONNECTTOGITHUBFAIL)
}
}
Expand All @@ -144,12 +72,14 @@ export const GetDeviceCode = () => {
}
},[context.gitHubUser])

const disconnect = async () => {
await sendToMatomo(gitMatomoEventTypes.DISCONNECTFROMGITHUB)
setAuthorized(false)
setGitHubResponse(null)
await pluginActions.saveToken(null)
await actions.loadGitHubUserFromToken()
const handleDisconnect = async () => {
try {
await disconnectFromGitHub()
setAuthorized(false)
setGitHubResponse(null)
} catch (error) {
console.error('Failed to disconnect from GitHub:', error)
}
}

return (
Expand All @@ -162,7 +92,7 @@ export const GetDeviceCode = () => {
{popupError && !gitHubResponse && !authorized && (
<div className="alert alert-warning mt-2" role="alert">
GitHub login failed. You can continue using another method.
<button className='btn btn-outline-primary btn-sm mt-2 w-100' onClick={getDeviceCodeFromGitHub}>
<button className='btn btn-outline-primary btn-sm mt-2 w-100' onClick={handleGetDeviceCode}>
Use another method
</button>
</div>
Expand All @@ -171,7 +101,7 @@ export const GetDeviceCode = () => {
<i className="fas fa-spinner fa-spin fa-2x mt-1"></i>
<div className="alert alert-warning mt-2" role="alert">
In case of issues, you can try another method.
<button className='btn btn-outline-primary btn-sm mt-2 w-100' onClick={getDeviceCodeFromGitHub}>
<button className='btn btn-outline-primary btn-sm mt-2 w-100' onClick={handleGetDeviceCode}>
Use another method
</button>
</div>
Expand Down Expand Up @@ -202,7 +132,7 @@ export const GetDeviceCode = () => {
(context.gitHubUser && context.gitHubUser.isConnected) ?
<div className="pt-2">
<button data-id='disconnect-github' className='btn btn-primary mt-1 w-100' onClick={async () => {
disconnect()
handleDisconnect()
}}>Disconnect</button>
</div> : null
}
Expand Down
4 changes: 2 additions & 2 deletions libs/remix-ui/git/src/components/gitui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { RemotesNavigation } from './navigation/remotes'
import { Remotes } from './panels/remotes'
import { GitHubNavigation } from './navigation/github'
import { loaderReducer } from '../state/loaderReducer'
import { GetDeviceCode } from './github/devicecode'
import { ConnectToGitHub } from './github/devicecode'
import { LogNavigation } from './navigation/log'
import LogViewer from './panels/log'
import { SourceControlBase } from './buttons/sourceControlBase'
Expand Down Expand Up @@ -241,7 +241,7 @@ export const GitUI = (props: IGitUi) => {
<GitHubNavigation eventKey={gitUIPanels.GITHUB} activePanel={activePanel} callback={setActivePanel} />
<Accordion.Collapse className='bg-light' eventKey={gitUIPanels.GITHUB}>
<div className="px-2 py-2">
<GetDeviceCode></GetDeviceCode>
<ConnectToGitHub></ConnectToGitHub>
<hr></hr>
<GitHubCredentials></GitHubCredentials>
</div>
Expand Down
Loading