mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-03 12:30:19 +08:00
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
This commit is contained in:
121
web/src/common/CasdoorAppConnector.js
Normal file
121
web/src/common/CasdoorAppConnector.js
Normal file
@ -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 <Alert message={error} type="error" showIcon />;
|
||||
}
|
||||
|
||||
return (
|
||||
<QRCode
|
||||
value={qrUrl}
|
||||
icon={icon}
|
||||
errorLevel="M"
|
||||
size={230}
|
||||
bordered={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
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 <Alert message={error} type="error" showIcon />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: "10px",
|
||||
}}>
|
||||
<span>{i18next.t("general:URL String")}</span>
|
||||
{window.isSecureContext && (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={handleCopyUrl}
|
||||
style={{marginLeft: "10px"}}
|
||||
>
|
||||
{i18next.t("general:Copy URL")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
padding: "10px",
|
||||
maxWidth: "400px",
|
||||
maxHeight: "100px",
|
||||
overflow: "auto",
|
||||
wordBreak: "break-all",
|
||||
whiteSpace: "pre-wrap",
|
||||
cursor: "pointer",
|
||||
userSelect: "all",
|
||||
backgroundColor: "#f5f5f5",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
onClick={(e) => {
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(e.target);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}}
|
||||
>
|
||||
{qrUrl}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -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 <Alert message={error} type="error" showIcon />;
|
||||
} else {
|
||||
return (
|
||||
<QRCode
|
||||
value={qrUrl}
|
||||
icon={this.state.icon}
|
||||
errorLevel="M"
|
||||
size={230}
|
||||
bordered={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderTable(table) {
|
||||
const columns = [
|
||||
{
|
||||
@ -194,10 +159,25 @@ class MfaAccountTable extends React.Component {
|
||||
title={() => (
|
||||
<div>
|
||||
{this.props.title}
|
||||
<Button style={{marginRight: "10px"}} type="primary" size="small" onClick={() => this.addRow(table)}>{i18next.t("general:Add")}</Button>
|
||||
<Popover trigger="focus" overlayInnerStyle={{padding: 0}}
|
||||
content={this.renderQrCode()}>
|
||||
<Button style={{marginLeft: "5px"}} type="primary" size="small">{i18next.t("general:QR Code")}</Button>
|
||||
<Button style={{marginRight: "10px"}} type="primary" size="small" onClick={() => this.addRow(table)}>
|
||||
{i18next.t("general:Add")}
|
||||
</Button>
|
||||
<Popover
|
||||
trigger="focus"
|
||||
overlayInnerStyle={{padding: 0}}
|
||||
content={<CasdoorAppQrCode accessToken={this.props.accessToken} icon={this.state.icon} />}
|
||||
>
|
||||
<Button style={{marginRight: "10px"}} type="primary" size="small">
|
||||
{i18next.t("general:QR Code")}
|
||||
</Button>
|
||||
</Popover>
|
||||
<Popover
|
||||
trigger="click"
|
||||
content={<CasdoorAppUrl accessToken={this.props.accessToken} />}
|
||||
>
|
||||
<Button type="primary" size="small">
|
||||
{i18next.t("general:Show URL")}
|
||||
</Button>
|
||||
</Popover>
|
||||
</div>
|
||||
)}
|
||||
|
Reference in New Issue
Block a user