mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-07 22:49:26 +08:00
feat(notification): adapt the style of the notification pop-up window for mobile (#6557)
* refactor: improve message detail navigation * refactor: extract InboxPopup component for better readability * refactor: restructure ActionDrawer to use MobilePopup for improved modularity * feat: enhance MobilePopup and related components for better mobile experience * feat: add onClick handler to PinnedPluginList for better user interaction * fix: update InboxPopup visibility logic and enhance message navigation handling
This commit is contained in:
parent
d429835215
commit
4ee9ccebfb
@ -88,7 +88,7 @@ const dividerTheme = {
|
||||
},
|
||||
};
|
||||
|
||||
export const PinnedPluginList = React.memo(() => {
|
||||
export const PinnedPluginList = React.memo((props: { onClick?: () => void }) => {
|
||||
const { allowAll, snippets } = useACLRoleContext();
|
||||
const getSnippetsAllow = (aclKey) => {
|
||||
return allowAll || aclKey === '*' || snippets?.includes(aclKey);
|
||||
@ -98,6 +98,7 @@ export const PinnedPluginList = React.memo(() => {
|
||||
|
||||
return (
|
||||
<div className={pinnedPluginListClassName}>
|
||||
<div onClick={props.onClick}>
|
||||
{Object.keys(ctx.items)
|
||||
.sort((a, b) => ctx.items[a].order - ctx.items[b].order)
|
||||
.filter((key) => getSnippetsAllow(ctx.items[key].snippet))
|
||||
@ -105,6 +106,7 @@ export const PinnedPluginList = React.memo(() => {
|
||||
const Action = get(components, ctx.items[key].component);
|
||||
return Action ? <Action key={key} /> : null;
|
||||
})}
|
||||
</div>
|
||||
<ConfigProvider theme={dividerTheme}>
|
||||
<Divider type="vertical" />
|
||||
</ConfigProvider>
|
||||
|
@ -12,6 +12,7 @@ import ProLayout, { RouteContext, RouteContextType } from '@ant-design/pro-layou
|
||||
import { HeaderViewProps } from '@ant-design/pro-layout/es/components/Header';
|
||||
import { css } from '@emotion/css';
|
||||
import { theme as antdTheme, ConfigProvider, Popover, Tooltip } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import React, { createContext, FC, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Link, Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom';
|
||||
@ -52,7 +53,6 @@ import { KeepAlive } from './KeepAlive';
|
||||
import { NocoBaseDesktopRoute, NocoBaseDesktopRouteType } from './convertRoutesToSchema';
|
||||
import { MenuSchemaToolbar, ResetThemeTokenAndKeepAlgorithm } from './menuItemSettings';
|
||||
import { userCenterSettings } from './userCenterSettings';
|
||||
import { createGlobalStyle, createStyles } from 'antd-style';
|
||||
|
||||
export { KeepAlive, NocoBaseDesktopRouteType };
|
||||
|
||||
@ -418,9 +418,22 @@ const popoverStyle = css`
|
||||
|
||||
const MobileActions: FC = (props) => {
|
||||
const { token } = useToken();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// 点击时立即关闭 Popover,避免影响用户操作
|
||||
const handleContentClick = useCallback(() => {
|
||||
setOpen(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Popover rootClassName={popoverStyle} content={<PinnedPluginList />} color={token.colorBgHeader}>
|
||||
<Popover
|
||||
rootClassName={popoverStyle}
|
||||
content={<PinnedPluginList onClick={handleContentClick} />}
|
||||
color={token.colorBgHeader}
|
||||
trigger="click"
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
<div style={{ padding: '0 16px', display: 'flex', alignItems: 'center', height: '100%', marginRight: -16 }}>
|
||||
<EllipsisOutlined
|
||||
style={{
|
||||
|
@ -20,55 +20,54 @@ import {
|
||||
import { ConfigProvider } from 'antd';
|
||||
import { Popup } from 'antd-mobile';
|
||||
import { CloseOutline } from 'antd-mobile-icons';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import React, { FC, ReactNode, useCallback, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMobileActionDrawerStyle } from './ActionDrawer.style';
|
||||
import { usePopupContainer } from './FilterAction';
|
||||
import { MIN_Z_INDEX_INCREMENT } from './zIndex';
|
||||
|
||||
export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?: string }) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const field = useField();
|
||||
const { visible, setVisible } = useActionContext();
|
||||
const { popupContainerRef, visiblePopup } = usePopupContainer(visible);
|
||||
export interface MobilePopupProps {
|
||||
title?: string;
|
||||
visible: boolean;
|
||||
minHeight?: number | string;
|
||||
onClose: () => void;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const MobilePopup: FC<MobilePopupProps> = (props) => {
|
||||
const {
|
||||
title,
|
||||
visible,
|
||||
onClose: closePopup,
|
||||
children,
|
||||
minHeight,
|
||||
} = props;
|
||||
const { t } = useTranslation();
|
||||
const { popupContainerRef } = usePopupContainer(visible);
|
||||
const { componentCls, hashId } = useMobileActionDrawerStyle();
|
||||
const parentZIndex = useZIndexContext();
|
||||
const { theme: globalTheme } = useGlobalTheme();
|
||||
|
||||
// this schema need to add padding in the content area of the popup
|
||||
const isSpecialSchema = isChangePasswordSchema(fieldSchema) || isEditProfileSchema(fieldSchema);
|
||||
|
||||
const footerNodeName = isSpecialSchema ? 'Action.Drawer.Footer' : props.footerNodeName;
|
||||
|
||||
const specialStyle = isSpecialSchema ? { backgroundColor: 'white' } : {};
|
||||
|
||||
const newZIndex = parentZIndex + MIN_Z_INDEX_INCREMENT;
|
||||
|
||||
const zIndexStyle = useMemo(() => {
|
||||
return {
|
||||
zIndex: newZIndex,
|
||||
minHeight,
|
||||
};
|
||||
}, [newZIndex]);
|
||||
|
||||
const footerSchema = fieldSchema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === footerNodeName) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
|
||||
const title = field.title || '';
|
||||
|
||||
const closePopup = useCallback(() => {
|
||||
setVisible(false);
|
||||
}, [setVisible]);
|
||||
}, [newZIndex, minHeight]);
|
||||
|
||||
const theme = useMemo(() => {
|
||||
return {
|
||||
...globalTheme,
|
||||
token: {
|
||||
...globalTheme.token,
|
||||
marginBlock: 12,
|
||||
zIndexPopupBase: newZIndex,
|
||||
paddingPageHorizontal: 8,
|
||||
paddingPageVertical: 8,
|
||||
marginBlock: 12,
|
||||
borderRadiusBlock: 8,
|
||||
fontSize: 14,
|
||||
},
|
||||
};
|
||||
}, [globalTheme, newZIndex]);
|
||||
@ -78,7 +77,7 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
|
||||
<ConfigProvider theme={theme}>
|
||||
<Popup
|
||||
className={`${componentCls} ${hashId}`}
|
||||
visible={visiblePopup}
|
||||
visible={visible}
|
||||
onClose={closePopup}
|
||||
onMaskClick={closePopup}
|
||||
getContainer={() => popupContainerRef.current}
|
||||
@ -93,11 +92,50 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
|
||||
<CloseOutline />
|
||||
</span>
|
||||
<span>{title}</span>
|
||||
<span className="nb-mobile-action-drawer-close-icon" onClick={closePopup}>
|
||||
<span
|
||||
className="nb-mobile-action-drawer-close-icon"
|
||||
onClick={closePopup}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={t("Close")}
|
||||
>
|
||||
<CloseOutline />
|
||||
</span>
|
||||
</div>
|
||||
{isSpecialSchema ? (
|
||||
{children}
|
||||
</Popup>
|
||||
</ConfigProvider>
|
||||
</zIndexContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?: string }) => {
|
||||
const fieldSchema = useFieldSchema();
|
||||
const field = useField();
|
||||
const { visible, setVisible } = useActionContext();
|
||||
const { visiblePopup } = usePopupContainer(visible);
|
||||
|
||||
// this schema need to add padding in the content area of the popup
|
||||
const isSpecialSchema = isChangePasswordSchema(fieldSchema) || isEditProfileSchema(fieldSchema);
|
||||
|
||||
const footerNodeName = isSpecialSchema ? 'Action.Drawer.Footer' : props.footerNodeName;
|
||||
|
||||
const specialStyle = isSpecialSchema ? { backgroundColor: 'white' } : {};
|
||||
|
||||
const footerSchema = fieldSchema.reduceProperties((buf, s) => {
|
||||
if (s['x-component'] === footerNodeName) {
|
||||
return s;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
|
||||
const title = field.title || '';
|
||||
|
||||
const closePopup = useCallback(() => {
|
||||
setVisible(false);
|
||||
}, [setVisible]);
|
||||
|
||||
const popupContent = isSpecialSchema ? (
|
||||
<div style={{ padding: 12, ...specialStyle }}>
|
||||
<SchemaComponent
|
||||
schema={fieldSchema}
|
||||
@ -114,8 +152,9 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
|
||||
return s['x-component'] !== footerNodeName;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{footerSchema ? (
|
||||
);
|
||||
|
||||
const footerContent = footerSchema ? (
|
||||
<div className="nb-mobile-action-drawer-footer" style={isSpecialSchema ? specialStyle : null}>
|
||||
<NocoBaseRecursionField
|
||||
basePath={field.address}
|
||||
@ -126,10 +165,17 @@ export const ActionDrawerUsedInMobile: any = observer((props: { footerNodeName?:
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</Popup>
|
||||
</ConfigProvider>
|
||||
</zIndexContext.Provider>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<MobilePopup
|
||||
title={title}
|
||||
visible={visiblePopup}
|
||||
onClose={closePopup}
|
||||
>
|
||||
{popupContent}
|
||||
{footerContent}
|
||||
</MobilePopup>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -55,6 +55,7 @@ import { MobileSettingsBlockSchemaSettings } from './mobile-blocks/settings-bloc
|
||||
import pkg from './../../package.json';
|
||||
import { MobileComponentsProvider } from './MobileComponentsProvider';
|
||||
|
||||
export { MobilePopup } from './adaptor-of-desktop/ActionDrawer';
|
||||
export * from './desktop-mode';
|
||||
export * from './mobile';
|
||||
export * from './mobile-layout';
|
||||
|
@ -12,7 +12,7 @@ import _ from 'lodash';
|
||||
import React, { FC, useEffect } from 'react';
|
||||
import { PageBackgroundColor } from '../../../constants';
|
||||
|
||||
export const MobilePageContentContainer: FC<{ hideTabBar?: boolean }> = ({ children, hideTabBar }) => {
|
||||
export const MobilePageContentContainer: FC<{ hideTabBar?: boolean; displayPageHeader?: boolean }> = ({ children, hideTabBar, displayPageHeader = true }) => {
|
||||
const [mobileTabBarHeight, setMobileTabBarHeight] = React.useState(0);
|
||||
const [mobilePageHeader, setMobilePageHeader] = React.useState(0);
|
||||
const { token } = useToken();
|
||||
@ -29,7 +29,7 @@ export const MobilePageContentContainer: FC<{ hideTabBar?: boolean }> = ({ child
|
||||
});
|
||||
return (
|
||||
<>
|
||||
{mobilePageHeader ? <div style={{ height: mobilePageHeader }}></div> : null}
|
||||
{(mobilePageHeader && displayPageHeader) ? <div style={{ height: mobilePageHeader }}></div> : null}
|
||||
<div
|
||||
className="mobile-page-content"
|
||||
data-testid="mobile-page-content"
|
||||
|
@ -12,13 +12,13 @@ import { cx, NocoBaseRecursionField, SchemaToolbarProvider } from '@nocobase/cli
|
||||
import { NavBar } from 'antd-mobile';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { useRouteTranslation } from '../../../../locale';
|
||||
import { useMobileTitle } from '../../../../mobile-providers';
|
||||
import { useMobilePage } from '../../context';
|
||||
import { useStyles } from './styles';
|
||||
import { useRouteTranslation } from '../../../../locale';
|
||||
|
||||
export const MobilePageNavigationBar: FC = () => {
|
||||
const { title } = useMobileTitle();
|
||||
const { title } = useMobileTitle() || {};
|
||||
const { displayNavigationBar = true, displayPageTitle = true } = useMobilePage();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const { componentCls, hashId } = useStyles();
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
export const mobilePageHeaderStyle: any = {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
borderBottom: '1px solid var(--adm-color-border)',
|
||||
|
@ -16,27 +16,23 @@
|
||||
* For more information, please rwefer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { reaction } from '@formily/reactive';
|
||||
import { Badge, Button, ConfigProvider, Drawer, Tooltip, notification, theme } from 'antd';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { Icon } from '@nocobase/client';
|
||||
import { InboxContent } from './InboxContent';
|
||||
import { useLocalTranslation } from '../../locale';
|
||||
import { fetchChannels } from '../observables';
|
||||
import { observer } from '@formily/reactive-react';
|
||||
import { useCurrentUserContext } from '@nocobase/client';
|
||||
import { Icon, useCurrentUserContext, useMobileLayout } from '@nocobase/client';
|
||||
import { MobilePopup } from '@nocobase/plugin-mobile/client';
|
||||
import { Badge, Button, ConfigProvider, Drawer, notification, theme, Tooltip } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import React, { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { useLocalTranslation } from '../../locale';
|
||||
import { Channel } from '../../types';
|
||||
import {
|
||||
updateUnreadMsgsCount,
|
||||
unreadMsgsCountObs,
|
||||
startMsgSSEStreamWithRetry,
|
||||
inboxVisible,
|
||||
userIdObs,
|
||||
liveSSEObs,
|
||||
fetchChannels, inboxVisible, liveSSEObs,
|
||||
messageMapObs,
|
||||
selectedChannelNameObs,
|
||||
selectedChannelNameObs, startMsgSSEStreamWithRetry, unreadMsgsCountObs, updateUnreadMsgsCount, userIdObs
|
||||
} from '../observables';
|
||||
import { InboxContent } from './InboxContent';
|
||||
import { MobileChannelPage } from './mobile/ChannelPage';
|
||||
import { MobileMessagePage } from './mobile/MessagePage';
|
||||
const useStyles = createStyles(({ token }) => {
|
||||
return {
|
||||
button: {
|
||||
@ -46,6 +42,42 @@ const useStyles = createStyles(({ token }) => {
|
||||
};
|
||||
});
|
||||
|
||||
const InboxPopup: FC<{ title: string; visible: boolean; onClose: () => void }> = (props) => {
|
||||
const { token } = theme.useToken();
|
||||
const { isMobileLayout } = useMobileLayout();
|
||||
const [selectedChannel, setSelectedChannel] = useState<Channel | null>(null);
|
||||
|
||||
if (isMobileLayout) {
|
||||
return (
|
||||
<>
|
||||
<MobilePopup title={props.title} visible={props.visible} onClose={props.onClose} minHeight={'60vh'}>
|
||||
<MobileChannelPage displayNavigationBar={false} onClickItem={setSelectedChannel} />
|
||||
</MobilePopup>
|
||||
<MobilePopup title={selectedChannel?.title} visible={props.visible && !!selectedChannel} onClose={() => setSelectedChannel(null)} minHeight={'60vh'}>
|
||||
<MobileMessagePage displayPageHeader={false} />
|
||||
</MobilePopup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
title={<div style={{ padding: '0', paddingLeft: token.padding }}>{props.title}</div>}
|
||||
open={props.visible}
|
||||
width={900}
|
||||
onClose={props.onClose}
|
||||
styles={{
|
||||
header: {
|
||||
paddingLeft: token.paddingMD,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<InboxContent />
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const InnerInbox = (props) => {
|
||||
const { t } = useLocalTranslation();
|
||||
const { styles } = useStyles();
|
||||
@ -89,7 +121,6 @@ const InnerInbox = (props) => {
|
||||
document.removeEventListener('visibilitychange', onVisibilityChange);
|
||||
};
|
||||
}, []);
|
||||
const DrawerTitle = <div style={{ padding: '0', paddingLeft: token.padding }}>{t('Message')}</div>;
|
||||
useEffect(() => {
|
||||
const dispose = reaction(
|
||||
() => liveSSEObs.value,
|
||||
@ -138,21 +169,7 @@ const InnerInbox = (props) => {
|
||||
</Badge>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Drawer
|
||||
title={DrawerTitle}
|
||||
open={inboxVisible.value}
|
||||
width={900}
|
||||
onClose={() => {
|
||||
inboxVisible.value = false;
|
||||
}}
|
||||
styles={{
|
||||
header: {
|
||||
paddingLeft: token.paddingMD,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<InboxContent />
|
||||
</Drawer>
|
||||
<InboxPopup title={t('Message')} visible={inboxVisible.value} onClose={() => { inboxVisible.value = false; }} />
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
@ -7,18 +7,18 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { observer } from '@formily/reactive-react';
|
||||
import { reaction } from '@formily/reactive';
|
||||
import { List, Badge, InfiniteScroll, ListRef } from 'antd-mobile';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { dayjs } from '@nocobase/utils/client';
|
||||
import InfiniteScrollContent from './InfiniteScrollContent';
|
||||
import { channelListObs, channelStatusFilterObs, showChannelLoadingMoreObs, fetchChannels } from '../../observables';
|
||||
import { Schema } from '@formily/react';
|
||||
import { reaction } from '@formily/reactive';
|
||||
import { observer } from '@formily/reactive-react';
|
||||
import { useApp } from '@nocobase/client';
|
||||
import { dayjs } from '@nocobase/utils/client';
|
||||
import { Badge, InfiniteScroll, List, ListRef } from 'antd-mobile';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { channelListObs, channelStatusFilterObs, fetchChannels, showChannelLoadingMoreObs } from '../../observables';
|
||||
import InfiniteScrollContent from './InfiniteScrollContent';
|
||||
|
||||
const InternalChannelList = () => {
|
||||
const InternalChannelList = (props: { onClickItem?: (item: any) => void }) => {
|
||||
const app = useApp();
|
||||
const navigate = useNavigate();
|
||||
const channels = channelListObs.value;
|
||||
@ -66,7 +66,11 @@ const InternalChannelList = () => {
|
||||
<List.Item
|
||||
key={item.name}
|
||||
onClick={() => {
|
||||
if (props.onClickItem) {
|
||||
props.onClickItem(item);
|
||||
} else {
|
||||
navigate(`/page/in-app-message/messages?channel=${item.name}`);
|
||||
}
|
||||
}}
|
||||
description={
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
|
@ -7,20 +7,20 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { Tabs } from 'antd-mobile';
|
||||
import { observer } from '@formily/reactive-react';
|
||||
import { useCurrentUserContext, css } from '@nocobase/client';
|
||||
import { css, useCurrentUserContext } from '@nocobase/client';
|
||||
import {
|
||||
MobilePageContentContainer,
|
||||
MobilePageHeader,
|
||||
MobilePageNavigationBar,
|
||||
MobilePageProvider,
|
||||
MobilePageContentContainer,
|
||||
} from '@nocobase/plugin-mobile/client';
|
||||
import { userIdObs, fetchChannels, ChannelStatus, channelStatusFilterObs } from '../../observables';
|
||||
import { ChannelList } from './ChannelList';
|
||||
import { Tabs } from 'antd-mobile';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useLocalTranslation } from '../../../locale';
|
||||
const MobileMessageBoxInner = () => {
|
||||
import { ChannelStatus, channelStatusFilterObs, fetchChannels, userIdObs } from '../../observables';
|
||||
import { ChannelList } from './ChannelList';
|
||||
const MobileMessageBoxInner = (props: { displayNavigationBar?: boolean; onClickItem?: (item: any) => void; }) => {
|
||||
const { t } = useLocalTranslation();
|
||||
const ctx = useCurrentUserContext();
|
||||
const currUserId = ctx.data?.data?.id;
|
||||
@ -31,7 +31,7 @@ const MobileMessageBoxInner = () => {
|
||||
fetchChannels({});
|
||||
}, []);
|
||||
return (
|
||||
<MobilePageProvider>
|
||||
<MobilePageProvider displayNavigationBar={props.displayNavigationBar}>
|
||||
<MobilePageHeader>
|
||||
<MobilePageNavigationBar />
|
||||
<Tabs
|
||||
@ -57,7 +57,7 @@ const MobileMessageBoxInner = () => {
|
||||
</Tabs>
|
||||
</MobilePageHeader>
|
||||
<MobilePageContentContainer>
|
||||
<ChannelList />
|
||||
<ChannelList onClickItem={props.onClickItem} />
|
||||
</MobilePageContentContainer>
|
||||
</MobilePageProvider>
|
||||
);
|
||||
|
@ -7,35 +7,34 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useCallback, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { List, Badge, InfiniteScroll, NavBar, DotLoading } from 'antd-mobile';
|
||||
import { observer } from '@formily/reactive-react';
|
||||
import { useCurrentUserContext, css, useApp } from '@nocobase/client';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { css, useApp, useCurrentUserContext } from '@nocobase/client';
|
||||
import { dayjs } from '@nocobase/utils/client';
|
||||
import { Badge, InfiniteScroll, List, NavBar } from 'antd-mobile';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { Schema } from '@formily/react';
|
||||
import {
|
||||
MobilePageHeader,
|
||||
MobilePageProvider,
|
||||
MobilePageContentContainer,
|
||||
useMobileTitle,
|
||||
MobilePageHeader,
|
||||
MobilePageProvider
|
||||
} from '@nocobase/plugin-mobile/client';
|
||||
import { useLocalTranslation } from '../../../locale';
|
||||
import {
|
||||
userIdObs,
|
||||
fetchChannels,
|
||||
fetchMessages,
|
||||
inboxVisible,
|
||||
selectedChannelNameObs,
|
||||
selectedChannelObs,
|
||||
selectedMessageListObs,
|
||||
fetchChannels,
|
||||
updateMessage,
|
||||
fetchMessages,
|
||||
showMsgLoadingMoreObs,
|
||||
updateMessage,
|
||||
userIdObs,
|
||||
} from '../../observables';
|
||||
import { useLocalTranslation } from '../../../locale';
|
||||
import InfiniteScrollContent from './InfiniteScrollContent';
|
||||
import { Schema } from '@formily/react';
|
||||
|
||||
const MobileMessagePageInner = () => {
|
||||
const MobileMessagePageInner = (props: { displayPageHeader?: boolean }) => {
|
||||
const app = useApp();
|
||||
const { t } = useLocalTranslation();
|
||||
const navigate = useNavigate();
|
||||
@ -58,11 +57,13 @@ const MobileMessagePageInner = () => {
|
||||
}, [currUserId]);
|
||||
const messages = selectedMessageListObs.value;
|
||||
const viewMessageDetail = (message) => {
|
||||
const url = message.options?.mobileUrl;
|
||||
const url = message.options?.mobileUrl || message.options?.url;
|
||||
if (url) {
|
||||
if (url.startsWith('/m/')) navigate(url.substring(2));
|
||||
else if (url.startsWith('/')) navigate(url);
|
||||
else {
|
||||
else if (url.startsWith('/')) {
|
||||
navigate(url);
|
||||
inboxVisible.value = false;
|
||||
} else {
|
||||
window.location.href = url;
|
||||
}
|
||||
}
|
||||
@ -101,13 +102,13 @@ const MobileMessagePageInner = () => {
|
||||
const title = Schema.compile(selectedChannelObs.value?.title, { t: app.i18n.t }) || t('Message');
|
||||
|
||||
return (
|
||||
<MobilePageProvider>
|
||||
<MobilePageProvider displayPageHeader={props.displayPageHeader}>
|
||||
<MobilePageHeader>
|
||||
<NavBar className="nb-message-back-action" onBack={() => navigate('/page/in-app-message')}>
|
||||
{title}
|
||||
</NavBar>
|
||||
</MobilePageHeader>
|
||||
<MobilePageContentContainer>
|
||||
<MobilePageContentContainer displayPageHeader={props.displayPageHeader}>
|
||||
<div
|
||||
style={{ height: '100%', overflowY: 'auto' }}
|
||||
className={css({
|
||||
|
Loading…
x
Reference in New Issue
Block a user