Skip to content

Commit

Permalink
feat:Added functionality to retrieve and save GitHub token (hypertron…
Browse files Browse the repository at this point in the history
…s#839)

* feat:Added functionality to retrieve and save GitHub token (hypertrons#812)

Signed-off-by: wblx <904653630@qq.com>

* solved all the problems

Signed-off-by: wblx <904653630@qq.com>

* sync

* Resolve inconsistent margins

Signed-off-by: wblx <904653630@qq.com>

* response problem

Signed-off-by: wblx <904653630@qq.com>

* duplicated options

Signed-off-by: wblx <904653630@qq.com>

* chore: run prettier to format

Signed-off-by: Harry <xiaohanwu12@gmail.com>

---------

Signed-off-by: wblx <904653630@qq.com>
Signed-off-by: Harry <xiaohanwu12@gmail.com>
  • Loading branch information
wblxxx authored and wangyantong2000 committed Aug 13, 2024
1 parent 6ae48d6 commit d40c396
Show file tree
Hide file tree
Showing 9 changed files with 320 additions and 42 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"mini-css-extract-plugin": "^2.7.2",
"mkdirp": "^1.0.4",
"prettier": "^3.3.3",
"pretty-quick": "^3.1.3",
"pretty-quick": "^4.0.0",
"querystring": "^0.2.1",
"sass": "^1.52.1",
"sass-loader": "^12.4.0",
Expand Down
19 changes: 19 additions & 0 deletions src/api/githubApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getToken, saveToken } from '../helpers/github-token';

export const githubRequest = async (endpoint: string, options: RequestInit = {}): Promise<any | null> => {
const token = getToken();
if (!token) {
return null;
}

try {
const response = await fetch(`https://api.github.com${endpoint}`, {
...options,
});
return response.json();
} catch (error) {
return null;
}
};

export { saveToken, getToken };
7 changes: 7 additions & 0 deletions src/helpers/github-token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const saveToken = (token: string) => {
localStorage.setItem('github_token', token);
};

export const getToken = (): string => {
return localStorage.getItem('github_token') || '';
};
26 changes: 25 additions & 1 deletion src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,29 @@
"activity_icon": "activity",
"openrank_icon": "openrank",
"contributors_participants_icon": "contributors and participants",
"merged_lines_icon": "code lines change"
"merged_lines_icon": "code lines change",
"github_token_configuration": "GitHub Token Configuration",
"github_token_tooltip": "Enter your GitHub Token here for authentication.",
"github_token_description": "Providing a GitHub Token ensures that HyperCRX can securely and effectively access and operate on your GitHub data for personalized analysis.",
"github_token_how_to_generate": "How to generate a GitHub Token?",
"github_token_step1": "1. Log in to GitHub and go to Settings.",
"github_token_step2": "2. Select Developer settings.",
"github_token_step3": "3. Choose Personal access tokens, click Tokens(classic), then Generate a personal access token.",
"github_token_step4": "4. Set token details:",
"github_token_note": "Note",
"github_token_note_description": "A descriptive name, e.g., \"HyperCrx Token\".",
"github_token_expiration": "Expiration",
"github_token_expiration_description": "Choose the validity period.",
"github_token_scopes": "Scopes",
"github_token_scopes_description": "Select permission scopes, such as repo and workflow.",
"github_token_step5": "5. Click the Generate token button.",
"github_token_step6": "6. Copy the generated token and paste it into the input box below.",
"github_token_placeholder": "GitHub Token",
"github_token_save": "Save",
"github_token_edit": "Edit",
"github_token_test": "Test Token",
"github_token_error_empty": "Token cannot be empty",
"github_token_success_save": "Token saved successfully",
"github_token_success_valid": "Token is valid. Username: {{username}}",
"github_token_error_invalid": "Invalid token or request failed"
}
26 changes: 25 additions & 1 deletion src/locales/zh_CN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,29 @@
"activity_icon": "activity数",
"openrank_icon": "openrank值",
"contributors_participants_icon": "contributors和participants数",
"merged_lines_icon": "代码变化量"
"merged_lines_icon": "代码变化量",
"github_token_configuration": "GitHub 令牌配置",
"github_token_tooltip": "在此输入您的 GitHub 令牌以进行身份验证。",
"github_token_description": "提供 GitHub 令牌可确保 HyperCRX 能够安全有效地访问和操作您的 GitHub 数据以进行个性化分析。",
"github_token_how_to_generate": "如何生成 GitHub 令牌?",
"github_token_step1": "1. 登录 GitHub 并进入设置。",
"github_token_step2": "2. 选择开发者设置。",
"github_token_step3": "3. 选择个人访问令牌,Tokens(classic),然后生成个人访问令牌。",
"github_token_step4": "4. 设置令牌详细信息:",
"github_token_note": "注意",
"github_token_note_description": "描述性名称,例如“HyperCrx Token",
"github_token_expiration": "过期时间",
"github_token_expiration_description": "选择有效期。",
"github_token_scopes": "权限范围",
"github_token_scopes_description": "选择权限范围,例如 repo 和 workflow。",
"github_token_step5": "5. 点击生成令牌按钮。",
"github_token_step6": "6. 复制生成的令牌并将其粘贴到下面的输入框中。",
"github_token_placeholder": "GitHub 令牌",
"github_token_save": "保存",
"github_token_edit": "编辑",
"github_token_test": "测试令牌",
"github_token_error_empty": "令牌不能为空",
"github_token_success_save": "令牌保存成功",
"github_token_success_valid": "令牌有效。用户名:{{username}}",
"github_token_error_invalid": "令牌无效或请求失败"
}
56 changes: 56 additions & 0 deletions src/pages/Options/Options.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,59 @@ li {
a {
color: blue;
}

.github-token-options {
width: 75vw;
min-width: 350px;
max-width: 600px;
background-color: white;
border: 1px solid #e1e4e8;
border-radius: 6px;
margin-top: -10px;
}

.github-token-options .Box-header {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
padding: 0 25px;
margin: -1px -1px 0;
background-color: #242a2e;
color: white;
border: 1px solid #e1e4e8;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
}

.github-token-options .Box-title {
font-size: 18px;
font-weight: 600;
margin-right: 8px;
}

.github-token-options p {
padding: 0 25px;
}

.github-token-options input {
width: calc(100% - 50px);
padding: 8px;
margin: 10px 25px;
border: 1px solid #ccc;
border-radius: 4px;
}

.github-token-options button {
padding: 8px 16px;
margin: 0 25px 20px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}

.github-token-options button:hover {
background-color: #0056b3;
}
22 changes: 17 additions & 5 deletions src/pages/Options/Options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import TooltipTrigger from '../../components/TooltipTrigger';
import './Options.css';
import { useTranslation } from 'react-i18next';
import '../../helpers/i18n';
import GitHubToken from './components/GitHubToken';

const stacksStyleOptions = {
headerStack: {
paddingBottom: '10px',
Expand All @@ -17,6 +19,9 @@ const stacksStyleOptions = {
settingStack: {
margin: '10px 25px',
},
tokenStack: {
margin: '10px 25px',
},
};

const Options = (): JSX.Element => {
Expand Down Expand Up @@ -64,11 +69,7 @@ const Options = (): JSX.Element => {
</Space>
</Row>

<Row
justify="center"
style={stacksStyleOptions.mainStack}
gutter={[30, 30]} // 设置间距
>
<Row justify="center" style={stacksStyleOptions.mainStack} gutter={[30, 30]}>
<Col
span={24}
style={{
Expand Down Expand Up @@ -146,6 +147,17 @@ const Options = (): JSX.Element => {
</div>
</div>
</Col>
<Col
span={24}
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
margin: stacksStyleOptions.tokenStack.margin,
}}
>
<GitHubToken /> {/* Add GitHubToken component */}
</Col>
</Row>
</Space>
</div>
Expand Down
134 changes: 134 additions & 0 deletions src/pages/Options/components/GitHubToken.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import React, { useState, useEffect, useRef } from 'react';
import TooltipTrigger from '../../../components/TooltipTrigger';
import { saveToken, getToken, githubRequest } from '../../../api/githubApi';
import { message } from 'antd';
import { useTranslation } from 'react-i18next';

const GitHubToken = () => {
const [token, setToken] = useState('');
const [isCollapsed, setIsCollapsed] = useState(true);
const [isEditing, setIsEditing] = useState(false);
const { t } = useTranslation();
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
const storedToken = getToken();
if (storedToken) {
setToken(storedToken);
}
}, []);

const handleSave = () => {
if (!token.trim()) {
showMessage(t('github_token_error_empty'), 'error');
return;
}
saveToken(token);
showMessage(t('github_token_success_save'), 'success');
setIsEditing(false);
};

const handleEdit = () => {
setIsEditing(true);
};

const handleTestToken = async () => {
const userData = await githubRequest('/user', {
headers: { Authorization: `Bearer ${token}` },
});

if (userData === null || userData.message) {
showMessage(t('github_token_error_invalid'), 'error');
} else {
showMessage(t('github_token_success_valid', { username: userData.login }), 'success');
}
};

const obfuscateToken = (token: string): string => {
if (token.length <= 4) return token;
return `${token[0]}${'*'.repeat(token.length - 2)}${token[token.length - 1]}`;
};

const showMessage = (content: string, type: 'success' | 'error') => {
if (inputRef.current) {
const rect = inputRef.current.getBoundingClientRect();
message.config({
top: rect.top - 50,
duration: 2,
maxCount: 3,
});
message[type](content);
}
};

return (
<div className="github-token-options Box">
<div className="Box-header">
<h2 className="Box-title">{t('github_token_configuration')}</h2>
<TooltipTrigger content={t('github_token_tooltip')} />
</div>
<p>{t('github_token_description')}</p>
<div className="collapsible-section">
<div
className="collapsible-header"
onClick={() => setIsCollapsed(!isCollapsed)}
style={{ cursor: 'pointer', display: 'flex', alignItems: 'center' }}
>
<p>{t('github_token_how_to_generate')}</p>
<span style={{ marginLeft: '5px' }}>{isCollapsed ? '▶' : '▼'}</span>
</div>
{!isCollapsed && (
<div className="instructions Box-body">
<ol>
<li>{t('github_token_step1')}</li>
<li>{t('github_token_step2')}</li>
<li>{t('github_token_step3')}</li>
<li>
{t('github_token_step4')}
<ul>
<li>
<strong>{t('github_token_note')}</strong>: {t('github_token_note_description')}
</li>
<li>
<strong>{t('github_token_expiration')}</strong>: {t('github_token_expiration_description')}
</li>
<li>
<strong>{t('github_token_scopes')}</strong>: {t('github_token_scopes_description')}
</li>
</ul>
</li>
<li>{t('github_token_step5')}</li>
<li>{t('github_token_step6')}</li>
</ol>
</div>
)}
</div>
<div style={{ marginBottom: '10px' }} id="message-container"></div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<input
type="text"
ref={inputRef}
value={isEditing ? token : obfuscateToken(token)}
onChange={(e) => setToken(e.target.value)}
placeholder={t('github_token_placeholder')}
style={{ marginRight: '10px', flex: 1 }}
disabled={!isEditing}
/>
{isEditing ? (
<button onClick={handleSave} style={{ marginRight: '10px', marginTop: '17px' }}>
{t('github_token_save')}
</button>
) : (
<button onClick={handleEdit} style={{ marginRight: '10px', marginTop: '17px' }}>
{t('github_token_edit')}
</button>
)}
<button onClick={handleTestToken} style={{ marginTop: '17px' }}>
{t('github_token_test')}
</button>
</div>
</div>
);
};

export default GitHubToken;
Loading

0 comments on commit d40c396

Please sign in to comment.