diff --git a/desktop/renderer-app/src/api-middleware/flatServer/constants.ts b/desktop/renderer-app/src/api-middleware/flatServer/constants.ts index 2134401675e..c48886fe995 100644 --- a/desktop/renderer-app/src/api-middleware/flatServer/constants.ts +++ b/desktop/renderer-app/src/api-middleware/flatServer/constants.ts @@ -13,6 +13,7 @@ export const FLAT_SERVER_LOGIN = { } as const; export const FLAT_SERVER_USER_BINDING = { + GITHUB_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/login/github/callback/binding`, WECHAT_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/user/binding/platform/wechat/web`, } as const; diff --git a/desktop/renderer-app/src/pages/LoginPage/githubLogin.ts b/desktop/renderer-app/src/pages/LoginPage/githubLogin.ts index bc29d040332..6271e703ad8 100644 --- a/desktop/renderer-app/src/pages/LoginPage/githubLogin.ts +++ b/desktop/renderer-app/src/pages/LoginPage/githubLogin.ts @@ -10,11 +10,6 @@ export const githubLogin: LoginExecutor = onSuccess => { let timer = NaN; const authUUID = uuidv4(); - function getGithubURL(authUUID: string): string { - const redirectURL = FLAT_SERVER_LOGIN.GITHUB_CALLBACK; - return `https://github.com/login/oauth/authorize?client_id=${GITHUB.CLIENT_ID}&redirect_uri=${redirectURL}&state=${authUUID}`; - } - void (async () => { try { await setAuthUUID(authUUID); @@ -22,7 +17,7 @@ export const githubLogin: LoginExecutor = onSuccess => { errorTips(err); } - void shell.openExternal(getGithubURL(authUUID)); + void shell.openExternal(getGithubURL(authUUID, FLAT_SERVER_LOGIN.GITHUB_CALLBACK)); const githubLoginProcessRequest = async (): Promise => { try { @@ -46,3 +41,8 @@ export const githubLogin: LoginExecutor = onSuccess => { window.clearTimeout(timer); }; }; + +export function getGithubURL(authUUID: string, redirect_uri: string): string { + const redirectURL = encodeURIComponent(redirect_uri); + return `https://github.com/login/oauth/authorize?client_id=${GITHUB.CLIENT_ID}&redirect_uri=${redirectURL}&state=${authUUID}`; +} diff --git a/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/binding/GitHub.tsx b/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/binding/GitHub.tsx new file mode 100644 index 00000000000..a3fcf68497f --- /dev/null +++ b/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/binding/GitHub.tsx @@ -0,0 +1,93 @@ +import { shell } from "electron"; +import { v4 } from "uuid"; +import classNames from "classnames"; +import React, { useCallback, useEffect, useState } from "react"; +import { message, Modal } from "antd"; +import { useTranslation } from "react-i18next"; +import { GithubFilled } from "@ant-design/icons"; + +import { GlobalStore } from "../../../../stores/global-store"; +import { useSafePromise } from "../../../../utils/hooks/lifecycle"; +import { + bindingProcess, + LoginPlatform, + removeBinding, + setBindingAuthUUID, +} from "../../../../api-middleware/flatServer"; +import { FLAT_SERVER_USER_BINDING } from "../../../../api-middleware/flatServer/constants"; +import { getGithubURL } from "../../../LoginPage/githubLogin"; +import { errorTips } from "../../../../components/Tips/ErrorTips"; + +export interface BindGitHubProps { + isBind: boolean; + onRefresh: () => void; + globalStore: GlobalStore; +} + +export const BindGitHub: React.FC = ({ isBind, onRefresh, globalStore }) => { + const sp = useSafePromise(); + const { t } = useTranslation(); + const [authUUID, setAuthUUID] = useState(""); + + const cancel = useCallback((): void => { + setAuthUUID(""); + onRefresh(); + }, [onRefresh]); + + useEffect(() => { + let timer = NaN; + async function waitUntilBindFinish(): Promise { + const result = await sp(bindingProcess(authUUID)); + if (result.processing) { + timer = window.setTimeout(waitUntilBindFinish, 2000); + } else { + if (!result.status) { + message.info(t("bind-wechat-failed")); + } + cancel(); + } + } + if (authUUID) { + waitUntilBindFinish(); + return () => { + Number.isNaN(timer) || window.clearTimeout(timer); + }; + } + return; + }, [authUUID, cancel, onRefresh, sp, t]); + + const bindGitHub = async (): Promise => { + const authUUID = v4(); + setAuthUUID(authUUID); + await sp(setBindingAuthUUID(authUUID)); + void shell.openExternal(getGithubURL(authUUID, FLAT_SERVER_USER_BINDING.GITHUB_CALLBACK)); + }; + + const unbind = (): void => { + Modal.confirm({ + content: t("unbind-confirm"), + onOk: async () => { + try { + const { token } = await sp(removeBinding(LoginPlatform.Github)); + globalStore.updateUserToken(token); + onRefresh(); + message.info(t("unbind-success")); + } catch (err) { + errorTips(err); + } + }, + }); + }; + + return ( + + + + ); +}; diff --git a/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/binding/WeChat.tsx b/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/binding/WeChat.tsx index 1490ffb44b0..69303fd8e4a 100644 --- a/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/binding/WeChat.tsx +++ b/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/binding/WeChat.tsx @@ -23,7 +23,7 @@ export interface BindingWeChatProps { globalStore: GlobalStore; } -export const BindingWeChat: React.FC = ({ isBind, onRefresh, globalStore }) => { +export const BindWeChat: React.FC = ({ isBind, onRefresh, globalStore }) => { const sp = useSafePromise(); const { t } = useTranslation(); const [authUUID, setAuthUUID] = useState(""); diff --git a/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/index.tsx b/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/index.tsx index 9a827ffaff0..c9b14971151 100644 --- a/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/index.tsx +++ b/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/index.tsx @@ -14,7 +14,8 @@ import { loginCheck, rename } from "../../../api-middleware/flatServer"; import { ConfirmButtons } from "./ConfirmButtons"; import { UploadAvatar, uploadAvatar } from "./UploadAvatar"; import { useBindingList } from "./binding"; -import { BindingWeChat } from "./binding/WeChat"; +import { BindWeChat } from "./binding/WeChat"; +import { BindGitHub } from "./binding/GitHub"; enum SelectLanguage { Chinese, @@ -101,11 +102,16 @@ export const GeneralSettingPage = observer(function GeneralSettingPage() {
- +
diff --git a/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/style.less b/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/style.less index 550b01bd6f5..166cdc564ff 100644 --- a/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/style.less +++ b/desktop/renderer-app/src/pages/UserSettingPage/GeneralSettingPage/style.less @@ -124,6 +124,7 @@ width: 24px; height: 24px; border-radius: 50%; + margin-right: 8px; cursor: pointer; &.is-bind { @@ -131,6 +132,12 @@ } } +.binding-github:extend(.binding-wechat) { + &.is-bind { + background-color: var(--grey-11); + } +} + .binding-wechat-modal { .ant-modal-body { text-align: center; diff --git a/web/flat-web/src/api-middleware/flatServer/constants.ts b/web/flat-web/src/api-middleware/flatServer/constants.ts index 9f403443e87..beb9606d367 100644 --- a/web/flat-web/src/api-middleware/flatServer/constants.ts +++ b/web/flat-web/src/api-middleware/flatServer/constants.ts @@ -14,6 +14,7 @@ export const FLAT_SERVER_LOGIN = { } as const; export const FLAT_SERVER_USER_BINDING = { + GITHUB_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/login/github/callback/binding`, WECHAT_CALLBACK: `${FLAT_SERVER_VERSIONS.V1}/user/binding/platform/wechat/web`, } as const; diff --git a/web/flat-web/src/pages/LoginPage/agoraLogin.ts b/web/flat-web/src/pages/LoginPage/agoraLogin.ts index 0f495259d33..2d4e0f5da1d 100644 --- a/web/flat-web/src/pages/LoginPage/agoraLogin.ts +++ b/web/flat-web/src/pages/LoginPage/agoraLogin.ts @@ -8,11 +8,6 @@ import { AGORA_OAUTH } from "../../constants/process"; export const agoraLogin: LoginExecutor = () => { const authUUID = uuidv4(); - function getAgoraURL(authUUID: string): string { - const redirectURL = encodeURIComponent(FLAT_SERVER_LOGIN.AGORA_CALLBACK); - return `https://sso2.agora.io/api/v0/oauth/authorize?response_type=code&client_id=${AGORA_OAUTH.CLIENT_ID}&redirect_uri=${redirectURL}&scope=basic_info&state=${authUUID}&toPage=signup`; - } - void (async () => { try { await setAuthUUID(authUUID); @@ -20,9 +15,14 @@ export const agoraLogin: LoginExecutor = () => { errorTips(err); } - window.location.href = getAgoraURL(authUUID); + window.location.href = getAgoraURL(authUUID, FLAT_SERVER_LOGIN.AGORA_CALLBACK); })(); // eslint-disable-next-line @typescript-eslint/no-empty-function return () => {}; }; + +export function getAgoraURL(authUUID: string, redirect_uri: string): string { + const redirectURL = encodeURIComponent(redirect_uri); + return `https://sso2.agora.io/api/v0/oauth/authorize?response_type=code&client_id=${AGORA_OAUTH.CLIENT_ID}&redirect_uri=${redirectURL}&scope=basic_info&state=${authUUID}&toPage=signup`; +} diff --git a/web/flat-web/src/pages/LoginPage/githubLogin.ts b/web/flat-web/src/pages/LoginPage/githubLogin.ts index 0c94fb30b06..44572914366 100644 --- a/web/flat-web/src/pages/LoginPage/githubLogin.ts +++ b/web/flat-web/src/pages/LoginPage/githubLogin.ts @@ -9,11 +9,6 @@ export const githubLogin: LoginExecutor = onSuccess => { let timer = NaN; const authUUID = uuidv4(); - function getGithubURL(authUUID: string): string { - const redirectURL = encodeURIComponent(FLAT_SERVER_LOGIN.GITHUB_CALLBACK); - return `https://github.com/login/oauth/authorize?client_id=${GITHUB.CLIENT_ID}&redirect_uri=${redirectURL}&state=${authUUID}`; - } - void (async () => { try { await setAuthUUID(authUUID); @@ -21,7 +16,7 @@ export const githubLogin: LoginExecutor = onSuccess => { errorTips(err); } - void window.open(getGithubURL(authUUID)); + void window.open(getGithubURL(authUUID, FLAT_SERVER_LOGIN.GITHUB_CALLBACK)); const githubLoginProcessRequest = async (): Promise => { try { @@ -45,3 +40,8 @@ export const githubLogin: LoginExecutor = onSuccess => { window.clearTimeout(timer); }; }; + +export function getGithubURL(authUUID: string, redirect_uri: string): string { + const redirectURL = encodeURIComponent(redirect_uri); + return `https://github.com/login/oauth/authorize?client_id=${GITHUB.CLIENT_ID}&redirect_uri=${redirectURL}&state=${authUUID}`; +} diff --git a/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/binding/GitHub.tsx b/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/binding/GitHub.tsx new file mode 100644 index 00000000000..44f9647fcb6 --- /dev/null +++ b/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/binding/GitHub.tsx @@ -0,0 +1,92 @@ +import { v4 } from "uuid"; +import classNames from "classnames"; +import React, { useCallback, useEffect, useState } from "react"; +import { message, Modal } from "antd"; +import { useTranslation } from "react-i18next"; +import { GithubFilled } from "@ant-design/icons"; + +import { GlobalStore } from "../../../../stores/GlobalStore"; +import { useSafePromise } from "../../../../utils/hooks/lifecycle"; +import { + bindingProcess, + LoginPlatform, + removeBinding, + setBindingAuthUUID, +} from "../../../../api-middleware/flatServer"; +import { FLAT_SERVER_USER_BINDING } from "../../../../api-middleware/flatServer/constants"; +import { getGithubURL } from "../../../LoginPage/githubLogin"; +import { errorTips } from "../../../../components/Tips/ErrorTips"; + +export interface BindGitHubProps { + isBind: boolean; + onRefresh: () => void; + globalStore: GlobalStore; +} + +export const BindGitHub: React.FC = ({ isBind, onRefresh, globalStore }) => { + const sp = useSafePromise(); + const { t } = useTranslation(); + const [authUUID, setAuthUUID] = useState(""); + + const cancel = useCallback((): void => { + setAuthUUID(""); + onRefresh(); + }, [onRefresh]); + + useEffect(() => { + let timer = NaN; + async function waitUntilBindFinish(): Promise { + const result = await sp(bindingProcess(authUUID)); + if (result.processing) { + timer = window.setTimeout(waitUntilBindFinish, 2000); + } else { + if (!result.status) { + message.info(t("bind-wechat-failed")); + } + cancel(); + } + } + if (authUUID) { + waitUntilBindFinish(); + return () => { + Number.isNaN(timer) || window.clearTimeout(timer); + }; + } + return; + }, [authUUID, cancel, onRefresh, sp, t]); + + const bindGitHub = async (): Promise => { + const authUUID = v4(); + setAuthUUID(authUUID); + await sp(setBindingAuthUUID(authUUID)); + void window.open(getGithubURL(authUUID, FLAT_SERVER_USER_BINDING.GITHUB_CALLBACK)); + }; + + const unbind = (): void => { + Modal.confirm({ + content: t("unbind-confirm"), + onOk: async () => { + try { + const { token } = await sp(removeBinding(LoginPlatform.Github)); + globalStore.updateUserToken(token); + onRefresh(); + message.info(t("unbind-success")); + } catch (err) { + errorTips(err); + } + }, + }); + }; + + return ( + + + + ); +}; diff --git a/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/binding/WeChat.tsx b/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/binding/WeChat.tsx index 4b2dbf5e5df..41a114ade21 100644 --- a/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/binding/WeChat.tsx +++ b/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/binding/WeChat.tsx @@ -23,7 +23,7 @@ export interface BindingWeChatProps { globalStore: GlobalStore; } -export const BindingWeChat: React.FC = ({ isBind, onRefresh, globalStore }) => { +export const BindWeChat: React.FC = ({ isBind, onRefresh, globalStore }) => { const sp = useSafePromise(); const { t } = useTranslation(); const [authUUID, setAuthUUID] = useState(""); diff --git a/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/index.less b/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/index.less index da24b2ee465..8d08c339134 100644 --- a/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/index.less +++ b/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/index.less @@ -128,6 +128,7 @@ width: 24px; height: 24px; border-radius: 50%; + margin-right: 8px; cursor: pointer; &.is-bind { @@ -135,6 +136,12 @@ } } +.binding-github:extend(.binding-wechat) { + &.is-bind { + background-color: var(--grey-11); + } +} + .binding-wechat-modal { .ant-modal-body { text-align: center; diff --git a/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/index.tsx b/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/index.tsx index 27fe7369add..e51f5e12c10 100644 --- a/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/index.tsx +++ b/web/flat-web/src/pages/UserSettingPage/GeneralSettingPage/index.tsx @@ -14,8 +14,9 @@ import { useSafePromise } from "../../../utils/hooks/lifecycle"; import { loginCheck, rename } from "../../../api-middleware/flatServer"; import { ConfirmButtons } from "./ConfirmButtons"; import { uploadAvatar, UploadAvatar } from "./UploadAvatar"; -import { BindingWeChat } from "./binding/WeChat"; +import { BindWeChat } from "./binding/WeChat"; import { useBindingList } from "./binding"; +import { BindGitHub } from "./binding/GitHub"; enum SelectLanguage { Chinese, @@ -85,11 +86,16 @@ export const GeneralSettingPage = observer(function GeneralSettingPage() {
- +