mirror of
https://github.com/EnixCoda/Gitako.git
synced 2026-03-11 08:54:44 +00:00
feat(settings): button for access token input
This commit is contained in:
parent
bf0bdf92e8
commit
b003f3acc3
4 changed files with 97 additions and 54 deletions
|
|
@ -9,30 +9,48 @@ const ACCESS_TOKEN_REGEXP = /^[0-9a-f]{40}$/
|
|||
export default class SettingsBar extends preact.Component {
|
||||
state = {
|
||||
hint: null,
|
||||
tokenCleared: false,
|
||||
hasAccessToken: false,
|
||||
accessToken: '',
|
||||
}
|
||||
|
||||
handleAccessTokenChange = event => {
|
||||
const value = event.target.value
|
||||
componentWillMount() {
|
||||
const { hasAccessToken } = this.props
|
||||
if (value === '') {
|
||||
if (hasAccessToken) {
|
||||
storageHelper.setAccessToken('')
|
||||
this.setState({ tokenCleared: true })
|
||||
} else {
|
||||
this.setState({ hint: '' })
|
||||
}
|
||||
} else if (ACCESS_TOKEN_REGEXP.test(value)) {
|
||||
storageHelper.setAccessToken(value)
|
||||
this.setState({ hint: 'Your token is saved, refresh the page to make it work!' })
|
||||
} else {
|
||||
this.setState({ hint: 'Invalid token' })
|
||||
this.setState({ hasAccessToken })
|
||||
}
|
||||
|
||||
componentWillReceiveProps({ hasAccessToken }) {
|
||||
this.setState({ hasAccessToken })
|
||||
}
|
||||
|
||||
onInputAccessToken = event => {
|
||||
const value = event.target.value
|
||||
this.setState({ accessToken: value })
|
||||
this.setState({
|
||||
hint: ACCESS_TOKEN_REGEXP.test(value)
|
||||
? ''
|
||||
: 'This token is in unknown format.'
|
||||
})
|
||||
}
|
||||
|
||||
saveToken = () => {
|
||||
const { accessToken } = this.state
|
||||
if (accessToken) {
|
||||
storageHelper.setAccessToken(accessToken)
|
||||
this.setState({
|
||||
hasAccessToken: true,
|
||||
hint: 'Your token is saved, will work after reloading the page!',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
clearToken = () => {
|
||||
storageHelper.setAccessToken('')
|
||||
this.setState({ accessToken: '', hasAccessToken: false })
|
||||
}
|
||||
|
||||
render() {
|
||||
const { hint, tokenCleared } = this.state
|
||||
const { toggleShowSettings, activated, hasAccessToken } = this.props
|
||||
const { hint, accessToken, hasAccessToken } = this.state
|
||||
const { toggleShowSettings, activated} = this.props
|
||||
return (
|
||||
<div className={'gitako-settings-bar'}>
|
||||
<div className={'placeholder-row'}>
|
||||
|
|
@ -47,31 +65,39 @@ export default class SettingsBar extends preact.Component {
|
|||
<div className={'gitako-settings-bar-content-section access-token'}>
|
||||
<h4>Access Token</h4>
|
||||
<span>
|
||||
With access token, Gitako will be able to browse your private repositories with no
|
||||
API request time limit.
|
||||
With access token provided, Gitako can access more repositories.
|
||||
</span>
|
||||
<br />
|
||||
<a href="https://github.com/blog/1509-personal-api-tokens" target="_blank">
|
||||
How to create access token?
|
||||
Help: how to create access token?
|
||||
</a>
|
||||
<br />
|
||||
<span>
|
||||
Gitako stores the token in{' '}
|
||||
Gitako stores the token in
|
||||
<a href="https://developer.chrome.com/apps/storage" target="_blank">
|
||||
chrome local storage
|
||||
</a>{' '}
|
||||
locally and safely.
|
||||
</a>
|
||||
locally and safely.
|
||||
</span>
|
||||
<br />
|
||||
<input
|
||||
className={'access-token-input form-control'}
|
||||
placeholder={
|
||||
<div className={'access-token-input-control'}>
|
||||
<input
|
||||
className={'access-token-input form-control'}
|
||||
disabled={hasAccessToken}
|
||||
placeholder={
|
||||
hasAccessToken
|
||||
? 'Your token is saved'
|
||||
: 'Input your token here'
|
||||
}
|
||||
value={accessToken}
|
||||
onInput={this.onInputAccessToken}
|
||||
/>
|
||||
{
|
||||
hasAccessToken
|
||||
? tokenCleared ? 'Your token is cleared' : 'Your token is saved'
|
||||
: 'Input your token here'
|
||||
? <button className={'btn'} onClick={this.clearToken}>Clear</button>
|
||||
: <button className={'btn'} onClick={this.saveToken}>Save</button>
|
||||
}
|
||||
onInput={this.handleAccessTokenChange}
|
||||
/>
|
||||
</div>
|
||||
{hint && <span className={'hint'}>{hint}</span>}
|
||||
</div>
|
||||
<div className={'gitako-settings-bar-content-section position'}>
|
||||
|
|
@ -89,11 +115,12 @@ export default class SettingsBar extends preact.Component {
|
|||
</div>
|
||||
<div className={'gitako-settings-bar-content-section issue'}>
|
||||
<h4>Issue</h4>
|
||||
<a href="https://github.com/EnixCoda/Gitako/issues" target="_blank">
|
||||
Draft a issue on Github.
|
||||
</a>
|
||||
<br />
|
||||
<span>Report BUG or request feature.</span>
|
||||
<span>
|
||||
<a href="https://github.com/EnixCoda/Gitako/issues" target="_blank">
|
||||
Draft a issue on Github
|
||||
</a>
|
||||
for bug report or feature request.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import SettingsBar from './SettingsBar'
|
|||
|
||||
import cx from '../utils/cx'
|
||||
import DOMHelper, { REPO_TYPE_PRIVATE } from '../utils/DOMHelper'
|
||||
import GitHubHelper, { NOT_FOUND } from '../utils/GitHubHelper'
|
||||
import GitHubHelper, { NOT_FOUND, BAD_CREDENTIALS } from '../utils/GitHubHelper'
|
||||
import storageHelper from '../utils/storageHelper'
|
||||
import URLHelper from '../utils/URLHelper'
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ export default class SideBar extends preact.Component {
|
|||
// whether pending for network request
|
||||
loading: true,
|
||||
// whether failed loading the repo due to it is private
|
||||
errorDueToPrivateRepo: false,
|
||||
errorDueToAuth: false,
|
||||
// got access token for GitHub
|
||||
hasAccessToken: null,
|
||||
// meta data for the repository
|
||||
|
|
@ -37,7 +37,7 @@ export default class SideBar extends preact.Component {
|
|||
this.setState({ metaData: metaDataFromUrl })
|
||||
DOMHelper.decorateGitHubPageContent()
|
||||
const accessToken = await storageHelper.getAccessToken()
|
||||
this.setState({ hasAccessToken: !!accessToken })
|
||||
this.setState({ hasAccessToken: Boolean(accessToken) })
|
||||
const metaDataFromAPI = await GitHubHelper.getRepoMeta({ ...metaDataFromUrl, accessToken })
|
||||
const branchName = metaDataFromUrl.branchName || metaDataFromAPI['default_branch']
|
||||
const metaData = { ...metaDataFromUrl, branchName, api: metaDataFromAPI }
|
||||
|
|
@ -51,14 +51,14 @@ export default class SideBar extends preact.Component {
|
|||
window.addEventListener('pjax:complete', this.onPJAXEnd)
|
||||
} catch (err) {
|
||||
// TODO: detect request time exceeds limit
|
||||
if (err.message === NOT_FOUND) {
|
||||
if (err.message === NOT_FOUND || err.message === BAD_CREDENTIALS) {
|
||||
const repoPageType = await DOMHelper.getRepoPageType()
|
||||
const errorDueToPrivateRepo = repoPageType === REPO_TYPE_PRIVATE
|
||||
const errorDueToAuth = repoPageType === REPO_TYPE_PRIVATE || err.message === BAD_CREDENTIALS
|
||||
this.setState({
|
||||
showSettings: repoPageType !== null,
|
||||
errorDueToPrivateRepo,
|
||||
errorDueToAuth,
|
||||
})
|
||||
this.setShouldShow(errorDueToPrivateRepo)
|
||||
this.setShouldShow(errorDueToAuth)
|
||||
} else {
|
||||
console.error(err)
|
||||
this.setShouldShow(false)
|
||||
|
|
@ -100,24 +100,24 @@ export default class SideBar extends preact.Component {
|
|||
this.setState({ showSettings: !showSettings })
|
||||
}
|
||||
|
||||
renderPrivateRepoError() {
|
||||
renderAccessDeniedError() {
|
||||
return (
|
||||
<div className={'description'}>
|
||||
<h5>Access Denied</h5>
|
||||
<p>
|
||||
Gitako need access token with proper scopes (recommended: repo) to read this repository's
|
||||
data.
|
||||
Gitako needs access token with proper scopes (recommended: repo) to access this repository.
|
||||
Please save it in the settings below.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
const { errorDueToPrivateRepo, metaData, treeData, showSettings } = this.state
|
||||
const { errorDueToAuth, metaData, treeData, showSettings } = this.state
|
||||
return (
|
||||
<div className={'gitako-side-bar-content'}>
|
||||
{metaData && <MetaBar metaData={metaData} />}
|
||||
{errorDueToPrivateRepo && this.renderPrivateRepoError()}
|
||||
{errorDueToAuth && this.renderAccessDeniedError()}
|
||||
{metaData && treeData && <FileExplorer metaData={metaData} treeData={treeData} freeze={showSettings} />}
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@
|
|||
}
|
||||
|
||||
.action-icon {
|
||||
color: #0366d6;
|
||||
color: #0366d6d0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,6 +156,7 @@
|
|||
.meta-bar {
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
padding-right: 30px;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
color: #586069;
|
||||
|
|
@ -247,7 +248,6 @@
|
|||
|
||||
.@{name}-settings-bar {
|
||||
border-top: 1px solid #eaecef;
|
||||
box-shadow: 0px -2px 16px #666666dd;
|
||||
&-content {
|
||||
padding: 0 10px;
|
||||
&-section {
|
||||
|
|
@ -256,11 +256,21 @@
|
|||
}
|
||||
.access-token {
|
||||
border-bottom: none; // prevent overwrite by github style
|
||||
.hint {
|
||||
color: #6a737d;
|
||||
}
|
||||
}
|
||||
.access-token-input {
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
box-shadow: none;
|
||||
.access-token-input-control {
|
||||
display: flex;
|
||||
margin-top: 8px;
|
||||
.access-token-input {
|
||||
flex: 1;
|
||||
box-shadow: none;
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
.placeholder-row {
|
||||
display: flex;
|
||||
|
|
@ -268,11 +278,16 @@
|
|||
align-items: center;
|
||||
padding: 6px 10px;
|
||||
|
||||
.settings-icon {
|
||||
.show-settings-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.hide-settings-icon {
|
||||
width: 15px;
|
||||
height: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
export const NOT_FOUND = 'Repo Not Found'
|
||||
export const BAD_CREDENTIALS = 'Bad credentials'
|
||||
|
||||
async function request(url, { accessToken } = {}) {
|
||||
const headers = {}
|
||||
|
|
|
|||
Loading…
Reference in a new issue