feat(settings): button for access token input

This commit is contained in:
EnixCoda 2018-05-19 14:19:02 +08:00
parent bf0bdf92e8
commit b003f3acc3
4 changed files with 97 additions and 54 deletions

View file

@ -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&nbsp;
<a href="https://developer.chrome.com/apps/storage" target="_blank">
chrome local storage
</a>{' '}
locally and safely.
</a>
&nbsp;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>
&nbsp;for bug report or feature request.
</span>
</div>
</div>
)}

View file

@ -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>
)

View file

@ -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;
}
}
}
}

View file

@ -1,4 +1,5 @@
export const NOT_FOUND = 'Repo Not Found'
export const BAD_CREDENTIALS = 'Bad credentials'
async function request(url, { accessToken } = {}) {
const headers = {}