mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-22 00:13:09 +08:00
feat(minapp): add Google login tip for untrusted browser issue (#8230)
* feat(minapp): add Google login tip for untrusted browser issue * feat(miniapp): add open Google button for Google Login popup * feat(minapp): replace custom alert with Ant Design Alert component
This commit is contained in:
parent
4a4d861592
commit
ee32942f71
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@ -10,7 +10,7 @@
|
|||||||
"windows": {
|
"windows": {
|
||||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
|
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
|
||||||
},
|
},
|
||||||
"runtimeArgs": ["--sourcemap"],
|
"runtimeArgs": ["--inspect", "--sourcemap"],
|
||||||
"env": {
|
"env": {
|
||||||
"REMOTE_DEBUGGING_PORT": "9222"
|
"REMOTE_DEBUGGING_PORT": "9222"
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@
|
|||||||
"request": "attach",
|
"request": "attach",
|
||||||
"type": "chrome",
|
"type": "chrome",
|
||||||
"webRoot": "${workspaceFolder}/src/renderer",
|
"webRoot": "${workspaceFolder}/src/renderer",
|
||||||
"timeout": 60000,
|
"timeout": 3000000,
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"hidden": true
|
"hidden": true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { useAppDispatch } from '@renderer/store'
|
|||||||
import { setMinappsOpenLinkExternal } from '@renderer/store/settings'
|
import { setMinappsOpenLinkExternal } from '@renderer/store/settings'
|
||||||
import { MinAppType } from '@renderer/types'
|
import { MinAppType } from '@renderer/types'
|
||||||
import { delay } from '@renderer/utils'
|
import { delay } from '@renderer/utils'
|
||||||
import { Avatar, Drawer, Tooltip } from 'antd'
|
import { Alert, Avatar, Button, Drawer, Tooltip } from 'antd'
|
||||||
import { WebviewTag } from 'electron'
|
import { WebviewTag } from 'electron'
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -39,6 +39,100 @@ interface AppExtraInfo {
|
|||||||
|
|
||||||
type AppInfo = MinAppType & AppExtraInfo
|
type AppInfo = MinAppType & AppExtraInfo
|
||||||
|
|
||||||
|
/** Google login tip component */
|
||||||
|
const GoogleLoginTip = ({
|
||||||
|
isReady,
|
||||||
|
currentUrl,
|
||||||
|
currentAppId
|
||||||
|
}: {
|
||||||
|
appId?: string | null
|
||||||
|
isReady: boolean
|
||||||
|
currentUrl: string | null
|
||||||
|
currentAppId: string | null
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [visible, setVisible] = useState(false)
|
||||||
|
const { openMinappById } = useMinappPopup()
|
||||||
|
|
||||||
|
// 判断当前URL是否涉及Google登录
|
||||||
|
const needsGoogleLogin = useMemo(() => {
|
||||||
|
// 如果当前已经在Google小程序中,不需要显示提示
|
||||||
|
if (currentAppId === 'google') return false
|
||||||
|
|
||||||
|
if (!currentUrl) return false
|
||||||
|
|
||||||
|
const googleLoginPatterns = [
|
||||||
|
'accounts.google.com',
|
||||||
|
'signin/oauth',
|
||||||
|
'auth/google',
|
||||||
|
'login/google',
|
||||||
|
'sign-in/google',
|
||||||
|
'google.com/signin',
|
||||||
|
'gsi/client'
|
||||||
|
]
|
||||||
|
|
||||||
|
return googleLoginPatterns.some((pattern) => currentUrl.toLowerCase().includes(pattern.toLowerCase()))
|
||||||
|
}, [currentUrl, currentAppId])
|
||||||
|
|
||||||
|
// 在URL更新时检查是否需要显示提示
|
||||||
|
useEffect(() => {
|
||||||
|
let showTimer: NodeJS.Timeout | null = null
|
||||||
|
let hideTimer: NodeJS.Timeout | null = null
|
||||||
|
|
||||||
|
// 如果是Google登录相关URL且小程序已加载完成,则延迟显示提示
|
||||||
|
if (needsGoogleLogin && isReady) {
|
||||||
|
showTimer = setTimeout(() => {
|
||||||
|
setVisible(true)
|
||||||
|
hideTimer = setTimeout(() => {
|
||||||
|
setVisible(false)
|
||||||
|
}, 30000)
|
||||||
|
}, 500)
|
||||||
|
} else {
|
||||||
|
setVisible(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (showTimer) clearTimeout(showTimer)
|
||||||
|
if (hideTimer) clearTimeout(hideTimer)
|
||||||
|
}
|
||||||
|
}, [needsGoogleLogin, isReady, currentUrl])
|
||||||
|
|
||||||
|
// 处理关闭提示
|
||||||
|
const handleClose = () => {
|
||||||
|
setVisible(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转到Google小程序
|
||||||
|
const openGoogleMinApp = () => {
|
||||||
|
// 使用openMinappById方法打开Google小程序
|
||||||
|
openMinappById('google', true)
|
||||||
|
// 关闭提示
|
||||||
|
setVisible(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只在需要Google登录时显示提示
|
||||||
|
if (!needsGoogleLogin || !visible) return null
|
||||||
|
|
||||||
|
// 使用直接的消息文本
|
||||||
|
const message = t('miniwindow.alert.google_login')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Alert
|
||||||
|
message={message}
|
||||||
|
type="warning"
|
||||||
|
showIcon
|
||||||
|
closable
|
||||||
|
onClose={handleClose}
|
||||||
|
action={
|
||||||
|
<Button type="primary" size="small" onClick={openGoogleMinApp}>
|
||||||
|
{t('common.open')} Google
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
style={{ zIndex: 10, animation: 'fadeIn 0.3s ease-in-out' }}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/** The main container for MinApp popup */
|
/** The main container for MinApp popup */
|
||||||
const MinappPopupContainer: React.FC = () => {
|
const MinappPopupContainer: React.FC = () => {
|
||||||
const { openedKeepAliveMinapps, openedOneOffMinapp, currentMinappId, minappShow } = useRuntime()
|
const { openedKeepAliveMinapps, openedOneOffMinapp, currentMinappId, minappShow } = useRuntime()
|
||||||
@ -198,9 +292,11 @@ const MinappPopupContainer: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** the callback function to handle the webview navigate to new url */
|
/** the callback function to handle webview navigation */
|
||||||
const handleWebviewNavigate = (appid: string, url: string) => {
|
const handleWebviewNavigate = (appid: string, url: string) => {
|
||||||
|
// 记录当前URL,用于GoogleLoginTip判断
|
||||||
if (appid === currentMinappId) {
|
if (appid === currentMinappId) {
|
||||||
|
console.log('URL changed:', url)
|
||||||
setCurrentUrl(url)
|
setCurrentUrl(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -297,36 +393,36 @@ const MinappPopupContainer: React.FC = () => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
{appInfo.canOpenExternalLink && (
|
{appInfo.canOpenExternalLink && (
|
||||||
<Tooltip title={t('minapp.popup.openExternal')} mouseEnterDelay={0.8} placement="bottom">
|
<Tooltip title={t('minapp.popup.openExternal')} mouseEnterDelay={0.8} placement="bottom">
|
||||||
<Button onClick={() => handleOpenLink(url ?? appInfo.url)}>
|
<TitleButton onClick={() => handleOpenLink(url ?? appInfo.url)}>
|
||||||
<ExportOutlined />
|
<ExportOutlined />
|
||||||
</Button>
|
</TitleButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<ButtonsGroup className={isWin || isLinux ? 'windows' : ''}>
|
<ButtonsGroup className={isWin || isLinux ? 'windows' : ''}>
|
||||||
<Tooltip title={t('minapp.popup.goBack')} mouseEnterDelay={0.8} placement="bottom">
|
<Tooltip title={t('minapp.popup.goBack')} mouseEnterDelay={0.8} placement="bottom">
|
||||||
<Button onClick={() => handleGoBack(appInfo.id)}>
|
<TitleButton onClick={() => handleGoBack(appInfo.id)}>
|
||||||
<ArrowLeftOutlined />
|
<ArrowLeftOutlined />
|
||||||
</Button>
|
</TitleButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title={t('minapp.popup.goForward')} mouseEnterDelay={0.8} placement="bottom">
|
<Tooltip title={t('minapp.popup.goForward')} mouseEnterDelay={0.8} placement="bottom">
|
||||||
<Button onClick={() => handleGoForward(appInfo.id)}>
|
<TitleButton onClick={() => handleGoForward(appInfo.id)}>
|
||||||
<ArrowRightOutlined />
|
<ArrowRightOutlined />
|
||||||
</Button>
|
</TitleButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title={t('minapp.popup.refresh')} mouseEnterDelay={0.8} placement="bottom">
|
<Tooltip title={t('minapp.popup.refresh')} mouseEnterDelay={0.8} placement="bottom">
|
||||||
<Button onClick={() => handleReload(appInfo.id)}>
|
<TitleButton onClick={() => handleReload(appInfo.id)}>
|
||||||
<ReloadOutlined />
|
<ReloadOutlined />
|
||||||
</Button>
|
</TitleButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{appInfo.canPinned && (
|
{appInfo.canPinned && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={appInfo.isPinned ? t('minapp.sidebar.remove.title') : t('minapp.sidebar.add.title')}
|
title={appInfo.isPinned ? t('minapp.sidebar.remove.title') : t('minapp.sidebar.add.title')}
|
||||||
mouseEnterDelay={0.8}
|
mouseEnterDelay={0.8}
|
||||||
placement="bottom">
|
placement="bottom">
|
||||||
<Button onClick={() => handleTogglePin(appInfo.id)} className={appInfo.isPinned ? 'pinned' : ''}>
|
<TitleButton onClick={() => handleTogglePin(appInfo.id)} className={appInfo.isPinned ? 'pinned' : ''}>
|
||||||
<PushpinOutlined style={{ fontSize: 16 }} />
|
<PushpinOutlined style={{ fontSize: 16 }} />
|
||||||
</Button>
|
</TitleButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@ -337,28 +433,28 @@ const MinappPopupContainer: React.FC = () => {
|
|||||||
}
|
}
|
||||||
mouseEnterDelay={0.8}
|
mouseEnterDelay={0.8}
|
||||||
placement="bottom">
|
placement="bottom">
|
||||||
<Button onClick={handleToggleOpenExternal} className={minappsOpenLinkExternal ? 'open-external' : ''}>
|
<TitleButton onClick={handleToggleOpenExternal} className={minappsOpenLinkExternal ? 'open-external' : ''}>
|
||||||
<LinkOutlined />
|
<LinkOutlined />
|
||||||
</Button>
|
</TitleButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{isInDevelopment && (
|
{isInDevelopment && (
|
||||||
<Tooltip title={t('minapp.popup.devtools')} mouseEnterDelay={0.8} placement="bottom">
|
<Tooltip title={t('minapp.popup.devtools')} mouseEnterDelay={0.8} placement="bottom">
|
||||||
<Button onClick={() => handleOpenDevTools(appInfo.id)}>
|
<TitleButton onClick={() => handleOpenDevTools(appInfo.id)}>
|
||||||
<CodeOutlined />
|
<CodeOutlined />
|
||||||
</Button>
|
</TitleButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{canMinimize && (
|
{canMinimize && (
|
||||||
<Tooltip title={t('minapp.popup.minimize')} mouseEnterDelay={0.8} placement="bottom">
|
<Tooltip title={t('minapp.popup.minimize')} mouseEnterDelay={0.8} placement="bottom">
|
||||||
<Button onClick={() => handlePopupMinimize()}>
|
<TitleButton onClick={() => handlePopupMinimize()}>
|
||||||
<MinusOutlined />
|
<MinusOutlined />
|
||||||
</Button>
|
</TitleButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Tooltip title={t('minapp.popup.close')} mouseEnterDelay={0.8} placement="bottom">
|
<Tooltip title={t('minapp.popup.close')} mouseEnterDelay={0.8} placement="bottom">
|
||||||
<Button onClick={() => handlePopupClose(appInfo.id)}>
|
<TitleButton onClick={() => handlePopupClose(appInfo.id)}>
|
||||||
<CloseOutlined />
|
<CloseOutlined />
|
||||||
</Button>
|
</TitleButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ButtonsGroup>
|
</ButtonsGroup>
|
||||||
</TitleContainer>
|
</TitleContainer>
|
||||||
@ -399,6 +495,8 @@ const MinappPopupContainer: React.FC = () => {
|
|||||||
marginLeft: 'var(--sidebar-width)',
|
marginLeft: 'var(--sidebar-width)',
|
||||||
backgroundColor: window.root.style.background
|
backgroundColor: window.root.style.background
|
||||||
}}>
|
}}>
|
||||||
|
{/* 在所有小程序中显示GoogleLoginTip */}
|
||||||
|
<GoogleLoginTip isReady={isReady} currentUrl={currentUrl} currentAppId={currentMinappId} />
|
||||||
{!isReady && (
|
{!isReady && (
|
||||||
<EmptyView>
|
<EmptyView>
|
||||||
<Avatar
|
<Avatar
|
||||||
@ -460,7 +558,7 @@ const ButtonsGroup = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const Button = styled.div`
|
const TitleButton = styled.div`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
|||||||
@ -437,6 +437,7 @@
|
|||||||
"more": "More",
|
"more": "More",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"no_results": "No results",
|
"no_results": "No results",
|
||||||
|
"open": "Open",
|
||||||
"paste": "Paste",
|
"paste": "Paste",
|
||||||
"prompt": "Prompt",
|
"prompt": "Prompt",
|
||||||
"provider": "Provider",
|
"provider": "Provider",
|
||||||
@ -813,6 +814,9 @@
|
|||||||
"title": "MinApp"
|
"title": "MinApp"
|
||||||
},
|
},
|
||||||
"miniwindow": {
|
"miniwindow": {
|
||||||
|
"alert": {
|
||||||
|
"google_login": "Tip: If you see a 'browser not trusted' message when logging into Google, please first login through the Google mini app in the mini app list, then use Google login in other mini apps"
|
||||||
|
},
|
||||||
"clipboard": {
|
"clipboard": {
|
||||||
"empty": "Clipboard is empty"
|
"empty": "Clipboard is empty"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -437,6 +437,7 @@
|
|||||||
"more": "もっと",
|
"more": "もっと",
|
||||||
"name": "名前",
|
"name": "名前",
|
||||||
"no_results": "検索結果なし",
|
"no_results": "検索結果なし",
|
||||||
|
"open": "開く",
|
||||||
"paste": "貼り付け",
|
"paste": "貼り付け",
|
||||||
"prompt": "プロンプト",
|
"prompt": "プロンプト",
|
||||||
"provider": "プロバイダー",
|
"provider": "プロバイダー",
|
||||||
@ -813,6 +814,9 @@
|
|||||||
"title": "ミニアプリ"
|
"title": "ミニアプリ"
|
||||||
},
|
},
|
||||||
"miniwindow": {
|
"miniwindow": {
|
||||||
|
"alert": {
|
||||||
|
"google_login": "ヒント:Googleログイン時に「信頼できないブラウザ」というメッセージが表示された場合は、先にミニアプリリストのGoogleミニアプリでアカウントログインを完了してから、他のミニアプリでGoogleログインを使用してください"
|
||||||
|
},
|
||||||
"clipboard": {
|
"clipboard": {
|
||||||
"empty": "クリップボードが空です"
|
"empty": "クリップボードが空です"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -437,6 +437,7 @@
|
|||||||
"more": "Ещё",
|
"more": "Ещё",
|
||||||
"name": "Имя",
|
"name": "Имя",
|
||||||
"no_results": "Результатов не найдено",
|
"no_results": "Результатов не найдено",
|
||||||
|
"open": "Открыть",
|
||||||
"paste": "Вставить",
|
"paste": "Вставить",
|
||||||
"prompt": "Промпт",
|
"prompt": "Промпт",
|
||||||
"provider": "Провайдер",
|
"provider": "Провайдер",
|
||||||
@ -813,6 +814,9 @@
|
|||||||
"title": "Встроенные приложения"
|
"title": "Встроенные приложения"
|
||||||
},
|
},
|
||||||
"miniwindow": {
|
"miniwindow": {
|
||||||
|
"alert": {
|
||||||
|
"google_login": "Совет: Если при входе в Google вы видите сообщение 'ненадежный браузер', сначала войдите в аккаунт через мини-приложение Google в списке мини-приложений, а затем используйте вход через Google в других мини-приложениях"
|
||||||
|
},
|
||||||
"clipboard": {
|
"clipboard": {
|
||||||
"empty": "Буфер обмена пуст"
|
"empty": "Буфер обмена пуст"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -437,6 +437,7 @@
|
|||||||
"more": "更多",
|
"more": "更多",
|
||||||
"name": "名称",
|
"name": "名称",
|
||||||
"no_results": "无结果",
|
"no_results": "无结果",
|
||||||
|
"open": "打开",
|
||||||
"paste": "粘贴",
|
"paste": "粘贴",
|
||||||
"prompt": "提示词",
|
"prompt": "提示词",
|
||||||
"provider": "提供商",
|
"provider": "提供商",
|
||||||
@ -813,6 +814,9 @@
|
|||||||
"title": "小程序"
|
"title": "小程序"
|
||||||
},
|
},
|
||||||
"miniwindow": {
|
"miniwindow": {
|
||||||
|
"alert": {
|
||||||
|
"google_login": "提示:如遇到Google登录提示\"不受信任的浏览器\",请先在小程序列表中的Google小程序中完成账号登录,再在其它小程序使用Google登录"
|
||||||
|
},
|
||||||
"clipboard": {
|
"clipboard": {
|
||||||
"empty": "剪贴板为空"
|
"empty": "剪贴板为空"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -437,6 +437,7 @@
|
|||||||
"more": "更多",
|
"more": "更多",
|
||||||
"name": "名稱",
|
"name": "名稱",
|
||||||
"no_results": "沒有結果",
|
"no_results": "沒有結果",
|
||||||
|
"open": "開啟",
|
||||||
"paste": "貼上",
|
"paste": "貼上",
|
||||||
"prompt": "提示詞",
|
"prompt": "提示詞",
|
||||||
"provider": "供應商",
|
"provider": "供應商",
|
||||||
@ -813,6 +814,9 @@
|
|||||||
"title": "小工具"
|
"title": "小工具"
|
||||||
},
|
},
|
||||||
"miniwindow": {
|
"miniwindow": {
|
||||||
|
"alert": {
|
||||||
|
"google_login": "提示:如遇到Google登入提示\"不受信任的瀏覽器\",請先在小程序列表中的Google小程序中完成帳號登入,再在其它小程序使用Google登入"
|
||||||
|
},
|
||||||
"clipboard": {
|
"clipboard": {
|
||||||
"empty": "剪貼簿為空"
|
"empty": "剪貼簿為空"
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user