feat: add minapp popup

This commit is contained in:
kangfenmao 2024-08-14 19:47:58 +08:00
parent ebe74ffd05
commit ba5c70c45a
9 changed files with 189 additions and 10 deletions

View File

@ -6,7 +6,7 @@
<meta name="viewport" content="initial-scale=1, width=device-width" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; connect-src *; script-src 'self' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data:" />
content="default-src 'self'; connect-src *; script-src 'self' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data:; frame-src *" />
</head>
<body>
<div id="root"></div>

View File

@ -162,3 +162,40 @@ body,
.ant-segmented-group {
gap: 4px;
}
.dragable {
-webkit-app-region: drag;
}
.dragdisable {
-webkit-app-region: no-drag;
}
.minapp-drawer {
.ant-drawer-header-title {
flex-direction: row-reverse;
}
.ant-drawer-close {
position: absolute;
top: 6px;
right: 15px;
padding: 15px;
margin-right: -5px;
-webkit-app-region: no-drag;
z-index: 100000;
}
.ant-drawer-header {
height: calc(var(--navbar-height) + 0.5px);
background: var(--navbar-background);
width: calc(100vw - var(--sidebar-width));
padding-right: 10px !important;
border-bottom: 0.5px solid var(--color-border);
margin-top: -0.5px;
}
.ant-drawer-body {
padding: 0;
}
.minapp-mask {
background-color: transparent !important;
}
}

View File

@ -0,0 +1,125 @@
import { CloseOutlined, ExportOutlined, ReloadOutlined } from '@ant-design/icons'
import store from '@renderer/store'
import { setMinappShow } from '@renderer/store/runtime'
import { Drawer } from 'antd'
import { useRef, useState } from 'react'
import styled from 'styled-components'
import { TopView } from '../TopView'
interface ShowParams {
title: string
url: string
}
interface Props extends ShowParams {
resolve: (data: any) => void
}
const PopupContainer: React.FC<Props> = ({ title, url, resolve }) => {
const [open, setOpen] = useState(true)
const iframeRef = useRef<HTMLIFrameElement>(null)
const onClose = () => {
setOpen(false)
setTimeout(() => resolve({}), 300)
}
const onReload = () => {
if (iframeRef.current) {
iframeRef.current.src = url
}
}
const onOpenLink = () => {
window.api.openWebsite(url)
}
return (
<Drawer
title={title}
placement="bottom"
onClose={onClose}
open={open}
mask={true}
rootClassName="minapp-drawer"
maskClassName="minapp-mask"
height={'100%'}
maskClosable={false}
closeIcon={null}
style={{ marginLeft: 'var(--sidebar-width)' }}>
<Frame src={url} ref={iframeRef} />
<ButtonsGroup>
<Button onClick={onReload}>
<ReloadOutlined />
</Button>
<Button onClick={onOpenLink}>
<ExportOutlined />
</Button>
<Button onClick={onClose}>
<CloseOutlined />
</Button>
</ButtonsGroup>
</Drawer>
)
}
const Frame = styled.iframe`
width: calc(100vw - var(--sidebar-width));
height: calc(100vh - var(--navbar-height));
border: none;
`
const ButtonsGroup = styled.div`
position: absolute;
top: 0;
right: 0;
height: var(--navbar-height);
display: flex;
flex-direction: row;
align-items: center;
gap: 5px;
padding: 0 10px;
`
const Button = styled.div`
-webkit-app-region: no-drag;
cursor: pointer;
width: 30px;
height: 30px;
border-radius: 5px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
color: var(--color-text-2);
transition: all 0.2s ease;
font-size: 14px;
&:hover {
color: var(--color-text-1);
background-color: var(--color-background-mute);
}
`
export default class MinApp {
static topviewId = 0
static close() {
TopView.hide('MinApp')
store.dispatch(setMinappShow(false))
}
static start(props: ShowParams) {
store.dispatch(setMinappShow(true))
return new Promise<any>((resolve) => {
TopView.show(
<PopupContainer
{...props}
resolve={(v) => {
resolve(v)
this.close()
}}
/>,
'MinApp'
)
})
}
}

View File

@ -1,11 +1,19 @@
import { isMac } from '@renderer/config/constant'
import { useRuntime } from '@renderer/hooks/useStore'
import { FC, PropsWithChildren } from 'react'
import styled from 'styled-components'
type Props = PropsWithChildren & JSX.IntrinsicElements['div']
export const Navbar: FC<Props> = ({ children, ...props }) => {
return <NavbarContainer {...props}>{children}</NavbarContainer>
const { minappShow } = useRuntime()
const backgroundColor = minappShow ? 'var(--color-background)' : 'var(--navbar-background)'
return (
<NavbarContainer {...props} style={{ backgroundColor }}>
{children}
</NavbarContainer>
)
}
export const NavbarLeft: FC<Props> = ({ children, ...props }) => {
@ -26,11 +34,12 @@ const NavbarContainer = styled.div`
flex-direction: row;
min-height: var(--navbar-height);
max-height: var(--navbar-height);
-webkit-app-region: drag;
margin-left: calc(var(--sidebar-width) * -1);
padding-left: ${isMac ? 'var(--sidebar-width)' : 0};
border-bottom: 0.5px solid var(--color-border);
background-color: var(--navbar-background);
transition: background-color 0.3s ease;
-webkit-app-region: drag;
`
const NavbarLeftContainer = styled.div`
@ -56,5 +65,5 @@ const NavbarRightContainer = styled.div`
min-width: var(--settings-width);
display: flex;
align-items: center;
padding: 0 16px;
padding: 0 12px;
`

View File

@ -1,6 +1,7 @@
import { TranslationOutlined } from '@ant-design/icons'
import Logo from '@renderer/assets/images/logo.png'
import useAvatar from '@renderer/hooks/useAvatar'
import { useRuntime } from '@renderer/hooks/useStore'
import { FC } from 'react'
import { Link, useLocation } from 'react-router-dom'
import styled from 'styled-components'
@ -8,11 +9,12 @@ import styled from 'styled-components'
const Sidebar: FC = () => {
const { pathname } = useLocation()
const avatar = useAvatar()
const { minappShow } = useRuntime()
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
return (
<Container>
<Container style={{ backgroundColor: minappShow ? 'var(--color-background)' : 'var(--sidebar-background)' }}>
<StyledLink to="/">
<AvatarImg src={avatar || Logo} draggable={false} />
</StyledLink>
@ -58,6 +60,7 @@ const Container = styled.div`
margin-top: var(--navbar-height);
margin-bottom: var(--navbar-height);
background-color: var(--sidebar-background);
transition: background-color 0.3s ease;
`
const AvatarImg = styled.img`

View File

@ -51,7 +51,7 @@ const HomePage: FC = () => {
</NavbarLeft>
)}
<Navigation activeAssistant={activeAssistant} />
<NavbarRight style={{ justifyContent: 'flex-end', paddingRight: isWindows ? 140 : 8 }}>
<NavbarRight style={{ justifyContent: 'flex-end', paddingRight: isWindows ? 140 : 12 }}>
<ThemeSwitch
checkedChildren={<i className="iconfont icon-theme icon-dark1" />}
unCheckedChildren={<i className="iconfont icon-theme icon-theme-light" />}

View File

@ -148,7 +148,7 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
<HelpTextRow>
<HelpText>{t('settings.provider.docs_check')} </HelpText>
<HelpLink target="_blank" href={docsWebsite}>
{t(`provider.${provider.id}`)}
{t(`provider.${provider.id}`) + ' '}
{t('common.docs')}
</HelpLink>
<HelpText>{t('common.and')}</HelpText>

View File

@ -167,7 +167,7 @@ const ProviderList = styled.div`
flex: 1;
flex-direction: column;
height: calc(100vh - var(--navbar-height));
overflow: scroll;
overflow: auto;
padding: 8px;
`

View File

@ -4,11 +4,13 @@ import Logo from '@renderer/assets/images/logo.png'
export interface RuntimeState {
avatar: string
generating: boolean
minappShow: boolean
}
const initialState: RuntimeState = {
avatar: Logo,
generating: false
generating: false,
minappShow: false
}
const runtimeSlice = createSlice({
@ -20,10 +22,13 @@ const runtimeSlice = createSlice({
},
setGenerating: (state, action: PayloadAction<boolean>) => {
state.generating = action.payload
},
setMinappShow: (state, action: PayloadAction<boolean>) => {
state.minappShow = action.payload
}
}
})
export const { setAvatar, setGenerating } = runtimeSlice.actions
export const { setAvatar, setGenerating, setMinappShow } = runtimeSlice.actions
export default runtimeSlice.reducer