Browse Source

Application share qrcode (#1277)

Co-authored-by: luowei <glpat-EjySCyNjWiLqAED-YmwM>
Co-authored-by: crazywoola <427733928@qq.com>
Charlie.Wei 1 year ago
parent
commit
b711ce33b7

+ 2 - 0
web/app/components/app/overview/appCard.tsx

@@ -22,6 +22,7 @@ import Tag from '@/app/components/base/tag'
 import Switch from '@/app/components/base/switch'
 import Divider from '@/app/components/base/divider'
 import CopyFeedback from '@/app/components/base/copy-feedback'
+import ShareQRCode from '@/app/components/base/qrcode'
 import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
 import type { AppDetailResponse } from '@/models/app'
 import { AppType } from '@/types/app'
@@ -168,6 +169,7 @@ function AppCard({
                 </div>
               </div>
               <Divider type="vertical" className="!h-3.5 shrink-0 !mx-0.5" />
+              {isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} selectorId={randomString(8)} className={'hover:bg-gray-200'} />}
               <CopyFeedback
                 content={isApp ? appUrl : apiUrl}
                 selectorId={randomString(8)}

+ 61 - 0
web/app/components/base/qrcode/index.tsx

@@ -0,0 +1,61 @@
+'use client'
+import React, { useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { debounce } from 'lodash-es'
+import QRCode from 'qrcode.react'
+import Tooltip from '../tooltip'
+import QrcodeStyle from './style.module.css'
+
+type Props = {
+  content: string
+  selectorId: string
+  className?: string
+}
+
+const prefixEmbedded = 'appOverview.overview.appInfo.qrcode.title'
+
+const ShareQRCode = ({ content, selectorId, className }: Props) => {
+  const { t } = useTranslation()
+  const [isShow, setisShow] = useState<boolean>(false)
+  const onClickShow = debounce(() => {
+    setisShow(true)
+  }, 100)
+
+  const downloadQR = () => {
+    const canvas = document.getElementsByTagName('canvas')[0]
+    const link = document.createElement('a')
+    link.download = 'qrcode.png'
+    link.href = canvas.toDataURL()
+    link.click()
+  }
+
+  const onMouseLeave = debounce(() => {
+    setisShow(false)
+  }, 500)
+
+  return (
+    <Tooltip
+      selector={`common-qrcode-show-${selectorId}`}
+      content={t(`${prefixEmbedded}`) || ''}
+    >
+      <div
+        className={`w-8 h-8 cursor-pointer rounded-lg ${className ?? ''}`}
+        onMouseLeave={onMouseLeave}
+        onClick={onClickShow}
+      >
+        <div className={`w-full h-full ${QrcodeStyle.QrcodeIcon} ${isShow ? QrcodeStyle.show : ''}`} />
+        {isShow && <div className={QrcodeStyle.qrcodeform}>
+          <QRCode size={160} value={content} className={QrcodeStyle.qrcodeimage}/>
+          <div className={QrcodeStyle.text}>
+            <div className={`text-gray-500 ${QrcodeStyle.scan}`}>{t('appOverview.overview.appInfo.qrcode.scan')}</div>
+            <div className={`text-gray-500 ${QrcodeStyle.scan}`}>·</div>
+            <div className={QrcodeStyle.download} onClick={downloadQR}>{t('appOverview.overview.appInfo.qrcode.download')}</div>
+          </div>
+        </div>
+        }
+      </div>
+    </Tooltip>
+  )
+}
+
+export default ShareQRCode

+ 61 - 0
web/app/components/base/qrcode/style.module.css

@@ -0,0 +1,61 @@
+.QrcodeIcon {
+  background-image: url(~@/app/components/develop/secret-key/assets/qrcode.svg);
+  background-position: center;
+  background-repeat: no-repeat;
+}
+
+.QrcodeIcon:hover {
+  background-image: url(~@/app/components/develop/secret-key/assets/qrcode-hover.svg);
+  background-position: center;
+  background-repeat: no-repeat;
+}
+
+.QrcodeIcon.show {
+  background-image: url(~@/app/components/develop/secret-key/assets/qrcode-hover.svg);
+  background-position: center;
+  background-repeat: no-repeat;
+}
+
+.qrcodeimage {
+  position: relative;
+  object-fit: cover;
+}
+.scan {
+  margin: 0;
+  line-height: 1rem;
+  font-size: 0.75rem;
+}
+.download {
+  position: relative;
+  color: #155eef;
+  font-size: 0.75rem;
+  line-height: 1rem;
+}
+.text {
+  align-self: stretch;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: center;
+  gap: 4px;
+}
+.qrcodeform {
+  border: 0.5px solid #eaecf0;
+  display: flex;
+  flex-direction: column;
+  margin: 0 !important;
+  margin-top: 4px !important;
+  margin-left: -75px !important;
+  position: absolute;
+  border-radius: 8px;
+  background-color: #fff;
+  box-shadow: 0 12px 16px -4px rgba(16, 24, 40, 0.08),
+    0 4px 6px -2px rgba(16, 24, 40, 0.03);
+  overflow: hidden;
+  align-items: center;
+  justify-content: center;
+  padding: 12px;
+  gap: 8px;
+  z-index: 3;
+  font-family: "PingFang SC", serif;
+}

File diff suppressed because it is too large
+ 4 - 0
web/app/components/develop/secret-key/assets/qrcode-hover.svg


File diff suppressed because it is too large
+ 4 - 0
web/app/components/develop/secret-key/assets/qrcode.svg


+ 5 - 0
web/i18n/lang/app-overview.en.ts

@@ -61,6 +61,11 @@ const translation = {
         copied: 'Copied',
         copy: 'Copy',
       },
+      qrcode: {
+        title: 'QR code to share',
+        scan: 'Scan Share Application',
+        download: 'Download QR Code',
+      },
       customize: {
         way: 'way',
         entry: 'Customize',

+ 5 - 0
web/i18n/lang/app-overview.zh.ts

@@ -61,6 +61,11 @@ const translation = {
         copied: '已复制',
         copy: '复制',
       },
+      qrcode: {
+        title: '二维码分享',
+        scan: '扫码分享应用',
+        download: '下载二维码',
+      },
       customize: {
         way: '方法',
         entry: '定制化',

+ 1 - 0
web/package.json

@@ -46,6 +46,7 @@
     "mermaid": "10.4.0",
     "negotiator": "^0.6.3",
     "next": "13.3.1",
+    "qrcode.react": "^3.1.0",
     "qs": "^6.11.1",
     "react": "^18.2.0",
     "react-18-input-autosize": "^3.0.0",