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}     - - - + + } + > + + + } + > +
)}