mirror of
https://github.com/nocobase/nocobase.git
synced 2026-09-01 14:57:36 +08:00
Merge branch 'main' into next
This commit is contained in:
+12
-13
@@ -7,33 +7,32 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { observer } from '@formily/reactive-react';
|
||||
import { Schema } from '@formily/react';
|
||||
import { Layout, List, Badge, Button, Flex, Tabs, ConfigProvider, theme } from 'antd';
|
||||
import { css } from '@emotion/css';
|
||||
import { Schema } from '@formily/react';
|
||||
import { observer } from '@formily/reactive-react';
|
||||
import { useApp } from '@nocobase/client';
|
||||
import { dayjs } from '@nocobase/utils/client';
|
||||
import { Badge, Button, ConfigProvider, Flex, Layout, List, Tabs, theme } from 'antd';
|
||||
import React from 'react';
|
||||
import { useLocalTranslation } from '../../locale';
|
||||
import {
|
||||
fetchChannels,
|
||||
selectedChannelNameObs,
|
||||
channelListObs,
|
||||
isFetchingChannelsObs,
|
||||
showChannelLoadingMoreObs,
|
||||
selectedMessageListObs,
|
||||
channelStatusFilterObs,
|
||||
ChannelStatus,
|
||||
channelStatusFilterObs,
|
||||
fetchChannels,
|
||||
isFetchingChannelsObs,
|
||||
selectedChannelNameObs,
|
||||
selectedMessageListObs,
|
||||
showChannelLoadingMoreObs,
|
||||
} from '../observables';
|
||||
import MessageList from './MessageList';
|
||||
import FilterTab from './FilterTab';
|
||||
import { useApp } from '@nocobase/client';
|
||||
import MessageList from './MessageList';
|
||||
|
||||
const InnerInboxContent = () => {
|
||||
const app = useApp();
|
||||
const { token } = theme.useToken();
|
||||
const { t } = useLocalTranslation();
|
||||
const channels = channelListObs.value;
|
||||
const messages = selectedMessageListObs.value;
|
||||
const selectedChannelName = selectedChannelNameObs.value;
|
||||
|
||||
const onLoadChannelsMore = () => {
|
||||
|
||||
+27
-6
@@ -18,9 +18,11 @@ import { useLocalTranslation } from '../../locale';
|
||||
import { useApp } from '@nocobase/client';
|
||||
import {
|
||||
channelMapObs,
|
||||
channelStatusFilterObs,
|
||||
fetchMessages,
|
||||
inboxVisible,
|
||||
isFecthingMessageObs,
|
||||
markAllMessagesAsRead,
|
||||
selectedChannelNameObs,
|
||||
selectedMessageListObs,
|
||||
showMsgLoadingMoreObs,
|
||||
@@ -42,13 +44,22 @@ const MessageList = observer(() => {
|
||||
const { token } = theme.useToken();
|
||||
const [hoveredMessageId, setHoveredMessageId] = useState<string | null>(null);
|
||||
const selectedChannelName = selectedChannelNameObs.value;
|
||||
const selectedChannel = selectedChannelName ? channelMapObs.value[selectedChannelName] : null;
|
||||
const isFetchingMessages = isFecthingMessageObs.value;
|
||||
const messages = selectedMessageListObs.value;
|
||||
const msgStatusDict = {
|
||||
read: t('Read'),
|
||||
unread: t('Unread'),
|
||||
};
|
||||
|
||||
const onMarkAllReadClick = useCallback(() => {
|
||||
if (selectedChannelName) {
|
||||
markAllMessagesAsRead({ channelName: selectedChannelName });
|
||||
}
|
||||
}, [selectedChannelName]);
|
||||
|
||||
if (!selectedChannelName) return null;
|
||||
|
||||
const onItemClicked = (message) => {
|
||||
updateMessage({
|
||||
filterByTk: message.id,
|
||||
@@ -88,9 +99,19 @@ const MessageList = observer(() => {
|
||||
components: { Badge: { dotSize: 8 } },
|
||||
}}
|
||||
>
|
||||
<Typography.Title level={4} style={{ marginBottom: token.marginLG }}>
|
||||
{title}
|
||||
</Typography.Title>
|
||||
<div
|
||||
style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: token.marginLG }}
|
||||
>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
{title}
|
||||
</Typography.Title>
|
||||
<Button
|
||||
disabled={selectedChannel?.unreadMsgCnt === 0 || channelStatusFilterObs.value === 'read'}
|
||||
onClick={onMarkAllReadClick}
|
||||
>
|
||||
{t('Mark all as read')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{messages.length === 0 && isFecthingMessageObs.value ? (
|
||||
<Spin style={{ width: '100%', marginTop: token.marginXXL }} />
|
||||
@@ -150,7 +171,7 @@ const MessageList = observer(() => {
|
||||
<Descriptions.Item label={t('Datetime')}>{dayjs(message.receiveTimestamp).fromNow()}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Status')}>
|
||||
<div style={{ height: token.controlHeight }}>
|
||||
{hoveredMessageId === message.id && message.status === 'unread' ? (
|
||||
{hoveredMessageId === message.id ? (
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
@@ -159,12 +180,12 @@ const MessageList = observer(() => {
|
||||
updateMessage({
|
||||
filterByTk: message.id,
|
||||
values: {
|
||||
status: 'read',
|
||||
status: message.status === 'read' ? 'unread' : 'read',
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('Mark as read')}
|
||||
{t(message.status === 'unread' ? 'Mark as read' : 'Mark as unread')}
|
||||
</Button>
|
||||
) : (
|
||||
<Tag color={message.status === 'unread' ? 'red' : 'green'}>{msgStatusDict[message.status]}</Tag>
|
||||
|
||||
+13
-3
@@ -7,10 +7,10 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { observable, autorun, reaction } from '@formily/reactive';
|
||||
import { autorun, observable, reaction } from '@formily/reactive';
|
||||
import { merge } from '@nocobase/utils/client';
|
||||
import { Channel } from '../../types';
|
||||
import { getAPIClient } from '../utils';
|
||||
import { merge } from '@nocobase/utils/client';
|
||||
import { userIdObs } from './user';
|
||||
|
||||
export type ChannelStatus = 'all' | 'read' | 'unread';
|
||||
@@ -60,9 +60,19 @@ export const fetchChannels = async (params: any) => {
|
||||
channelMapObs.value[channel.name] = channel;
|
||||
});
|
||||
}
|
||||
await countChannels();
|
||||
isFetchingChannelsObs.value = false;
|
||||
};
|
||||
|
||||
export const countChannels = async () => {
|
||||
const apiClient = getAPIClient();
|
||||
const res = await apiClient.request({
|
||||
url: 'myInAppChannels:list',
|
||||
method: 'get',
|
||||
params: { filter: { status: channelStatusFilterObs.value } },
|
||||
});
|
||||
const count = res.data?.meta?.count;
|
||||
if (count >= 0) channelCountObs.value = count;
|
||||
isFetchingChannelsObs.value = false;
|
||||
};
|
||||
|
||||
autorun(() => {
|
||||
|
||||
+23
-13
@@ -7,19 +7,12 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { observable, autorun } from '@formily/reactive';
|
||||
import { Message } from '../../types';
|
||||
import { getAPIClient } from '../utils';
|
||||
import {
|
||||
channelMapObs,
|
||||
selectedChannelNameObs,
|
||||
fetchChannels,
|
||||
InappChannelStatusEnum,
|
||||
channelStatusFilterObs,
|
||||
} from './channel';
|
||||
import { userIdObs } from './user';
|
||||
import { InAppMessagesDefinition } from '../../types';
|
||||
import { autorun, observable } from '@formily/reactive';
|
||||
import { merge } from '@nocobase/utils/client';
|
||||
import { InAppMessagesDefinition, Message } from '../../types';
|
||||
import { getAPIClient } from '../utils';
|
||||
import { channelMapObs, channelStatusFilterObs, fetchChannels, selectedChannelNameObs } from './channel';
|
||||
import { userIdObs } from './user';
|
||||
|
||||
export const messageMapObs = observable<{ value: Record<string, Message> }>({ value: {} });
|
||||
export const isFecthingMessageObs = observable<{ value: boolean }>({ value: false });
|
||||
@@ -76,7 +69,24 @@ export const updateMessage = async (params: { filterByTk: any; values: Record<an
|
||||
});
|
||||
const unupdatedMessage = messageMapObs.value[params.filterByTk];
|
||||
messageMapObs.value[params.filterByTk] = { ...unupdatedMessage, ...params.values };
|
||||
// fetchChannels({ filter: { name: unupdatedMessage.channelName, status: InappChannelStatusEnum.all } });
|
||||
fetchChannels({ filter: { name: unupdatedMessage.channelName, status: 'all' } });
|
||||
updateUnreadMsgsCount();
|
||||
};
|
||||
|
||||
export const markAllMessagesAsRead = async ({ channelName }: { channelName: string }) => {
|
||||
const apiClient = getAPIClient();
|
||||
await apiClient.request({
|
||||
resource: InAppMessagesDefinition.name,
|
||||
action: 'update',
|
||||
method: 'post',
|
||||
params: { filter: { status: 'unread', channelName: channelName }, values: { status: 'read' } },
|
||||
});
|
||||
Object.values(messageMapObs.value).forEach((message) => {
|
||||
if (message.channelName === channelName && message.status === 'unread') {
|
||||
message.status = 'read';
|
||||
}
|
||||
});
|
||||
fetchChannels({ filter: { name: channelName, status: 'all' } });
|
||||
updateUnreadMsgsCount();
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -8,11 +8,11 @@
|
||||
*/
|
||||
|
||||
import { observable, reaction } from '@formily/reactive';
|
||||
import { SSEData } from '../../types';
|
||||
import { messageMapObs, updateUnreadMsgsCount } from './message';
|
||||
import { fetchChannels } from './channel';
|
||||
import { getAPIClient } from '../utils';
|
||||
import { uid } from '@nocobase/utils/client';
|
||||
import { SSEData } from '../../types';
|
||||
import { getAPIClient } from '../utils';
|
||||
import { fetchChannels } from './channel';
|
||||
import { messageMapObs, updateUnreadMsgsCount } from './message';
|
||||
|
||||
export const liveSSEObs = observable<{ value: SSEData | null }>({ value: null });
|
||||
reaction(
|
||||
|
||||
@@ -18,10 +18,14 @@
|
||||
"Channel name": "Channel name",
|
||||
"Message group name": "Message group name",
|
||||
"Message title": "Message title",
|
||||
"Content type": "Content type",
|
||||
"Plain text": "Plain text",
|
||||
"Message content": "Message content",
|
||||
"Details page for desktop": "Details page for desktop",
|
||||
"Support two types of links: internal links and external links. If using an internal link, the link starts with\"/\", for example, \"/admin\". If using an external link, the link starts with \"http\", for example, \"https://example.com\".": "Support two types of links: internal links and external links. If using an internal link, the link starts with \"/\", for example, \"/admin\". If using an external link, the link starts with \"http\", for example, \"https://example.com\".",
|
||||
"Mark as read": "Mark as read",
|
||||
"Mark as unread": "Mark as unread",
|
||||
"Mark all as read": "Mark all as read",
|
||||
"Support two types of links: internal links and external links. If using an internal link, the link starts with\"/\", for example, \"/m\". If using an external link, the link starts with \"http\", for example, \"https://example.com\".": "Support two types of links: internal links and external links. If using an internal link, the link starts with \"/\", for example, \"/m\". If using an external link, the link starts with \"http\", for example, \"https://example.com\".",
|
||||
"Details page for mobile": "Details page for mobile",
|
||||
"The message page has already been created.": "The message page has already been created.",
|
||||
|
||||
@@ -18,10 +18,14 @@
|
||||
"Message group name": "消息分组名称",
|
||||
"Message title": "消息标题",
|
||||
"Message content": "消息内容",
|
||||
"Content type": "内容格式",
|
||||
"Plain text": "纯文本",
|
||||
"detail URL": "详情链接",
|
||||
"Details page for desktop": "桌面端详情页",
|
||||
"Support two types of links: internal links and external links. If using an internal link, the link starts with\"/\", for example, \"/admin\". If using an external link, the link starts with \"http\", for example, \"https://example.com\".": "支持两种链接类型:内部链接和外部链接。如果使用内部链接,链接以“/”开头,例如“/admin”。如果使用外部链接,链接以“http”开头,例如“https://example.com”。",
|
||||
"Mark as read": "标记为已读",
|
||||
"Mark as read": "标为已读",
|
||||
"Mark as unread": "标为未读",
|
||||
"Mark all as read": "全部标为已读",
|
||||
"Support two types of links: internal links and external links. If using an internal link, the link starts with\"/\", for example, \"/m\". If using an external link, the link starts with \"http\", for example, \"https://example.com\".": "支持两种链接类型:内部链接和外部链接。如果使用内部链接,链接以“/”开头,例如“/m”。如果使用外部链接,链接以“http”开头,例如“https://example.com”。",
|
||||
"Details page for mobile": "移动端详情页",
|
||||
"The message page has already been created.": "站内信页面已创建。",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import { i18n } from '@nocobase/client';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const NAMESPACE = '@nocobase/plugin-notification-manager';
|
||||
export const NAMESPACE = 'notification-manager';
|
||||
|
||||
export function lang(key: string) {
|
||||
return i18n.t(key, { ns: NAMESPACE });
|
||||
|
||||
+1
-1
@@ -181,7 +181,7 @@ export const channelsSchema: ISchema = {
|
||||
properties: {
|
||||
edit: {
|
||||
type: 'void',
|
||||
title: 'Edit',
|
||||
title: '{{t("Edit")}}',
|
||||
'x-component': 'Action.Link',
|
||||
'x-component-props': {
|
||||
openMode: 'drawer',
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"Add new": "Add new",
|
||||
"Trigger From": "Trigger From",
|
||||
"Status": "Status",
|
||||
"Edit": "Edit",
|
||||
"Created At": "Created At",
|
||||
"Delete record": "Delete record",
|
||||
"Are you sure you want to delete it?": "Are you sure you want to delete it?",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"Are you sure you want to delete it?": "确定要删除吗?",
|
||||
"Deleted successfully!": "删除成功!",
|
||||
"Add user": "添加用户",
|
||||
"Edit": "编辑",
|
||||
"Select users": "选择用户",
|
||||
"Query users": "查询用户",
|
||||
"Receiver type": "接收人类型",
|
||||
|
||||
Reference in New Issue
Block a user