diff --git a/config/CN/.env.development b/config/CN/.env.development
index 4dee48920dc..f69a1391312 100644
--- a/config/CN/.env.development
+++ b/config/CN/.env.development
@@ -4,6 +4,7 @@ GITHUB_CLIENT_ID=9821657775fbc74773f1
WECHAT_APP_ID=wx1133c2153a45e9b8
AGORA_OAUTH_CLIENT_ID=flat-dev
GOOGLE_OAUTH_CLIENT_ID=273996094508-p97og69ojac5ja0khn1rvmi3tb7vgfgm.apps.googleusercontent.com
+QQ_APP_ID=
CLOUD_STORAGE_OSS_ALIBABA_ACCESS_KEY=LTAI5t9Gb6tzQzzLmB6cTVf7
CLOUD_STORAGE_OSS_ALIBABA_BUCKET=flat-storage
diff --git a/config/CN/.env.production b/config/CN/.env.production
index 29365132cb9..8994b124403 100644
--- a/config/CN/.env.production
+++ b/config/CN/.env.production
@@ -4,6 +4,7 @@ GITHUB_CLIENT_ID=71a29285a437998bdfe0
WECHAT_APP_ID=wx96d522d69d384cce
AGORA_OAUTH_CLIENT_ID=flat
GOOGLE_OAUTH_CLIENT_ID=273996094508-2rpraucen77a1o5dul5ftrua5k3og157.apps.googleusercontent.com
+QQ_APP_ID=
CLOUD_STORAGE_OSS_ALIBABA_ACCESS_KEY=LTAI5t9Gb6tzQzzLmB6cTVf7
CLOUD_STORAGE_OSS_ALIBABA_BUCKET=flat-storage
diff --git a/config/US/.env.development b/config/US/.env.development
index 1364f349022..989270e47df 100644
--- a/config/US/.env.development
+++ b/config/US/.env.development
@@ -4,6 +4,7 @@ GITHUB_CLIENT_ID=0ac608815326aead5db7
WECHAT_APP_ID=wx1133c2153a45e9b8
AGORA_OAUTH_CLIENT_ID=flat-dev
GOOGLE_OAUTH_CLIENT_ID=273996094508-p97og69ojac5ja0khn1rvmi3tb7vgfgm.apps.googleusercontent.com
+QQ_APP_ID=
CLOUD_STORAGE_OSS_ALIBABA_ACCESS_KEY=LTAI5t9Gb6tzQzzLmB6cTVf7
CLOUD_STORAGE_OSS_ALIBABA_BUCKET=flat-storage
diff --git a/config/US/.env.production b/config/US/.env.production
index 4857ce0bda1..a8a7e6ec484 100644
--- a/config/US/.env.production
+++ b/config/US/.env.production
@@ -4,6 +4,7 @@ GITHUB_CLIENT_ID=da83d7e14217594fba46
WECHAT_APP_ID=wx96d522d69d384cce
AGORA_OAUTH_CLIENT_ID=flat
GOOGLE_OAUTH_CLIENT_ID=273996094508-2rpraucen77a1o5dul5ftrua5k3og157.apps.googleusercontent.com
+QQ_APP_ID=
CLOUD_STORAGE_OSS_ALIBABA_ACCESS_KEY=LTAI5t9Gb6tzQzzLmB6cTVf7
CLOUD_STORAGE_OSS_ALIBABA_BUCKET=flat-storage
diff --git a/desktop/renderer-app/src/api-middleware/flatServer/index.ts b/desktop/renderer-app/src/api-middleware/flatServer/index.ts
index b34e9b824ed..bc401d85436 100644
--- a/desktop/renderer-app/src/api-middleware/flatServer/index.ts
+++ b/desktop/renderer-app/src/api-middleware/flatServer/index.ts
@@ -666,6 +666,7 @@ export enum LoginPlatform {
Agora = "Agora",
Google = "Google",
Phone = "Phone",
+ QQ = "QQ",
}
export interface RemoveBindingPayload {
diff --git a/packages/flat-components/src/components/LoginPage/LoginButton/icons/qq.svg b/packages/flat-components/src/components/LoginPage/LoginButton/icons/qq.svg
new file mode 100644
index 00000000000..9f1343d1004
--- /dev/null
+++ b/packages/flat-components/src/components/LoginPage/LoginButton/icons/qq.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/flat-components/src/components/LoginPage/LoginButton/index.tsx b/packages/flat-components/src/components/LoginPage/LoginButton/index.tsx
index a450129d000..22c1b5c0e55 100644
--- a/packages/flat-components/src/components/LoginPage/LoginButton/index.tsx
+++ b/packages/flat-components/src/components/LoginPage/LoginButton/index.tsx
@@ -2,11 +2,12 @@ import wechatSVG from "./icons/wechat.svg";
import agoraSVG from "./icons/agora.svg";
import githubSVG from "./icons/github.svg";
import googleSVG from "./icons/google.svg";
+import qqSVG from "./icons/qq.svg";
import "./index.less";
import React from "react";
-export type LoginButtonProviderType = "wechat" | "github" | "agora" | "google";
+export type LoginButtonProviderType = "wechat" | "github" | "agora" | "google" | "qq";
export interface LoginButtonProps {
provider: LoginButtonProviderType;
@@ -19,6 +20,7 @@ const svgDict: Record = {
agora: agoraSVG,
github: githubSVG,
google: googleSVG,
+ qq: qqSVG,
};
export const LoginButton: React.FC = ({ provider, text, onClick }) => {
diff --git a/packages/flat-i18n/locales/en.json b/packages/flat-i18n/locales/en.json
index 5932e8922b2..171e384e8e4 100644
--- a/packages/flat-i18n/locales/en.json
+++ b/packages/flat-i18n/locales/en.json
@@ -111,6 +111,7 @@
"begin-time": "Start time",
"join-and-book-by-room-uuid": "Can join and book by room ID",
"copy": "Copy",
+ "login-qq": "QQ",
"login-github": "GitHub",
"login-google": "Google",
"login-wechat": "WeChat",
diff --git a/packages/flat-i18n/locales/zh-CN.json b/packages/flat-i18n/locales/zh-CN.json
index bfaf59f98a5..fd2fb6c665f 100644
--- a/packages/flat-i18n/locales/zh-CN.json
+++ b/packages/flat-i18n/locales/zh-CN.json
@@ -112,6 +112,7 @@
"room-type": "房间类型",
"begin-time": "开始时间",
"copy": "复制",
+ "login-qq": "QQ",
"login-wechat": "微信",
"login-github": "GitHub",
"login-google": "Google",
diff --git a/packages/flat-pages/src/LoginPage/index.tsx b/packages/flat-pages/src/LoginPage/index.tsx
index b01753da117..962b9c91f62 100644
--- a/packages/flat-pages/src/LoginPage/index.tsx
+++ b/packages/flat-pages/src/LoginPage/index.tsx
@@ -9,6 +9,7 @@ import { githubLogin } from "./githubLogin";
import { WeChatLogin } from "./WeChatLogin";
import { agoraLogin } from "./agoraLogin";
import { googleLogin } from "./googleLogin";
+import { qqLogin } from "./qqLogin";
import { RouteNameType, usePushHistory, useURLParams } from "../utils/routes";
import { GlobalStoreContext, WindowsSystemBtnContext } from "../components/StoreProvider";
import { joinRoomHandler } from "../utils/join-room-handler";
@@ -113,6 +114,10 @@ export const LoginPage = observer(function LoginPage() {
loginDisposer.current = googleLogin(onLoginResult, windowsBtn);
return;
}
+ case "qq": {
+ loginDisposer.current = qqLogin(onLoginResult, windowsBtn);
+ return;
+ }
default: {
return;
}
@@ -155,7 +160,11 @@ export const LoginPage = observer(function LoginPage() {
bindingPhone={async (countryCode, phone, code) =>
wrap(bindingPhone(countryCode + phone, Number(code)).then(onBoundPhone))
}
- buttons={[process.env.FLAT_REGION === "US" ? "google" : "wechat", "github"]}
+ buttons={
+ process.env.FLAT_REGION === "US"
+ ? ["google", "github"]
+ : ["wechat", "qq", "github"]
+ }
cancelBindingPhone={() => setLoginResult(null)}
isBindingPhone={
NEED_BINDING_PHONE && (loginResult ? !loginResult.hasPhone : false)
diff --git a/packages/flat-pages/src/LoginPage/qqLogin.ts b/packages/flat-pages/src/LoginPage/qqLogin.ts
new file mode 100644
index 00000000000..de9494d0629
--- /dev/null
+++ b/packages/flat-pages/src/LoginPage/qqLogin.ts
@@ -0,0 +1,47 @@
+import { v4 as uuidv4 } from "uuid";
+import { LoginExecutor } from "./utils";
+import { QQ } from "../constants/process";
+import { errorTips } from "flat-components";
+import { FLAT_SERVER_LOGIN, setAuthUUID, loginProcess } from "@netless/flat-server-api";
+
+export const qqLogin: LoginExecutor = (onSuccess, windowsBtn) => {
+ let timer = NaN;
+ const authUUID = uuidv4();
+
+ void (async () => {
+ try {
+ await setAuthUUID(authUUID);
+ } catch (err) {
+ errorTips(err);
+ }
+
+ windowsBtn
+ ? windowsBtn.openExternalBrowser(getQqURL(authUUID, FLAT_SERVER_LOGIN.QQ_CALLBACK))
+ : void window.open(getQqURL(authUUID, FLAT_SERVER_LOGIN.QQ_CALLBACK));
+
+ const qqLoginProcessRequest = async (): Promise => {
+ try {
+ const data = await loginProcess(authUUID);
+ if (!data.name) {
+ timer = window.setTimeout(qqLoginProcessRequest, 2000);
+ return;
+ }
+
+ onSuccess(data);
+ } catch (err) {
+ errorTips(err);
+ }
+ };
+
+ void qqLoginProcessRequest();
+ })();
+
+ return () => {
+ window.clearTimeout(timer);
+ };
+};
+
+export function getQqURL(authUUID: string, redirect_uri: string): string {
+ const redirectURL = encodeURIComponent(redirect_uri);
+ return `https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=${QQ.CLIENT_ID}&redirect_uri=${redirectURL}&state=${authUUID}`;
+}
diff --git a/packages/flat-pages/src/UserSettingPage/GeneralSettingPage/binding/index.ts b/packages/flat-pages/src/UserSettingPage/GeneralSettingPage/binding/index.ts
index 9caffde0867..3d13adfa634 100644
--- a/packages/flat-pages/src/UserSettingPage/GeneralSettingPage/binding/index.ts
+++ b/packages/flat-pages/src/UserSettingPage/GeneralSettingPage/binding/index.ts
@@ -10,6 +10,7 @@ const defaultBindings: ListBindingsResult = {
apple: false,
github: false,
google: false,
+ qq: false,
};
export function useBindingList(): { bindings: ListBindingsResult; refresh: () => void } {
diff --git a/packages/flat-pages/src/constants/process.ts b/packages/flat-pages/src/constants/process.ts
index 5ff5bc170e4..f6ea363d065 100644
--- a/packages/flat-pages/src/constants/process.ts
+++ b/packages/flat-pages/src/constants/process.ts
@@ -32,6 +32,10 @@ export const GOOGLE = Object.freeze({
CLIENT_ID: process.env.GOOGLE_OAUTH_CLIENT_ID,
});
+export const QQ = Object.freeze({
+ CLIENT_ID: process.env.QQ_APP_ID,
+});
+
export const FLAT_DOWNLOAD_URL = process.env.FLAT_DOWNLOAD_URL;
export const PRIVACY_URL_CN = "https://flat.whiteboard.agora.io/privacy.html";
diff --git a/packages/flat-pages/src/typings/global.d.ts b/packages/flat-pages/src/typings/global.d.ts
index 92980332eec..94a1ddfa8da 100644
--- a/packages/flat-pages/src/typings/global.d.ts
+++ b/packages/flat-pages/src/typings/global.d.ts
@@ -20,6 +20,7 @@ declare namespace NodeJS {
GITHUB_CLIENT_ID: string;
GOOGLE_OAUTH_CLIENT_ID: string;
+ QQ_APP_ID: string;
WECHAT_APP_ID: string;
FLAT_SERVER_DOMAIN: string;
FLAT_WEB_DOMAIN: string;
diff --git a/packages/flat-server-api/src/constants.ts b/packages/flat-server-api/src/constants.ts
index d593f4cef4c..fa5ea3a192c 100644
--- a/packages/flat-server-api/src/constants.ts
+++ b/packages/flat-server-api/src/constants.ts
@@ -8,6 +8,7 @@ export const FLAT_SERVER_LOGIN = {
GITHUB_CALLBACK: `https://${process.env.FLAT_SERVER_DOMAIN}/v1/login/github/callback?platform=web`,
GOOGLE_CALLBACK: `https://${process.env.FLAT_SERVER_DOMAIN}/v1/login/google/callback`,
WECHAT_CALLBACK: `https://${process.env.FLAT_SERVER_DOMAIN}/v1/login/weChat/web/callback`,
+ QQ_CALLBACK: `https://${process.env.FLAT_SERVER_DOMAIN}/v1/login/qq/callback?platform=web`,
} as const;
export const FLAT_SERVER_USER_BINDING = {
diff --git a/packages/flat-server-api/src/login.ts b/packages/flat-server-api/src/login.ts
index 0542747bb22..7f49f3bb584 100644
--- a/packages/flat-server-api/src/login.ts
+++ b/packages/flat-server-api/src/login.ts
@@ -131,6 +131,7 @@ export interface ListBindingsResult {
apple: boolean;
github: boolean;
google: boolean;
+ qq: boolean;
}
export async function listBindings(): Promise {
@@ -188,6 +189,7 @@ export enum LoginPlatform {
Agora = "Agora",
Google = "Google",
Phone = "Phone",
+ QQ = "QQ",
}
export interface RemoveBindingPayload {
diff --git a/web/flat-web/typings/global.d.ts b/web/flat-web/typings/global.d.ts
index 92980332eec..94a1ddfa8da 100644
--- a/web/flat-web/typings/global.d.ts
+++ b/web/flat-web/typings/global.d.ts
@@ -20,6 +20,7 @@ declare namespace NodeJS {
GITHUB_CLIENT_ID: string;
GOOGLE_OAUTH_CLIENT_ID: string;
+ QQ_APP_ID: string;
WECHAT_APP_ID: string;
FLAT_SERVER_DOMAIN: string;
FLAT_WEB_DOMAIN: string;