feat: add minapp popup
This commit is contained in:
parent
ebe74ffd05
commit
ba5c70c45a
@ -6,7 +6,7 @@
|
|||||||
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
<meta name="viewport" content="initial-scale=1, width=device-width" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@ -162,3 +162,40 @@ body,
|
|||||||
.ant-segmented-group {
|
.ant-segmented-group {
|
||||||
gap: 4px;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
125
src/renderer/src/components/MinApp/index.tsx
Normal file
125
src/renderer/src/components/MinApp/index.tsx
Normal 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'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,19 @@
|
|||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
|
import { useRuntime } from '@renderer/hooks/useStore'
|
||||||
import { FC, PropsWithChildren } from 'react'
|
import { FC, PropsWithChildren } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
type Props = PropsWithChildren & JSX.IntrinsicElements['div']
|
type Props = PropsWithChildren & JSX.IntrinsicElements['div']
|
||||||
|
|
||||||
export const Navbar: FC<Props> = ({ children, ...props }) => {
|
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 }) => {
|
export const NavbarLeft: FC<Props> = ({ children, ...props }) => {
|
||||||
@ -26,11 +34,12 @@ const NavbarContainer = styled.div`
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
min-height: var(--navbar-height);
|
min-height: var(--navbar-height);
|
||||||
max-height: var(--navbar-height);
|
max-height: var(--navbar-height);
|
||||||
-webkit-app-region: drag;
|
|
||||||
margin-left: calc(var(--sidebar-width) * -1);
|
margin-left: calc(var(--sidebar-width) * -1);
|
||||||
padding-left: ${isMac ? 'var(--sidebar-width)' : 0};
|
padding-left: ${isMac ? 'var(--sidebar-width)' : 0};
|
||||||
border-bottom: 0.5px solid var(--color-border);
|
border-bottom: 0.5px solid var(--color-border);
|
||||||
background-color: var(--navbar-background);
|
background-color: var(--navbar-background);
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
-webkit-app-region: drag;
|
||||||
`
|
`
|
||||||
|
|
||||||
const NavbarLeftContainer = styled.div`
|
const NavbarLeftContainer = styled.div`
|
||||||
@ -56,5 +65,5 @@ const NavbarRightContainer = styled.div`
|
|||||||
min-width: var(--settings-width);
|
min-width: var(--settings-width);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 16px;
|
padding: 0 12px;
|
||||||
`
|
`
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { TranslationOutlined } from '@ant-design/icons'
|
import { TranslationOutlined } from '@ant-design/icons'
|
||||||
import Logo from '@renderer/assets/images/logo.png'
|
import Logo from '@renderer/assets/images/logo.png'
|
||||||
import useAvatar from '@renderer/hooks/useAvatar'
|
import useAvatar from '@renderer/hooks/useAvatar'
|
||||||
|
import { useRuntime } from '@renderer/hooks/useStore'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import { Link, useLocation } from 'react-router-dom'
|
import { Link, useLocation } from 'react-router-dom'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -8,11 +9,12 @@ import styled from 'styled-components'
|
|||||||
const Sidebar: FC = () => {
|
const Sidebar: FC = () => {
|
||||||
const { pathname } = useLocation()
|
const { pathname } = useLocation()
|
||||||
const avatar = useAvatar()
|
const avatar = useAvatar()
|
||||||
|
const { minappShow } = useRuntime()
|
||||||
|
|
||||||
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
|
const isRoute = (path: string): string => (pathname === path ? 'active' : '')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container style={{ backgroundColor: minappShow ? 'var(--color-background)' : 'var(--sidebar-background)' }}>
|
||||||
<StyledLink to="/">
|
<StyledLink to="/">
|
||||||
<AvatarImg src={avatar || Logo} draggable={false} />
|
<AvatarImg src={avatar || Logo} draggable={false} />
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
@ -58,6 +60,7 @@ const Container = styled.div`
|
|||||||
margin-top: var(--navbar-height);
|
margin-top: var(--navbar-height);
|
||||||
margin-bottom: var(--navbar-height);
|
margin-bottom: var(--navbar-height);
|
||||||
background-color: var(--sidebar-background);
|
background-color: var(--sidebar-background);
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
`
|
`
|
||||||
|
|
||||||
const AvatarImg = styled.img`
|
const AvatarImg = styled.img`
|
||||||
|
|||||||
@ -51,7 +51,7 @@ const HomePage: FC = () => {
|
|||||||
</NavbarLeft>
|
</NavbarLeft>
|
||||||
)}
|
)}
|
||||||
<Navigation activeAssistant={activeAssistant} />
|
<Navigation activeAssistant={activeAssistant} />
|
||||||
<NavbarRight style={{ justifyContent: 'flex-end', paddingRight: isWindows ? 140 : 8 }}>
|
<NavbarRight style={{ justifyContent: 'flex-end', paddingRight: isWindows ? 140 : 12 }}>
|
||||||
<ThemeSwitch
|
<ThemeSwitch
|
||||||
checkedChildren={<i className="iconfont icon-theme icon-dark1" />}
|
checkedChildren={<i className="iconfont icon-theme icon-dark1" />}
|
||||||
unCheckedChildren={<i className="iconfont icon-theme icon-theme-light" />}
|
unCheckedChildren={<i className="iconfont icon-theme icon-theme-light" />}
|
||||||
|
|||||||
@ -148,7 +148,7 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
|||||||
<HelpTextRow>
|
<HelpTextRow>
|
||||||
<HelpText>{t('settings.provider.docs_check')} </HelpText>
|
<HelpText>{t('settings.provider.docs_check')} </HelpText>
|
||||||
<HelpLink target="_blank" href={docsWebsite}>
|
<HelpLink target="_blank" href={docsWebsite}>
|
||||||
{t(`provider.${provider.id}`)}
|
{t(`provider.${provider.id}`) + ' '}
|
||||||
{t('common.docs')}
|
{t('common.docs')}
|
||||||
</HelpLink>
|
</HelpLink>
|
||||||
<HelpText>{t('common.and')}</HelpText>
|
<HelpText>{t('common.and')}</HelpText>
|
||||||
|
|||||||
@ -167,7 +167,7 @@ const ProviderList = styled.div`
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: calc(100vh - var(--navbar-height));
|
height: calc(100vh - var(--navbar-height));
|
||||||
overflow: scroll;
|
overflow: auto;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@ -4,11 +4,13 @@ import Logo from '@renderer/assets/images/logo.png'
|
|||||||
export interface RuntimeState {
|
export interface RuntimeState {
|
||||||
avatar: string
|
avatar: string
|
||||||
generating: boolean
|
generating: boolean
|
||||||
|
minappShow: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: RuntimeState = {
|
const initialState: RuntimeState = {
|
||||||
avatar: Logo,
|
avatar: Logo,
|
||||||
generating: false
|
generating: false,
|
||||||
|
minappShow: false
|
||||||
}
|
}
|
||||||
|
|
||||||
const runtimeSlice = createSlice({
|
const runtimeSlice = createSlice({
|
||||||
@ -20,10 +22,13 @@ const runtimeSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setGenerating: (state, action: PayloadAction<boolean>) => {
|
setGenerating: (state, action: PayloadAction<boolean>) => {
|
||||||
state.generating = action.payload
|
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
|
export default runtimeSlice.reducer
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user