mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
feat(notification): in-app message support
This commit is contained in:
parent
e3caaee61c
commit
cd8141ca15
@ -7,10 +7,10 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { SchemaComponent, css } from '@nocobase/client';
|
import { SchemaComponent, css } from '@nocobase/client';
|
||||||
import { useLocalTranslation } from '../../locale';
|
|
||||||
import { tval } from '@nocobase/utils/client';
|
import { tval } from '@nocobase/utils/client';
|
||||||
|
import React from 'react';
|
||||||
|
import { useLocalTranslation } from '../../locale';
|
||||||
|
|
||||||
export const ContentConfigForm = ({ variableOptions }) => {
|
export const ContentConfigForm = ({ variableOptions }) => {
|
||||||
const { t } = useLocalTranslation();
|
const { t } = useLocalTranslation();
|
||||||
@ -31,6 +31,17 @@ export const ContentConfigForm = ({ variableOptions }) => {
|
|||||||
useTypedConstant: ['string'],
|
useTypedConstant: ['string'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
contentType: {
|
||||||
|
type: 'string',
|
||||||
|
title: `{{t("Content type")}}`,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Radio.Group',
|
||||||
|
enum: [
|
||||||
|
{ label: 'HTML', value: 'html' },
|
||||||
|
{ label: `{{t("Plain text")}}`, value: 'text' },
|
||||||
|
],
|
||||||
|
default: 'html',
|
||||||
|
},
|
||||||
content: {
|
content: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -16,27 +16,26 @@
|
|||||||
* For more information, please rwefer to: https://www.nocobase.com/agreement.
|
* 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 } from 'antd';
|
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { createStyles } from 'antd-style';
|
import { reaction } from '@formily/reactive';
|
||||||
import { Icon } from '@nocobase/client';
|
|
||||||
import { InboxContent } from './InboxContent';
|
|
||||||
import { useLocalTranslation } from '../../locale';
|
|
||||||
import { fetchChannels } from '../observables';
|
|
||||||
import { observer } from '@formily/reactive-react';
|
import { observer } from '@formily/reactive-react';
|
||||||
import { useCurrentUserContext } from '@nocobase/client';
|
import { Icon, useCurrentUserContext } from '@nocobase/client';
|
||||||
|
import { Badge, Button, ConfigProvider, Drawer, Tooltip, notification } from 'antd';
|
||||||
|
import { createStyles } from 'antd-style';
|
||||||
|
import React, { useCallback, useEffect } from 'react';
|
||||||
|
import { useLocalTranslation } from '../../locale';
|
||||||
import {
|
import {
|
||||||
updateUnreadMsgsCount,
|
fetchChannels,
|
||||||
unreadMsgsCountObs,
|
|
||||||
startMsgSSEStreamWithRetry,
|
|
||||||
inboxVisible,
|
inboxVisible,
|
||||||
userIdObs,
|
|
||||||
liveSSEObs,
|
liveSSEObs,
|
||||||
messageMapObs,
|
messageMapObs,
|
||||||
selectedChannelNameObs,
|
selectedChannelNameObs,
|
||||||
|
startMsgSSEStreamWithRetry,
|
||||||
|
unreadMsgsCountObs,
|
||||||
|
updateUnreadMsgsCount,
|
||||||
|
userIdObs,
|
||||||
} from '../observables';
|
} from '../observables';
|
||||||
|
import { InboxContent } from './InboxContent';
|
||||||
const useStyles = createStyles(({ token }) => {
|
const useStyles = createStyles(({ token }) => {
|
||||||
return {
|
return {
|
||||||
button: {
|
button: {
|
||||||
@ -94,6 +93,12 @@ const InnerInbox = (props) => {
|
|||||||
<CloseOutlined />
|
<CloseOutlined />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
const renderContent = useCallback((content: string, contentType: string) => {
|
||||||
|
if (contentType === 'HTML') {
|
||||||
|
return <div dangerouslySetInnerHTML={{ __html: content }} />;
|
||||||
|
}
|
||||||
|
return content.slice(0, 100) + (content.length > 100 ? '...' : '');
|
||||||
|
}, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const dispose = reaction(
|
const dispose = reaction(
|
||||||
() => liveSSEObs.value,
|
() => liveSSEObs.value,
|
||||||
@ -116,7 +121,7 @@ const InnerInbox = (props) => {
|
|||||||
{data.title}
|
{data.title}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
description: data.content.slice(0, 100) + (data.content.length > 100 ? '...' : ''),
|
description: renderContent(data.content, data.contentType),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
inboxVisible.value = true;
|
inboxVisible.value = true;
|
||||||
selectedChannelNameObs.value = data.channelName;
|
selectedChannelNameObs.value = data.channelName;
|
||||||
|
@ -7,12 +7,12 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { SchemaComponent, css } from '@nocobase/client';
|
import { SchemaComponent, css } from '@nocobase/client';
|
||||||
import { useLocalTranslation, NAMESPACE } from '../../locale';
|
|
||||||
import { UsersSelect } from './UsersSelect';
|
|
||||||
import { UsersAddition } from './UsersAddition';
|
|
||||||
import { tval } from '@nocobase/utils/client';
|
import { tval } from '@nocobase/utils/client';
|
||||||
|
import React from 'react';
|
||||||
|
import { NAMESPACE, useLocalTranslation } from '../../locale';
|
||||||
|
import { UsersAddition } from './UsersAddition';
|
||||||
|
import { UsersSelect } from './UsersSelect';
|
||||||
|
|
||||||
export const MessageConfigForm = ({ variableOptions }) => {
|
export const MessageConfigForm = ({ variableOptions }) => {
|
||||||
const { t } = useLocalTranslation();
|
const { t } = useLocalTranslation();
|
||||||
@ -84,6 +84,17 @@ export const MessageConfigForm = ({ variableOptions }) => {
|
|||||||
useTypedConstant: ['string'],
|
useTypedConstant: ['string'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
contentType: {
|
||||||
|
type: 'string',
|
||||||
|
title: `{{t("Content type")}}`,
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Radio.Group',
|
||||||
|
enum: [
|
||||||
|
{ label: `{{t("Plain text")}}`, value: 'text' },
|
||||||
|
{ label: 'HTML', value: 'HTML' },
|
||||||
|
],
|
||||||
|
default: 'text',
|
||||||
|
},
|
||||||
content: {
|
content: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -90,7 +90,12 @@ const MessageList = observer(() => {
|
|||||||
}, [messages, selectedChannelName]);
|
}, [messages, selectedChannelName]);
|
||||||
|
|
||||||
const title = Schema.compile(channelMapObs.value[selectedChannelName].title, { t: app.i18n.t });
|
const title = Schema.compile(channelMapObs.value[selectedChannelName].title, { t: app.i18n.t });
|
||||||
|
const renderContent = (content: string, contentType: string) => {
|
||||||
|
if (contentType === 'HTML') {
|
||||||
|
return <div dangerouslySetInnerHTML={{ __html: content }} />;
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<ConfigProvider
|
<ConfigProvider
|
||||||
theme={{
|
theme={{
|
||||||
@ -156,10 +161,7 @@ const MessageList = observer(() => {
|
|||||||
>
|
>
|
||||||
<Descriptions key={index} column={1}>
|
<Descriptions key={index} column={1}>
|
||||||
<Descriptions.Item label={t('Content')}>
|
<Descriptions.Item label={t('Content')}>
|
||||||
{' '}
|
{renderContent(message.content, message.contentType)}
|
||||||
<Tooltip title={message.content?.length > 100 ? message.content : ''} mouseEnterDelay={0.5}>
|
|
||||||
{message.content?.slice(0, 100) + (message.content?.length > 100 ? '...' : '')}{' '}
|
|
||||||
</Tooltip>
|
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t('Datetime')}>{dayjs(message.receiveTimestamp).fromNow()}</Descriptions.Item>
|
<Descriptions.Item label={t('Datetime')}>{dayjs(message.receiveTimestamp).fromNow()}</Descriptions.Item>
|
||||||
<Descriptions.Item label={t('Status')}>
|
<Descriptions.Item label={t('Status')}>
|
||||||
|
@ -7,33 +7,32 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* 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 { observer } from '@formily/reactive-react';
|
||||||
import { useCurrentUserContext, css, useApp } from '@nocobase/client';
|
import { css, useApp, useCurrentUserContext } from '@nocobase/client';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
|
||||||
import { dayjs } from '@nocobase/utils/client';
|
import { dayjs } from '@nocobase/utils/client';
|
||||||
|
import { Badge, DotLoading, 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 {
|
import {
|
||||||
|
MobilePageContentContainer,
|
||||||
MobilePageHeader,
|
MobilePageHeader,
|
||||||
MobilePageProvider,
|
MobilePageProvider,
|
||||||
MobilePageContentContainer,
|
|
||||||
useMobileTitle,
|
useMobileTitle,
|
||||||
} from '@nocobase/plugin-mobile/client';
|
} from '@nocobase/plugin-mobile/client';
|
||||||
|
import { useLocalTranslation } from '../../../locale';
|
||||||
import {
|
import {
|
||||||
userIdObs,
|
fetchChannels,
|
||||||
|
fetchMessages,
|
||||||
selectedChannelNameObs,
|
selectedChannelNameObs,
|
||||||
selectedChannelObs,
|
selectedChannelObs,
|
||||||
selectedMessageListObs,
|
selectedMessageListObs,
|
||||||
fetchChannels,
|
|
||||||
updateMessage,
|
|
||||||
fetchMessages,
|
|
||||||
showMsgLoadingMoreObs,
|
showMsgLoadingMoreObs,
|
||||||
|
updateMessage,
|
||||||
|
userIdObs,
|
||||||
} from '../../observables';
|
} from '../../observables';
|
||||||
import { useLocalTranslation } from '../../../locale';
|
|
||||||
import InfiniteScrollContent from './InfiniteScrollContent';
|
import InfiniteScrollContent from './InfiniteScrollContent';
|
||||||
import { Schema } from '@formily/react';
|
|
||||||
|
|
||||||
const MobileMessagePageInner = () => {
|
const MobileMessagePageInner = () => {
|
||||||
const app = useApp();
|
const app = useApp();
|
||||||
@ -100,6 +99,12 @@ const MobileMessagePageInner = () => {
|
|||||||
}, [messages]);
|
}, [messages]);
|
||||||
const title = Schema.compile(selectedChannelObs.value?.title, { t: app.i18n.t }) || t('Message');
|
const title = Schema.compile(selectedChannelObs.value?.title, { t: app.i18n.t }) || t('Message');
|
||||||
|
|
||||||
|
const renderContent = useCallback((content: string, contentType: string) => {
|
||||||
|
if (contentType === 'HTML') {
|
||||||
|
return <div dangerouslySetInnerHTML={{ __html: content }} />;
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<MobilePageProvider>
|
<MobilePageProvider>
|
||||||
<MobilePageHeader>
|
<MobilePageHeader>
|
||||||
@ -131,7 +136,7 @@ const MobileMessagePageInner = () => {
|
|||||||
<Badge key={item.id} content={item.status === 'unread' ? Badge.dot : null} />
|
<Badge key={item.id} content={item.status === 'unread' ? Badge.dot : null} />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
description={item.content}
|
description={renderContent(item.content, item.contentType)}
|
||||||
extra={dayjs(item.receiveTimestamp).fromNow(true)}
|
extra={dayjs(item.receiveTimestamp).fromNow(true)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onMessageClick(item);
|
onMessageClick(item);
|
||||||
|
@ -11,13 +11,7 @@ import { autorun, observable } from '@formily/reactive';
|
|||||||
import { merge } from '@nocobase/utils/client';
|
import { merge } from '@nocobase/utils/client';
|
||||||
import { InAppMessagesDefinition, Message } from '../../types';
|
import { InAppMessagesDefinition, Message } from '../../types';
|
||||||
import { getAPIClient } from '../utils';
|
import { getAPIClient } from '../utils';
|
||||||
import {
|
import { channelMapObs, channelStatusFilterObs, selectedChannelNameObs } from './channel';
|
||||||
channelMapObs,
|
|
||||||
channelStatusFilterObs,
|
|
||||||
fetchChannels,
|
|
||||||
InappChannelStatusEnum,
|
|
||||||
selectedChannelNameObs,
|
|
||||||
} from './channel';
|
|
||||||
import { userIdObs } from './user';
|
import { userIdObs } from './user';
|
||||||
|
|
||||||
export const messageMapObs = observable<{ value: Record<string, Message> }>({ value: {} });
|
export const messageMapObs = observable<{ value: Record<string, Message> }>({ value: {} });
|
||||||
|
@ -7,14 +7,13 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { BaseNotificationChannel, SendFnType } from '@nocobase/plugin-notification-manager';
|
||||||
import { Application } from '@nocobase/server';
|
import { Application } from '@nocobase/server';
|
||||||
import { SendFnType, BaseNotificationChannel } from '@nocobase/plugin-notification-manager';
|
|
||||||
import { InAppMessageFormValues } from '../types';
|
|
||||||
import { PassThrough } from 'stream';
|
import { PassThrough } from 'stream';
|
||||||
import { InAppMessagesDefinition as MessagesDefinition } from '../types';
|
import { InAppMessageFormValues, InAppMessagesDefinition as MessagesDefinition } from '../types';
|
||||||
import { parseUserSelectionConf } from './parseUserSelectionConf';
|
|
||||||
import defineMyInAppMessages from './defineMyInAppMessages';
|
|
||||||
import defineMyInAppChannels from './defineMyInAppChannels';
|
import defineMyInAppChannels from './defineMyInAppChannels';
|
||||||
|
import defineMyInAppMessages from './defineMyInAppMessages';
|
||||||
|
import { parseUserSelectionConf } from './parseUserSelectionConf';
|
||||||
|
|
||||||
type UserID = string;
|
type UserID = string;
|
||||||
type ClientID = string;
|
type ClientID = string;
|
||||||
@ -76,6 +75,7 @@ export default class InAppNotificationChannel extends BaseNotificationChannel {
|
|||||||
|
|
||||||
saveMessageToDB = async ({
|
saveMessageToDB = async ({
|
||||||
content,
|
content,
|
||||||
|
contentType,
|
||||||
status,
|
status,
|
||||||
userId,
|
userId,
|
||||||
title,
|
title,
|
||||||
@ -85,6 +85,7 @@ export default class InAppNotificationChannel extends BaseNotificationChannel {
|
|||||||
}: {
|
}: {
|
||||||
content: string;
|
content: string;
|
||||||
userId: number;
|
userId: number;
|
||||||
|
contentType: 'text' | 'HTML';
|
||||||
title: string;
|
title: string;
|
||||||
channelName: string;
|
channelName: string;
|
||||||
status: 'read' | 'unread';
|
status: 'read' | 'unread';
|
||||||
@ -97,6 +98,7 @@ export default class InAppNotificationChannel extends BaseNotificationChannel {
|
|||||||
content,
|
content,
|
||||||
title,
|
title,
|
||||||
channelName,
|
channelName,
|
||||||
|
contentType,
|
||||||
status,
|
status,
|
||||||
userId,
|
userId,
|
||||||
receiveTimestamp: receiveTimestamp ?? Date.now(),
|
receiveTimestamp: receiveTimestamp ?? Date.now(),
|
||||||
@ -109,7 +111,7 @@ export default class InAppNotificationChannel extends BaseNotificationChannel {
|
|||||||
send: SendFnType<InAppMessageFormValues> = async (params) => {
|
send: SendFnType<InAppMessageFormValues> = async (params) => {
|
||||||
const { channel, message, receivers } = params;
|
const { channel, message, receivers } = params;
|
||||||
let userIds: number[];
|
let userIds: number[];
|
||||||
const { content, title, options = {} } = message;
|
const { content, contentType, title, options = {} } = message;
|
||||||
const userRepo = this.app.db.getRepository('users');
|
const userRepo = this.app.db.getRepository('users');
|
||||||
if (receivers?.type === 'userId') {
|
if (receivers?.type === 'userId') {
|
||||||
userIds = receivers.value;
|
userIds = receivers.value;
|
||||||
@ -123,6 +125,7 @@ export default class InAppNotificationChannel extends BaseNotificationChannel {
|
|||||||
content,
|
content,
|
||||||
status: 'unread',
|
status: 'unread',
|
||||||
userId,
|
userId,
|
||||||
|
contentType,
|
||||||
channelName: channel.name,
|
channelName: channel.name,
|
||||||
options,
|
options,
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* This file is part of the NocoBase (R) project.
|
||||||
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||||
|
* Authors: NocoBase Team.
|
||||||
|
*
|
||||||
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||||
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Migration } from '@nocobase/server';
|
||||||
|
import { InAppMessagesDefinition } from '../../types';
|
||||||
|
export default class extends Migration {
|
||||||
|
on = 'afterLoad'; // 'beforeLoad' or 'afterLoad'
|
||||||
|
appVersion = '<1.6.24';
|
||||||
|
|
||||||
|
async up() {
|
||||||
|
const { db } = this.context;
|
||||||
|
|
||||||
|
const collection = db.getCollection(InAppMessagesDefinition.name);
|
||||||
|
if (!collection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const Field = db.getRepository('fields');
|
||||||
|
const existed = await Field.count({
|
||||||
|
filter: {
|
||||||
|
name: 'contentType',
|
||||||
|
collectionName: InAppMessagesDefinition.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (existed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.getRepository('fields').create({
|
||||||
|
values: {
|
||||||
|
collectionName: InAppMessagesDefinition.name,
|
||||||
|
name: 'contentType',
|
||||||
|
type: 'string',
|
||||||
|
interface: 'select',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{t("Content type")}}',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
options: [
|
||||||
|
{ label: '{{t("Text")}}', value: 'text' },
|
||||||
|
{ label: '{{t("HTML")}}', value: 'HTML' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
enum: ['HTML', 'text'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update existing records to have contentType = 'text'
|
||||||
|
// Update existing records to have 'map' as the default value
|
||||||
|
await db.getRepository(InAppMessagesDefinition.name).update({
|
||||||
|
filter: {
|
||||||
|
contentType: null,
|
||||||
|
},
|
||||||
|
values: {
|
||||||
|
contentType: 'text',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ export interface Message {
|
|||||||
title: string;
|
title: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
channelName: string;
|
channelName: string;
|
||||||
|
contentType: 'text' | 'HTML';
|
||||||
content: string;
|
content: string;
|
||||||
receiveTimestamp: number;
|
receiveTimestamp: number;
|
||||||
status: 'read' | 'unread';
|
status: 'read' | 'unread';
|
||||||
@ -35,6 +36,7 @@ export type SSEData = {
|
|||||||
export interface InAppMessageFormValues {
|
export interface InAppMessageFormValues {
|
||||||
receivers: string[];
|
receivers: string[];
|
||||||
content: string;
|
content: string;
|
||||||
|
contentType: 'text' | 'HTML';
|
||||||
senderName: string;
|
senderName: string;
|
||||||
senderId: string;
|
senderId: string;
|
||||||
url: string;
|
url: string;
|
||||||
@ -49,6 +51,7 @@ export const InAppMessagesDefinition = {
|
|||||||
channelName: 'channelName',
|
channelName: 'channelName',
|
||||||
userId: 'userId',
|
userId: 'userId',
|
||||||
content: 'content',
|
content: 'content',
|
||||||
|
contentType: 'contentType',
|
||||||
status: 'status',
|
status: 'status',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
receiveTimestamp: 'receiveTimestamp',
|
receiveTimestamp: 'receiveTimestamp',
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { CollectionOptions } from '@nocobase/client';
|
import { CollectionOptions } from '@nocobase/client';
|
||||||
import { InAppMessagesDefinition, ChannelsDefinition } from './index';
|
import { ChannelsDefinition, InAppMessagesDefinition } from './index';
|
||||||
|
|
||||||
export const messageCollection: CollectionOptions = {
|
export const messageCollection: CollectionOptions = {
|
||||||
name: InAppMessagesDefinition.name,
|
name: InAppMessagesDefinition.name,
|
||||||
@ -61,6 +61,27 @@ export const messageCollection: CollectionOptions = {
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: InAppMessagesDefinition.fieldNameMap.contentType,
|
||||||
|
type: 'string',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{t("Content type")}}',
|
||||||
|
interface: 'select',
|
||||||
|
uiSchema: {
|
||||||
|
type: 'string',
|
||||||
|
title: '{{t("Content type")}}',
|
||||||
|
'x-component': 'Select',
|
||||||
|
'x-component-props': {
|
||||||
|
options: [
|
||||||
|
{ label: '{{t("Text")}}', value: 'text' },
|
||||||
|
{ label: '{{t("HTML")}}', value: 'HTML' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: InAppMessagesDefinition.fieldNameMap.content,
|
name: InAppMessagesDefinition.fieldNameMap.content,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user