From fd5ccd8d412e8a892da48fcd469b0dd42f1d38bb Mon Sep 17 00:00:00 2001
From: IZUMI-Zu <274620705z@gmail.com>
Date: Wed, 13 Nov 2024 17:06:09 +0800
Subject: [PATCH] feat: support copying token to clipboard for casdoor-app
(#3345)
* feat: support copy token to clipboard for casdoor-app auth
* feat: abstract casdoor-app related code
---
web/src/common/CasdoorAppConnector.js | 121 ++++++++++++++++++++++++++
web/src/table/MfaAccountTable.js | 62 +++++--------
2 files changed, 142 insertions(+), 41 deletions(-)
create mode 100644 web/src/common/CasdoorAppConnector.js
diff --git a/web/src/common/CasdoorAppConnector.js b/web/src/common/CasdoorAppConnector.js
new file mode 100644
index 00000000..f39c32c3
--- /dev/null
+++ b/web/src/common/CasdoorAppConnector.js
@@ -0,0 +1,121 @@
+// Copyright 2024 The Casdoor Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import React from "react";
+import {Alert, Button, QRCode} from "antd";
+import * as Setting from "../Setting";
+import i18next from "i18next";
+
+export const generateCasdoorAppUrl = (accessToken, forQrCode = true) => {
+ let qrUrl = "";
+ let error = null;
+
+ if (!accessToken) {
+ error = i18next.t("general:Access token is empty");
+ return {qrUrl, error};
+ }
+
+ qrUrl = `casdoor-app://login?serverUrl=${window.location.origin}&accessToken=${accessToken}`;
+
+ if (forQrCode && qrUrl.length >= 2000) {
+ qrUrl = "";
+ error = i18next.t("general:QR code is too large");
+ }
+
+ return {qrUrl, error};
+};
+
+export const CasdoorAppQrCode = ({accessToken, icon}) => {
+ const {qrUrl, error} = generateCasdoorAppUrl(accessToken, true);
+
+ if (error) {
+ return ;
+ }
+
+ return (
+
+ );
+};
+
+export const CasdoorAppUrl = ({accessToken}) => {
+ const {qrUrl, error} = generateCasdoorAppUrl(accessToken, false);
+
+ const handleCopyUrl = async() => {
+ if (!window.isSecureContext) {
+ return;
+ }
+
+ try {
+ await navigator.clipboard.writeText(qrUrl);
+ Setting.showMessage("success", i18next.t("general:Copied to clipboard"));
+ } catch (err) {
+ Setting.showMessage("error", i18next.t("general:Failed to copy"));
+ }
+ };
+
+ if (error) {
+ return ;
+ }
+
+ return (
+
+
+ {i18next.t("general:URL String")}
+ {window.isSecureContext && (
+
+ )}
+
+
{
+ const selection = window.getSelection();
+ const range = document.createRange();
+ range.selectNodeContents(e.target);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }}
+ >
+ {qrUrl}
+
+
+ );
+};
diff --git a/web/src/table/MfaAccountTable.js b/web/src/table/MfaAccountTable.js
index db589b89..8fe7651d 100644
--- a/web/src/table/MfaAccountTable.js
+++ b/web/src/table/MfaAccountTable.js
@@ -14,9 +14,10 @@
import React from "react";
import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
-import {Alert, Button, Col, Image, Input, Popover, QRCode, Row, Table, Tooltip} from "antd";
+import {Button, Col, Image, Input, Popover, Row, Table, Tooltip} from "antd";
import * as Setting from "../Setting";
import i18next from "i18next";
+import {CasdoorAppQrCode, CasdoorAppUrl} from "../common/CasdoorAppConnector";
class MfaAccountTable extends React.Component {
constructor(props) {
@@ -76,42 +77,6 @@ class MfaAccountTable extends React.Component {
this.updateTable(table);
}
- getQrUrl() {
- const {accessToken} = this.props;
- let qrUrl = `casdoor-app://login?serverUrl=${window.location.origin}&accessToken=${accessToken}`;
- let error = null;
-
- if (!accessToken) {
- qrUrl = "";
- error = i18next.t("general:Access token is empty");
- }
-
- if (qrUrl.length >= 2000) {
- qrUrl = "";
- error = i18next.t("general:QR code is too large");
- }
-
- return {qrUrl, error};
- }
-
- renderQrCode() {
- const {qrUrl, error} = this.getQrUrl();
-
- if (error) {
- return ;
- } else {
- return (
-
- );
- }
- }
-
renderTable(table) {
const columns = [
{
@@ -194,10 +159,25 @@ class MfaAccountTable extends React.Component {
title={() => (
{this.props.title}
-
-
-
+
+ }
+ >
+
+
+
}
+ >
+
)}