feat: MinApp added to the sidebar does not support direct hiding.

This commit is contained in:
hxp0618 2025-01-10 16:32:59 +08:00 committed by kangfenmao
parent ad9fb9aa6d
commit 7633d70435
9 changed files with 125 additions and 48 deletions

View File

@ -1,10 +1,11 @@
import { Center } from '@renderer/components/Layout' import { Center } from '@renderer/components/Layout'
import { getAllMinApps } from '@renderer/config/minapps' import { getAllMinApps } from '@renderer/config/minapps'
import { useSettings } from '@renderer/hooks/useSettings'
import App from '@renderer/pages/apps/App' import App from '@renderer/pages/apps/App'
import { Popover } from 'antd' import { Popover } from 'antd'
import { Empty } from 'antd' import { Empty } from 'antd'
import { isEmpty } from 'lodash' import { isEmpty } from 'lodash'
import { FC, useState } from 'react' import { FC, useMemo, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import styled from 'styled-components' import styled from 'styled-components'
@ -16,7 +17,14 @@ interface Props {
const AppStorePopover: FC<Props> = ({ children }) => { const AppStorePopover: FC<Props> = ({ children }) => {
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const apps = getAllMinApps() const { miniAppIcons } = useSettings()
const allApps = useMemo(() => getAllMinApps(), [])
// 只显示可见的小程序
const visibleApps = useMemo(() => {
if (!miniAppIcons?.visible) return allApps
return allApps.filter((app) => miniAppIcons.visible.includes(app.id))
}, [allApps, miniAppIcons?.visible])
useHotkeys('esc', () => { useHotkeys('esc', () => {
setOpen(false) setOpen(false)
@ -29,10 +37,10 @@ const AppStorePopover: FC<Props> = ({ children }) => {
const content = ( const content = (
<PopoverContent> <PopoverContent>
<AppsContainer> <AppsContainer>
{apps.map((app) => ( {visibleApps.map((app) => (
<App key={app.id} app={app} onClick={handleClose} size={50} /> <App key={app.id} app={app} onClick={handleClose} size={50} />
))} ))}
{isEmpty(apps) && ( {isEmpty(visibleApps) && (
<Center> <Center>
<Empty /> <Empty />
</Center> </Center>

View File

@ -411,6 +411,7 @@
"display.minApp.visible": "Visible MinApp", "display.minApp.visible": "Visible MinApp",
"display.minApp.disabled": "Hidden MinApp", "display.minApp.disabled": "Hidden MinApp",
"display.minApp.empty": "Drag minApp from the left to hide them here", "display.minApp.empty": "Drag minApp from the left to hide them here",
"display.minApp.pinnedError": "MinApp that have been added to the sidebar do not support hiding. If you want to hide them, please remove them from the sidebar first.",
"display.topic.title": "Topic Settings", "display.topic.title": "Topic Settings",
"display.custom.css": "Custom CSS", "display.custom.css": "Custom CSS",
"display.custom.css.placeholder": "/* Put custom CSS here */", "display.custom.css.placeholder": "/* Put custom CSS here */",

View File

@ -412,6 +412,7 @@
"display.minApp.visible": "表示中ミニプログラム", "display.minApp.visible": "表示中ミニプログラム",
"display.minApp.disabled": "非表示ミニプログラム", "display.minApp.disabled": "非表示ミニプログラム",
"display.minApp.empty": "非表示にしたいアプレットを左からここまでドラッグします", "display.minApp.empty": "非表示にしたいアプレットを左からここまでドラッグします",
"display.minApp.pinnedError": "サイドバーに追加されたミニ プログラムは非表示をサポートしていません。非表示にしたい場合は、まずサイドバーから削除してください",
"input.auto_translate_with_space": "スペースを3回押して翻訳", "input.auto_translate_with_space": "スペースを3回押して翻訳",
"messages.divider": "メッセージ間に区切り線を表示", "messages.divider": "メッセージ間に区切り線を表示",
"messages.input.paste_long_text_as_file": "長いテキストをファイルとして貼り付け", "messages.input.paste_long_text_as_file": "長いテキストをファイルとして貼り付け",

View File

@ -411,6 +411,7 @@
"display.minApp.visible": "Отображаемый апплет", "display.minApp.visible": "Отображаемый апплет",
"display.minApp.disabled": "скрытый апплет", "display.minApp.disabled": "скрытый апплет",
"display.minApp.empty": "Перетащите апплет, который хотите скрыть, слева сюда", "display.minApp.empty": "Перетащите апплет, который хотите скрыть, слева сюда",
"display.minApp.pinnedError": "Мини-программы, добавленные на боковую панель, не поддерживают скрытие. Если вы хотите скрыть их, сначала удалите их с боковой панели",
"display.topic.title": "Настройки топиков", "display.topic.title": "Настройки топиков",
"display.custom.css": "Пользовательский CSS", "display.custom.css": "Пользовательский CSS",
"display.custom.css.placeholder": "/* Здесь введите пользовательский CSS */", "display.custom.css.placeholder": "/* Здесь введите пользовательский CSS */",

View File

@ -412,6 +412,7 @@
"display.minApp.visible": "显示的小程序", "display.minApp.visible": "显示的小程序",
"display.minApp.disabled": "隐藏的小程序", "display.minApp.disabled": "隐藏的小程序",
"display.minApp.empty": "把要隐藏的小程序从左侧拖拽到这里", "display.minApp.empty": "把要隐藏的小程序从左侧拖拽到这里",
"display.minApp.pinnedError": "已经添加到侧边栏的小程序,不支持隐藏,如需隐藏请先从侧边栏移除",
"display.topic.title": "话题设置", "display.topic.title": "话题设置",
"display.custom.css": "自定义 CSS", "display.custom.css": "自定义 CSS",
"display.custom.css.placeholder": "/* 这里写自定义CSS */", "display.custom.css.placeholder": "/* 这里写自定义CSS */",

View File

@ -408,17 +408,14 @@
"display.sidebar.empty": "把要隱藏的功能從左側拖拽到這裡", "display.sidebar.empty": "把要隱藏的功能從左側拖拽到這裡",
"display.sidebar.visible": "顯示我的側邊欄圖標", "display.sidebar.visible": "顯示我的側邊欄圖標",
"display.sidebar.disabled": "隱藏我的側邊欄圖標", "display.sidebar.disabled": "隱藏我的側邊欄圖標",
"display.minApp.title": "小程序顯示設定",
"display.minApp.visible": "顯示的小程序",
"display.minApp.disabled": "隱藏的小程序",
"display.minApp.empty": "把要隱藏的小程序從左側拖拽到這裡",
"display.minApp.pinnedError": "已新增至側邊欄的小程序,不支援隱藏,如需隱藏請先從側邊欄移除",
"display.custom.css": "自定義 CSS", "display.custom.css": "自定義 CSS",
"display.custom.css.placeholder": "/* 這裡寫自定義 CSS */", "display.custom.css.placeholder": "/* 這裡寫自定義 CSS */",
"input.auto_translate_with_space": "快速敲擊3次空格翻譯", "input.auto_translate_with_space": "快速敲擊3次空格翻譯",
"display": {
"minApp": {
"title": "小程序顯示設定",
"visible": "顯示的小程序",
"disabled": "隱藏的小程序",
"empty": "把要隱藏的小程序從左側拖拽到這裡"
}
},
"messages.divider": "訊息間顯示分隔線", "messages.divider": "訊息間顯示分隔線",
"messages.input.paste_long_text_as_file": "將長文本貼上為檔案", "messages.input.paste_long_text_as_file": "將長文本貼上為檔案",
"messages.input.send_shortcuts": "發送快捷鍵", "messages.input.send_shortcuts": "發送快捷鍵",

View File

@ -8,7 +8,6 @@ import { FC } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
interface Props { interface Props {
app: MinAppType app: MinAppType
onClick?: () => void onClick?: () => void
@ -20,6 +19,7 @@ const App: FC<Props> = ({ app, onClick, size = 60 }) => {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { miniAppIcons } = useAppSelector((state) => state.settings) const { miniAppIcons } = useAppSelector((state) => state.settings)
const isPinned = miniAppIcons?.pinned.includes(app.id) const isPinned = miniAppIcons?.pinned.includes(app.id)
const isVisible = miniAppIcons?.visible.includes(app.id)
const handleClick = () => { const handleClick = () => {
MinApp.start(app) MinApp.start(app)
@ -38,13 +38,16 @@ const App: FC<Props> = ({ app, onClick, size = 60 }) => {
dispatch( dispatch(
setMiniAppIcons({ setMiniAppIcons({
...miniAppIcons, ...miniAppIcons,
pinned: newPinned pinned: newPinned,
visible: isPinned ? miniAppIcons.visible : [...new Set([...miniAppIcons.visible, app.id])]
}) })
) )
} }
} }
] ]
if (!isVisible) return null
return ( return (
<Dropdown menu={{ items: menuItems }} trigger={['contextMenu']}> <Dropdown menu={{ items: menuItems }} trigger={['contextMenu']}>
<Container onClick={handleClick}> <Container onClick={handleClick}>

View File

@ -17,11 +17,15 @@ const AppsPage: FC = () => {
const { miniAppIcons } = useSettings() const { miniAppIcons } = useSettings()
const allApps = useMemo(() => getAllMinApps(), []) const allApps = useMemo(() => getAllMinApps(), [])
// 只显示可见的小程序 // 只显示可见的小程序,但包括所有固定的小程序
const visibleApps = useMemo(() => { const visibleApps = useMemo(() => {
if (!miniAppIcons?.visible) return allApps if (!miniAppIcons?.visible) return allApps
return allApps.filter((app) => miniAppIcons.visible.includes(app.id)) const visibleIds = new Set([
}, [allApps, miniAppIcons?.visible]) ...miniAppIcons.visible,
...(miniAppIcons.pinned || []) // 确保固定的小程序总是可见
])
return allApps.filter((app) => visibleIds.has(app.id))
}, [allApps, miniAppIcons?.visible, miniAppIcons?.pinned])
const filteredApps = search const filteredApps = search
? visibleApps.filter( ? visibleApps.filter(

View File

@ -8,13 +8,13 @@ import {
DropResult DropResult
} from '@hello-pangea/dnd' } from '@hello-pangea/dnd'
import { getAllMinApps } from '@renderer/config/minapps' import { getAllMinApps } from '@renderer/config/minapps'
import { useSettings } from '@renderer/hooks/useSettings'
import { useAppDispatch } from '@renderer/store' import { useAppDispatch } from '@renderer/store'
import { MinAppIcon, setMiniAppIcons } from '@renderer/store/settings'
import { FC, useCallback, useMemo } from 'react' import { FC, useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import { MinAppIcon, setMiniAppIcons } from '../../../store/settings'
interface MiniAppManagerProps { interface MiniAppManagerProps {
visibleMiniApps: MinAppIcon[] visibleMiniApps: MinAppIcon[]
disabledMiniApps: MinAppIcon[] disabledMiniApps: MinAppIcon[]
@ -29,6 +29,50 @@ interface AppInfo {
logo?: string logo?: string
} }
// 添加 reorderLists 函数的接口定义
interface ReorderListsParams {
sourceList: MinAppIcon[]
destList: MinAppIcon[]
sourceIndex: number
destIndex: number
isSameList: boolean
}
interface ReorderListsResult {
sourceList: MinAppIcon[]
destList: MinAppIcon[]
}
// 添加 reorderLists 函数
const reorderLists = ({
sourceList,
destList,
sourceIndex,
destIndex,
isSameList
}: ReorderListsParams): ReorderListsResult => {
if (isSameList) {
// 在同一列表内重新排序
const newList = [...sourceList]
const [removed] = newList.splice(sourceIndex, 1)
newList.splice(destIndex, 0, removed)
return {
sourceList: newList,
destList: destList
}
} else {
// 在不同列表间移动
const newSourceList = [...sourceList]
const [removed] = newSourceList.splice(sourceIndex, 1)
const newDestList = [...destList]
newDestList.splice(destIndex, 0, removed)
return {
sourceList: newSourceList,
destList: newDestList
}
}
}
const MiniAppIconsManager: FC<MiniAppManagerProps> = ({ const MiniAppIconsManager: FC<MiniAppManagerProps> = ({
visibleMiniApps, visibleMiniApps,
disabledMiniApps, disabledMiniApps,
@ -37,6 +81,7 @@ const MiniAppIconsManager: FC<MiniAppManagerProps> = ({
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { miniAppIcons } = useSettings()
const allApps = useMemo(() => getAllMinApps(), []) const allApps = useMemo(() => getAllMinApps(), [])
// 创建 app 信息的 Map 缓存 // 创建 app 信息的 Map 缓存
@ -58,48 +103,64 @@ const MiniAppIconsManager: FC<MiniAppManagerProps> = ({
) )
const handleListUpdate = useCallback( const handleListUpdate = useCallback(
(visible: MinAppIcon[], disabled: MinAppIcon[]) => { (newVisible: MinAppIcon[], newDisabled: MinAppIcon[]) => {
setVisibleMiniApps(visible) setVisibleMiniApps(newVisible)
setDisabledMiniApps(disabled) setDisabledMiniApps(newDisabled)
dispatch(setMiniAppIcons({ visible, disabled, pinned: [] }))
// 保持 pinned 状态不变
dispatch(
setMiniAppIcons({
visible: newVisible,
disabled: newDisabled,
pinned: miniAppIcons.pinned // 保持原有的 pinned 状态
})
)
}, },
[dispatch, setVisibleMiniApps, setDisabledMiniApps] [dispatch, setVisibleMiniApps, setDisabledMiniApps, miniAppIcons.pinned]
) )
const onDragEnd = useCallback( const onDragEnd = useCallback(
(result: DropResult) => { (result: DropResult) => {
if (!result.destination) return
const { source, destination } = result const { source, destination } = result
if (!destination) return const sourceList = source.droppableId as ListType
const destList = destination.droppableId as ListType
const sourceList = source.droppableId === 'visible' ? visibleMiniApps : disabledMiniApps // 如果是 pinned 的小程序,不允许拖到 disabled
const destList = destination.droppableId === 'visible' ? visibleMiniApps : disabledMiniApps if (destList === 'disabled') {
const draggedApp = sourceList === 'visible' ? visibleMiniApps[source.index] : disabledMiniApps[source.index]
if (source.droppableId === destination.droppableId) { if (miniAppIcons.pinned.includes(draggedApp)) {
const newList = [...sourceList] window.message.error(t('settings.display.minApp.pinnedError'))
const [removed] = newList.splice(source.index, 1) return
newList.splice(destination.index, 0, removed) }
handleListUpdate(
source.droppableId === 'visible' ? newList : visibleMiniApps,
source.droppableId === 'disabled' ? newList : disabledMiniApps
)
} else {
const sourceNewList = [...sourceList]
const [removed] = sourceNewList.splice(source.index, 1)
const destNewList = [...destList]
destNewList.splice(destination.index, 0, removed)
handleListUpdate(
destination.droppableId === 'visible' ? destNewList : sourceNewList,
destination.droppableId === 'disabled' ? destNewList : sourceNewList
)
} }
const newLists = reorderLists({
sourceList: sourceList === 'visible' ? visibleMiniApps : disabledMiniApps,
destList: destList === 'visible' ? visibleMiniApps : disabledMiniApps,
sourceIndex: source.index,
destIndex: destination.index,
isSameList: sourceList === destList
})
handleListUpdate(
sourceList === 'visible' ? newLists.sourceList : newLists.destList,
sourceList === 'visible' ? newLists.destList : newLists.sourceList
)
}, },
[visibleMiniApps, disabledMiniApps, handleListUpdate] [visibleMiniApps, disabledMiniApps, handleListUpdate, miniAppIcons.pinned, t]
) )
const onMoveMiniApp = useCallback( const onMoveMiniApp = useCallback(
(program: MinAppIcon, fromList: ListType) => { (program: MinAppIcon, fromList: ListType) => {
// 如果是从可见列表移动到隐藏列表,且程序是 pinned 状态,则阻止移动
if (fromList === 'visible' && miniAppIcons.pinned.includes(program)) {
window.message.error(t('settings.display.minApp.pinnedError'))
return
}
const isMovingToVisible = fromList === 'disabled' const isMovingToVisible = fromList === 'disabled'
const newVisible = isMovingToVisible const newVisible = isMovingToVisible
? [...visibleMiniApps, program] ? [...visibleMiniApps, program]
@ -110,7 +171,7 @@ const MiniAppIconsManager: FC<MiniAppManagerProps> = ({
handleListUpdate(newVisible, newDisabled) handleListUpdate(newVisible, newDisabled)
}, },
[visibleMiniApps, disabledMiniApps, handleListUpdate] [visibleMiniApps, disabledMiniApps, handleListUpdate, miniAppIcons.pinned, t]
) )
const renderProgramItem = (program: MinAppIcon, provided: DraggableProvided, listType: ListType) => { const renderProgramItem = (program: MinAppIcon, provided: DraggableProvided, listType: ListType) => {