diff --git a/package.json b/package.json
index c1625404..54f88216 100644
--- a/package.json
+++ b/package.json
@@ -1,100 +1,102 @@
{
- "name": "rustchat-web",
- "version": "0.1.0",
- "private": true,
- "homepage": "http://privoce.rustchat.com",
- "dependencies": {
- "@babel/core": "^7.17.0",
- "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
- "@reduxjs/toolkit": "^1.7.2",
- "@rtk-query/codegen-openapi": "^1.0.0-alpha.1",
- "@svgr/webpack": "^6.2.1",
- "animate.css": "^4.1.1",
- "axios": "^0.25.0",
- "babel-loader": "^8.2.3",
- "babel-plugin-named-asset-import": "^0.3.8",
- "babel-preset-react-app": "^10.0.1",
- "bfj": "^7.0.2",
- "browserslist": "^4.19.1",
- "camelcase": "^6.3.0",
- "case-sensitive-paths-webpack-plugin": "^2.4.0",
- "css-loader": "^6.6.0",
- "css-minimizer-webpack-plugin": "^3.4.1",
- "dayjs": "^1.10.7",
- "dotenv": "^16.0.0",
- "dotenv-expand": "^8.0.1",
- "emoji-mart": "^3.0.1",
- "eslint": "^8.8.0",
- "event-source-polyfill": "^1.0.25",
- "file-loader": "^6.2.0",
- "fs-extra": "^10.0.0",
- "html-webpack-plugin": "^5.5.0",
- "mini-css-extract-plugin": "^2.5.3",
- "react": "^17.0.2",
- "react-dev-utils": "^12.0.0",
- "react-dnd": "^15.1.1",
- "react-dnd-html5-backend": "^15.1.2",
- "react-dom": "^17.0.2",
- "react-hot-toast": "^2.2.0",
- "react-icons": "^4.3.1",
- "react-redux": "^7.2.6",
- "react-refresh": "^0.11.0",
- "react-router-dom": "6",
- "react-textarea-autosize": "^8.3.3",
- "redux-persist": "^6.0.0",
- "resolve": "^1.22.0",
- "rooks": "^5.10.0",
- "semver": "^7.3.5",
- "source-map-loader": "^3.0.1",
- "style-loader": "^3.3.1",
- "styled-components": "^5.3.3",
- "styled-reset": "^4.3.4",
- "terser-webpack-plugin": "^5.3.1",
- "webpack": "^5.68.0",
- "webpack-dev-server": "^4.7.4",
- "webpack-manifest-plugin": "^4.1.1",
- "workbox-webpack-plugin": "^6.4.2"
- },
- "scripts": {
- "start": "node scripts/start.js",
- "build": "node scripts/build.js",
- "deploy": "yarn build && gh-pages -d build",
- "lint": "lint-staged",
- "prepare": "husky install"
- },
- "browserslist": {
- "production": [
- "last 5 chrome version",
- "last 5 firefox version",
- "last 5 safari version"
- ],
- "development": [
- "last 1 chrome version",
- "last 1 firefox version",
- "last 1 safari version"
- ]
- },
- "babel": {
- "presets": [
- "react-app"
- ]
- },
- "devDependencies": {
- "@commitlint/cli": "^16.1.0",
- "@commitlint/config-conventional": "^16.0.0",
- "autoprefixer": "^10.4.2",
- "eslint-config-prettier": "^8.3.0",
- "eslint-plugin-import": "^2.25.4",
- "eslint-plugin-prettier": "^4.0.0",
- "eslint-plugin-react": "^7.28.0",
- "eslint-plugin-react-hooks": "^4.3.0",
- "gh-pages": "^3.2.3",
- "husky": "^7.0.4",
- "lint-staged": "^12.3.3",
- "postcss": "^8.4.6",
- "postcss-flexbugs-fixes": "^5.0.2",
- "postcss-loader": "^6.2.1",
- "postcss-preset-env": "^7.3.1",
- "prettier": "^2.5.1"
- }
+ "name": "rustchat-web",
+ "version": "0.1.0",
+ "private": true,
+ "homepage": "http://privoce.rustchat.com",
+ "dependencies": {
+ "@babel/core": "^7.17.0",
+ "@metamask/onboarding": "^1.0.1",
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
+ "@reduxjs/toolkit": "^1.7.2",
+ "@rtk-query/codegen-openapi": "^1.0.0-alpha.1",
+ "@svgr/webpack": "^6.2.1",
+ "animate.css": "^4.1.1",
+ "axios": "^0.25.0",
+ "babel-loader": "^8.2.3",
+ "babel-plugin-named-asset-import": "^0.3.8",
+ "babel-preset-react-app": "^10.0.1",
+ "bfj": "^7.0.2",
+ "browserslist": "^4.19.1",
+ "camelcase": "^6.3.0",
+ "case-sensitive-paths-webpack-plugin": "^2.4.0",
+ "css-loader": "^6.6.0",
+ "css-minimizer-webpack-plugin": "^3.4.1",
+ "dayjs": "^1.10.7",
+ "dotenv": "^16.0.0",
+ "dotenv-expand": "^8.0.1",
+ "emoji-mart": "^3.0.1",
+ "eslint": "^8.8.0",
+ "event-source-polyfill": "^1.0.25",
+ "file-loader": "^6.2.0",
+ "fs-extra": "^10.0.0",
+ "html-webpack-plugin": "^5.5.0",
+ "mini-css-extract-plugin": "^2.5.3",
+ "react": "^17.0.2",
+ "react-dev-utils": "^12.0.0",
+ "react-dnd": "^15.1.1",
+ "react-dnd-html5-backend": "^15.1.2",
+ "react-dom": "^17.0.2",
+ "react-google-login": "^5.2.2",
+ "react-hot-toast": "^2.2.0",
+ "react-icons": "^4.3.1",
+ "react-redux": "^7.2.6",
+ "react-refresh": "^0.11.0",
+ "react-router-dom": "6",
+ "react-textarea-autosize": "^8.3.3",
+ "redux-persist": "^6.0.0",
+ "resolve": "^1.22.0",
+ "rooks": "^5.10.0",
+ "semver": "^7.3.5",
+ "source-map-loader": "^3.0.1",
+ "style-loader": "^3.3.1",
+ "styled-components": "^5.3.3",
+ "styled-reset": "^4.3.4",
+ "terser-webpack-plugin": "^5.3.1",
+ "webpack": "^5.68.0",
+ "webpack-dev-server": "^4.7.4",
+ "webpack-manifest-plugin": "^4.1.1",
+ "workbox-webpack-plugin": "^6.4.2"
+ },
+ "scripts": {
+ "start": "node scripts/start.js",
+ "build": "node scripts/build.js",
+ "deploy": "yarn build && gh-pages -d build",
+ "lint": "lint-staged",
+ "prepare": "husky install"
+ },
+ "browserslist": {
+ "production": [
+ "last 5 chrome version",
+ "last 5 firefox version",
+ "last 5 safari version"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ },
+ "babel": {
+ "presets": [
+ "react-app"
+ ]
+ },
+ "devDependencies": {
+ "@commitlint/cli": "^16.1.0",
+ "@commitlint/config-conventional": "^16.0.0",
+ "autoprefixer": "^10.4.2",
+ "eslint-config-prettier": "^8.3.0",
+ "eslint-plugin-import": "^2.25.4",
+ "eslint-plugin-prettier": "^4.0.0",
+ "eslint-plugin-react": "^7.28.0",
+ "eslint-plugin-react-hooks": "^4.3.0",
+ "gh-pages": "^3.2.3",
+ "husky": "^7.0.4",
+ "lint-staged": "^12.3.3",
+ "postcss": "^8.4.6",
+ "postcss-flexbugs-fixes": "^5.0.2",
+ "postcss-loader": "^6.2.1",
+ "postcss-preset-env": "^7.3.1",
+ "prettier": "^2.5.1"
+ }
}
diff --git a/src/routes/login/GoogleLoginButton.js b/src/routes/login/GoogleLoginButton.js
new file mode 100644
index 00000000..1a1415c8
--- /dev/null
+++ b/src/routes/login/GoogleLoginButton.js
@@ -0,0 +1,31 @@
+// import { useState, useEffect } from "react";
+import { useGoogleLogin } from 'react-google-login';
+import { googleClientID } from '../../app/config';
+export default function GoogleLoginButton({ login }) {
+ const { signIn, loaded } = useGoogleLogin({
+ clientId: googleClientID,
+ onSuccess: ({ tokenId, ...rest }) => {
+ console.log('success', tokenId, rest);
+ login({
+ id_token: tokenId,
+ type: 'google'
+ });
+ },
+ onFailure: (wtf) => {
+ console.log('failure', wtf);
+ }
+ });
+ const handleGoogleLogin = () => {
+ signIn();
+ };
+ return (
+
+ );
+}
diff --git a/src/routes/login/MetamaskLoginButton.js b/src/routes/login/MetamaskLoginButton.js
new file mode 100644
index 00000000..5e30906d
--- /dev/null
+++ b/src/routes/login/MetamaskLoginButton.js
@@ -0,0 +1,62 @@
+import { useState, useEffect, useRef } from 'react';
+import MetaMaskOnboarding from '@metamask/onboarding';
+const ONBOARD_TEXT = 'Click here to install MetaMask!';
+const CONNECT_TEXT = 'Sign in with MetaMask';
+const CONNECTED_TEXT = 'Connected';
+export default function MetamaskLoginButton() {
+ const [btnTxt, setBtnTxt] = useState(ONBOARD_TEXT);
+ const [isDisabled, setIsDisabled] = useState(false);
+ const [accounts, setAccounts] = useState([]);
+ const onboarding = useRef();
+ useEffect(() => {
+ if (!onboarding.current) {
+ onboarding.current = new MetaMaskOnboarding();
+ }
+ }, []);
+ useEffect(() => {
+ if (MetaMaskOnboarding.isMetaMaskInstalled()) {
+ console.log(accounts);
+ if (accounts.length > 0) {
+ setBtnTxt(CONNECTED_TEXT);
+ setIsDisabled(true);
+ onboarding.current.stopOnboarding();
+ } else {
+ setBtnTxt(CONNECT_TEXT);
+ setIsDisabled(false);
+ }
+ }
+ }, [accounts]);
+
+ useEffect(() => {
+ function handleNewAccounts(newAccounts) {
+ setAccounts(newAccounts);
+ }
+ if (MetaMaskOnboarding.isMetaMaskInstalled()) {
+ window.ethereum.request({ method: 'eth_requestAccounts' }).then(handleNewAccounts);
+ window.ethereum.on('accountsChanged', handleNewAccounts);
+ return () => {
+ window.ethereum.off('accountsChanged', handleNewAccounts);
+ };
+ }
+ }, []);
+
+ const handleMetamaskLogin = () => {
+ if (MetaMaskOnboarding.isMetaMaskInstalled()) {
+ window.ethereum
+ .request({ method: 'eth_requestAccounts' })
+ .then((newAccounts) => setAccounts(newAccounts));
+ } else {
+ onboarding.current.startOnboarding();
+ }
+ };
+ return (
+
+ );
+}
diff --git a/src/routes/login/index.js b/src/routes/login/index.js
index 7fbcd8c5..78640d83 100644
--- a/src/routes/login/index.js
+++ b/src/routes/login/index.js
@@ -1,98 +1,115 @@
-import { useState } from "react";
-import StyledWrapper from "./styled";
-import { useDispatch } from "react-redux";
-import { useNavigate } from "react-router-dom";
-import toast from "react-hot-toast";
+/* eslint-disable no-undef */
+import { useState, useEffect } from 'react';
+import StyledWrapper from './styled';
+import { useDispatch } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import toast from 'react-hot-toast';
-import { useLoginMutation } from "../../app/services/auth";
-import { setAuthData } from "../../app/slices/auth.data";
+// import web3 from "web3";
+import MetamaskLoginButton from './MetamaskLoginButton';
+
+import GoogleLoginButton from './GoogleLoginButton';
+import { useLoginMutation } from '../../app/services/auth';
+import { setAuthData } from '../../app/slices/auth.data';
export default function LoginPage() {
- // const { token } = useSelector((store) => store.authData);
- const navigateTo = useNavigate();
- const dispatch = useDispatch();
- const [login] = useLoginMutation();
- const [input, setInput] = useState({
- email: "",
- password: "",
+ const [login, { data, isSuccess, isLoading, error }] = useLoginMutation();
+
+ // const { token } = useSelector((store) => store.authData);
+ const navigateTo = useNavigate();
+ const dispatch = useDispatch();
+ const [input, setInput] = useState({
+ email: '',
+ password: ''
+ });
+
+ useEffect(() => {
+ if (error) {
+ console.log(error);
+ switch (error.status) {
+ case 'PARSING_ERROR':
+ toast.error(error.data);
+ break;
+ case 401:
+ toast.error('username or password incorrect');
+ break;
+ case 404:
+ toast.error('account not exsit');
+ break;
+
+ default:
+ toast.error('something error');
+ break;
+ }
+ return;
+ }
+ }, [error]);
+ useEffect(() => {
+ if (isSuccess && data) {
+ // 更新本地认证信息
+ toast.success('login success');
+ dispatch(setAuthData(data));
+ navigateTo('/');
+ }
+ }, [isSuccess, data]);
+
+ const handleLogin = (evt) => {
+ evt.preventDefault();
+ console.log('wtf', input);
+ login({
+ ...input,
+ type: 'password'
});
+ };
- const handleLogin = async (evt) => {
- evt.preventDefault();
- console.log("wtf", input);
- const { data, error } = await login({
- ...input,
- type: "password",
- });
- if (error) {
- console.log(error);
- switch (error.status) {
- case 401:
- toast.error("username or password incorrect");
- break;
-
- default:
- break;
- }
- return;
- }
- if (data) {
- // 更新本地认证信息
- toast.success("login success");
- dispatch(setAuthData(data));
- navigateTo("/");
- // location.reload(true);
- }
- };
- const handleInput = (evt) => {
- const { type } = evt.target.dataset;
- const { value } = evt.target;
- console.log(type, value);
- setInput((prev) => {
- prev[type] = value;
- return { ...prev };
- });
- };
- const { email, password } = input;
- return (
-
-
-
- );
+ const handleInput = (evt) => {
+ const { type } = evt.target.dataset;
+ const { value } = evt.target;
+ console.log(type, value);
+ setInput((prev) => {
+ prev[type] = value;
+ return { ...prev };
+ });
+ };
+ const { email, password } = input;
+ return (
+
+
+
+

+
Login to Rustchat
+
Please enter your details.
+
+
+
+
+
+
+
+ );
}
diff --git a/src/routes/login/styled.js b/src/routes/login/styled.js
index 88bdd788..643f3c6a 100644
--- a/src/routes/login/styled.js
+++ b/src/routes/login/styled.js
@@ -1,95 +1,103 @@
-import styled from "styled-components";
+import styled from 'styled-components';
const StyledWrapper = styled.div`
- display: flex;
- justify-content: center;
- align-items: center;
- height: 100vh;
- .form {
- padding: 36px 40px 32px 40px;
- /* border: 1px solid #eee; */
- box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1),
- 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
- border-radius: 12px;
- .tips {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding-bottom: 24px;
- .logo {
- width: 56px;
- height: 56px;
- margin-bottom: 28px;
- }
- .title {
- font-weight: 600;
- font-size: 24px;
- line-height: 32px;
- color: #101828;
- margin-bottom: 8px;
- }
- }
- .desc {
- font-weight: normal;
- font-size: 16px;
- line-height: 24px;
- color: #667085;
- }
- form {
- display: flex;
- flex-direction: column;
- gap: 20px;
- input {
- width: 360px;
- background: #ffffff;
- border: 1px solid #d0d5dd;
- box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
- border-radius: 8px;
- padding: 10px 14px;
- font-weight: normal;
- font-size: 16px;
- line-height: 24px;
- color: #667085;
- }
- }
- .or {
- border: none;
- position: relative;
- height: 1px;
- background-color: #e4e7ec;
- margin: 26px 0;
- &:after {
- padding: 4px;
- background-color: #fff;
- content: "OR";
- position: absolute;
- left: 50%;
- top: 50%;
- transform: translate3d(-50%, -50%, 0);
- font-size: 14px;
- line-height: 20px;
- color: #667085;
- }
- }
- .btn {
- display: inline-block;
- text-align: center;
- width: 100%;
- font-weight: 500;
- font-size: 16px;
- line-height: 24px;
- color: #ffffff;
- padding: 10px;
- background: #1fe1f9;
- border: 1px solid #1fe1f9;
- box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
- border-radius: 8px;
- &.google {
- color: #344054;
- border-color: #d0d5dd;
- background: none;
- }
- }
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ .form {
+ padding: 36px 40px 32px 40px;
+ /* border: 1px solid #eee; */
+ box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06);
+ border-radius: 12px;
+ .tips {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding-bottom: 24px;
+ .logo {
+ width: 56px;
+ height: 56px;
+ margin-bottom: 28px;
+ }
+ .title {
+ font-weight: 600;
+ font-size: 24px;
+ line-height: 32px;
+ color: #101828;
+ margin-bottom: 8px;
+ }
}
+ .desc {
+ font-weight: normal;
+ font-size: 16px;
+ line-height: 24px;
+ color: #667085;
+ }
+ form {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ input {
+ width: 360px;
+ background: #ffffff;
+ border: 1px solid #d0d5dd;
+ box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
+ border-radius: 8px;
+ padding: 10px 14px;
+ font-weight: normal;
+ font-size: 16px;
+ line-height: 24px;
+ color: #667085;
+ }
+ }
+ .or {
+ border: none;
+ position: relative;
+ height: 1px;
+ background-color: #e4e7ec;
+ margin: 26px 0;
+ &:after {
+ padding: 4px;
+ background-color: #fff;
+ content: 'OR';
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate3d(-50%, -50%, 0);
+ font-size: 14px;
+ line-height: 20px;
+ color: #667085;
+ }
+ }
+ .btn {
+ display: inline-block;
+ text-align: center;
+ width: 100%;
+ font-weight: 500;
+ font-size: 16px;
+ line-height: 24px;
+ color: #ffffff;
+ padding: 10px;
+ background: #1fe1f9;
+ border: 1px solid #1fe1f9;
+ box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
+ border-radius: 8px;
+ &.social {
+ margin-bottom: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ color: #344054;
+ border-color: #d0d5dd;
+ background: none;
+ .icon {
+ width: 24px;
+ height: 24px;
+ }
+ }
+ }
+ }
`;
export default StyledWrapper;
diff --git a/yarn.lock b/yarn.lock
index 71a563e3..7106953a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1401,6 +1401,13 @@
resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796"
integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==
+"@metamask/onboarding@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@metamask/onboarding/-/onboarding-1.0.1.tgz#14a36e1e175e2f69f09598e2008ab6dc1b3297e6"
+ integrity sha512-FqHhAsCI+Vacx2qa5mAFcWNSrTcVGMNjzxVgaX8ECSny/BJ9/vgXP9V7WF/8vb9DltPeQkxr+Fnfmm6GHfmdTQ==
+ dependencies:
+ bowser "^2.9.0"
+
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -2426,6 +2433,11 @@ boolbase@^1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
+bowser@^2.9.0:
+ version "2.11.0"
+ resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f"
+ integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==
+
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -6284,6 +6296,14 @@ react-error-overlay@^6.0.10:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6"
integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA==
+react-google-login@^5.2.2:
+ version "5.2.2"
+ resolved "https://registry.yarnpkg.com/react-google-login/-/react-google-login-5.2.2.tgz#a20b46440c6c1610175ef75baf427118ff0e9859"
+ integrity sha512-JUngfvaSMcOuV0lFff7+SzJ2qviuNMQdqlsDJkUM145xkGPVIfqWXq9Ui+2Dr6jdJWH5KYdynz9+4CzKjI5u6g==
+ dependencies:
+ "@types/react" "*"
+ prop-types "^15.6.0"
+
react-hot-toast@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.2.0.tgz#ab6f4caed4214b9534f94bb8cfaaf21b051e62b9"