From e1f971a3606a0c196b421b2c82b99b4653c6c2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E9=9B=BE=E4=B8=89=E8=AF=AD?= <32354856+baiwusanyu-c@users.noreply.github.com> Date: Thu, 13 Apr 2023 22:12:07 +0800 Subject: [PATCH] feat: added option page login modal --- packages/extension/assets/styles/login.css | 12 +- .../chrome-option/components/Header-Bar.tsx | 124 +++++++++++++++++- .../chrome-option/components/Repo-List.tsx | 8 +- .../components/pr-list/Pr-List.tsx | 2 +- .../chrome-option/view/OptionPage.tsx | 19 ++- 5 files changed, 150 insertions(+), 15 deletions(-) diff --git a/packages/extension/assets/styles/login.css b/packages/extension/assets/styles/login.css index 639d4e6..4872c2d 100644 --- a/packages/extension/assets/styles/login.css +++ b/packages/extension/assets/styles/login.css @@ -1,4 +1,14 @@ .login { background-repeat: no-repeat; background-size: cover; -} \ No newline at end of file +} + +.login-modal .ant-modal-content{ + background-repeat: no-repeat; + background-size: cover; + background-image: url("../../assets/img/login-bg.png"); +} + +.login-modal .ant-modal-header{ + background-color: transparent; +} diff --git a/packages/extension/chrome-option/components/Header-Bar.tsx b/packages/extension/chrome-option/components/Header-Bar.tsx index b6287c6..8d39159 100644 --- a/packages/extension/chrome-option/components/Header-Bar.tsx +++ b/packages/extension/chrome-option/components/Header-Bar.tsx @@ -1,16 +1,21 @@ -import { GithubOutlined, UserOutlined } from '@ant-design/icons' -import { Avatar } from 'antd' -import { useEffect, useState } from 'react' +import { GithubOutlined, PoweroffOutlined, UserOutlined } from '@ant-design/icons' +import { Avatar, Button, Form, Input, Modal, Popconfirm, Spin } from 'antd' +import { useCallback, useEffect, useState } from 'react' import { useMount } from 'ahooks' +import { getUserInfo } from '@pr-checker/fetchGit' +import { useStorage } from '../../hooks/use-storage' import { CarbonSun } from './Icon-Sun' import { CarbonMoon } from './Icon-Moon' import type { IRepoWithPRs } from './Repo-List' +import '../../assets/styles/login.css' +const logoImg = new URL('../../assets/img/logo.png', import.meta.url).href interface HeaderBarProps { userInfo: { avatar_url: string html_url: string } repoInfo: IRepoWithPRs + isLogin?: boolean } export const HeaderBar = (props: HeaderBarProps = { userInfo: { @@ -41,6 +46,50 @@ export const HeaderBar = (props: HeaderBarProps = { const htmlEl = document.querySelector('html') as Element htmlEl.className = dark ? 'dark' : '' }, [dark]) + + // TODO refactor with popup + const [isModalOpen, setIsModalOpen] = useState(false) + const [passwordVisible, setPasswordVisible] = useState(false) + const [opType, setOpType] = useState('') + const { setItem, CACHE_KEYS, getItem, removeItem } = useStorage() + + const [loading, setLoading] = useState(false) + const getUserData = useCallback(async(token: string) => { + setLoading(true) + const res = await getUserInfo(token) + await setItem(CACHE_KEYS.USER_INFO, JSON.stringify(res)) + setLoading(false) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + const onFinish = useCallback(async(values) => { + const userInfo = await getItem(CACHE_KEYS.USER_INFO) + if (!userInfo) + await getUserData(values.token) + + // 存储操作类型和 TOKEN + await Promise.all([ + setItem(CACHE_KEYS.OP_TYPE, opType), + setItem(CACHE_KEYS.TOKEN, values.token), + ]) + setIsModalOpen(false) + setTimeout(() => { + location.reload() + }, 500) + }, [CACHE_KEYS.OP_TYPE, CACHE_KEYS.TOKEN, CACHE_KEYS.USER_INFO, opType, setItem, getItem, getUserData]) + + const logout = useCallback(async() => { + // 存储操作类型和 TOKEN + await Promise.all([ + removeItem(CACHE_KEYS.OP_TYPE), + removeItem(CACHE_KEYS.TOKEN), + removeItem(CACHE_KEYS.USER_INFO), + ]) + setIsModalOpen(false) + setTimeout(() => { + location.reload() + }, 500) + }, [CACHE_KEYS.OP_TYPE, CACHE_KEYS.TOKEN, setItem]) return (
@@ -62,7 +111,76 @@ export const HeaderBar = (props: HeaderBarProps = { dark ? toggleTheme(false)} /> : toggleTheme(true)} /> } + { + !props.isLogin ? + : + + + }
+ {/* login modal */} + +
+ + pr-checker + +

+ pr-checker +

+
+
+ } + wrapClassName="login-modal" + onCancel={() => setIsModalOpen(false)} + open={isModalOpen} + footer={null} + > + +
+ + + + +
+ + +
+
+
+
+ ) } diff --git a/packages/extension/chrome-option/components/Repo-List.tsx b/packages/extension/chrome-option/components/Repo-List.tsx index e7a8983..feba6fa 100644 --- a/packages/extension/chrome-option/components/Repo-List.tsx +++ b/packages/extension/chrome-option/components/Repo-List.tsx @@ -26,8 +26,8 @@ export const RepoList = (props: IRepoListProps) => { const [loading, setLoading] = useState(false) const { token, userName, opType, onSelect } = props useEffect(() => { - setLoading(true) - if (opType === 'rebase') { + token && setLoading(true) + if (opType === 'rebase' && token) { getIssuesPR(token, userName) .then((res) => { // 使用 map 避免重复遍历 @@ -56,7 +56,7 @@ export const RepoList = (props: IRepoListProps) => { }) } // merge 模式 我们只获取仓库信息 - if (opType === 'merge') { + if (opType === 'merge' && token) { getAllRepo(token).then((res) => { const hasIssuesRepo = res.filter(val => val.open_issues_count > 0 && !val.fork) const repos = new Map() @@ -122,7 +122,7 @@ export const RepoList = (props: IRepoListProps) => {
    { /> diff --git a/packages/extension/chrome-option/view/OptionPage.tsx b/packages/extension/chrome-option/view/OptionPage.tsx index 51a6e4f..a96b7c0 100644 --- a/packages/extension/chrome-option/view/OptionPage.tsx +++ b/packages/extension/chrome-option/view/OptionPage.tsx @@ -1,5 +1,6 @@ import { Layout } from 'antd' import { useEffect, useState } from 'react' +import { isEmptyObj } from '@pr-checker/utils/common' import { getAllStorageSyncData } from '../../hooks/use-storage' import { RepoList } from '../components/Repo-List' import { HeaderBar } from '../components/Header-Bar' @@ -9,19 +10,25 @@ import type { IRepoWithPRs } from '../components/Repo-List' const logoImg = new URL('../../assets/img/logo.png', import.meta.url).href const { Header, Sider, Content } = Layout -// TODO: token is empty ? export const OptionPage = () => { const [storeData, setStoreData] = useState>({ USER_INFO: { login: '', }, }) + + const [isLogin, setIsLogin] = useState(false) useEffect(() => { const run = async() => { - // const data = await getAllStorageSyncData() - const data = { OP_TYPE: 'merge', TOKEN: '', USER_INFO: '{"login":"baiwusanyu-c", "avatar_url": "https://avatars.githubusercontent.com/u/32354856?v=4"}' } - data.USER_INFO = JSON.parse(data.USER_INFO) - setStoreData(data as Record) + const data = await getAllStorageSyncData() + if (!isEmptyObj(data)) { + // const data = { OP_TYPE: 'merge', TOKEN: '', USER_INFO: '{"login":"baiwusanyu-c", "avatar_url": "https://avatars.githubusercontent.com/u/32354856?v=4"}' } + data.USER_INFO = JSON.parse(data.USER_INFO) + setStoreData(data as Record) + setIsLogin(true) + } else { + setIsLogin(false) + } } run() }, []) @@ -60,7 +67,7 @@ export const OptionPage = () => {
    - +
    {storeData.OP_TYPE === 'rebase'