mirror of
https://github.com/langgenius/dify.git
synced 2026-03-21 06:18:27 +08:00
feat: use api to show notification
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import * as React from 'react'
|
||||
import { AppInitializer } from '@/app/components/app-initializer'
|
||||
import InSiteMessageNotification from '@/app/components/app/in-site-message-notification'
|
||||
import AmplitudeProvider from '@/app/components/base/amplitude'
|
||||
import GA, { GaType } from '@/app/components/base/ga'
|
||||
import Zendesk from '@/app/components/base/zendesk'
|
||||
@ -32,6 +33,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
||||
<RoleRouteGuard>
|
||||
{children}
|
||||
</RoleRouteGuard>
|
||||
<InSiteMessageNotification />
|
||||
<PartnerStack />
|
||||
<ReadmePanel />
|
||||
<GotoAnything />
|
||||
|
||||
100
web/app/components/app/in-site-message-notification.tsx
Normal file
100
web/app/components/app/in-site-message-notification.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
'use client'
|
||||
|
||||
import type { InSiteMessageActionItem } from './in-site-message'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { IS_CLOUD_EDITION } from '@/config'
|
||||
import { consoleClient, consoleQuery } from '@/service/client'
|
||||
import InSiteMessage from './in-site-message'
|
||||
|
||||
type NotificationBodyPayload = {
|
||||
actions: InSiteMessageActionItem[]
|
||||
main: string
|
||||
}
|
||||
|
||||
function isValidActionItem(value: unknown): value is InSiteMessageActionItem {
|
||||
if (!value || typeof value !== 'object')
|
||||
return false
|
||||
|
||||
const candidate = value as {
|
||||
action?: unknown
|
||||
data?: unknown
|
||||
text?: unknown
|
||||
type?: unknown
|
||||
}
|
||||
|
||||
return (
|
||||
typeof candidate.text === 'string'
|
||||
&& (candidate.type === 'primary' || candidate.type === 'default')
|
||||
&& (candidate.action === 'link' || candidate.action === 'close')
|
||||
&& (candidate.data === undefined || typeof candidate.data !== 'function')
|
||||
)
|
||||
}
|
||||
|
||||
function parseNotificationBody(body: string): NotificationBodyPayload | null {
|
||||
try {
|
||||
const parsed = JSON.parse(body) as {
|
||||
actions?: unknown
|
||||
main?: unknown
|
||||
}
|
||||
|
||||
if (!parsed || typeof parsed !== 'object')
|
||||
return null
|
||||
|
||||
if (typeof parsed.main !== 'string')
|
||||
return null
|
||||
|
||||
const actions = Array.isArray(parsed.actions)
|
||||
? parsed.actions.filter(isValidActionItem)
|
||||
: []
|
||||
|
||||
return {
|
||||
main: parsed.main,
|
||||
actions,
|
||||
}
|
||||
}
|
||||
catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function InSiteMessageNotification() {
|
||||
const { t } = useTranslation()
|
||||
const { data } = useQuery({
|
||||
queryKey: consoleQuery.notification.queryKey(),
|
||||
queryFn: async () => {
|
||||
return await consoleClient.notification()
|
||||
},
|
||||
enabled: IS_CLOUD_EDITION,
|
||||
})
|
||||
|
||||
const notification = data?.notifications?.[0]
|
||||
const parsedBody = notification ? parseNotificationBody(notification.body) : null
|
||||
|
||||
if (!IS_CLOUD_EDITION || !notification)
|
||||
return null
|
||||
|
||||
const fallbackActions: InSiteMessageActionItem[] = [
|
||||
{
|
||||
type: 'default',
|
||||
text: t('operation.close', { ns: 'common' }),
|
||||
action: 'close',
|
||||
},
|
||||
]
|
||||
|
||||
const actions = parsedBody?.actions?.length ? parsedBody.actions : fallbackActions
|
||||
const main = parsedBody?.main ?? 'Invalid notification body'
|
||||
|
||||
return (
|
||||
<InSiteMessage
|
||||
key={notification.notification_id}
|
||||
title={notification.title}
|
||||
subtitle={notification.subtitle}
|
||||
headerBgUrl={notification.title_pic_url}
|
||||
main={main}
|
||||
actions={actions}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default InSiteMessageNotification
|
||||
24
web/contract/console/notification.ts
Normal file
24
web/contract/console/notification.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { type } from '@orpc/contract'
|
||||
import { base } from '../base'
|
||||
|
||||
export type ConsoleNotification = {
|
||||
body: string
|
||||
frequency: 'once' | 'always'
|
||||
lang: string
|
||||
notification_id: string
|
||||
subtitle: string
|
||||
title: string
|
||||
title_pic_url?: string
|
||||
}
|
||||
|
||||
export type ConsoleNotificationResponse = {
|
||||
notifications: ConsoleNotification[]
|
||||
should_show: boolean
|
||||
}
|
||||
|
||||
export const notificationContract = base
|
||||
.route({
|
||||
path: '/notification',
|
||||
method: 'GET',
|
||||
})
|
||||
.output(type<ConsoleNotificationResponse>())
|
||||
@ -12,6 +12,7 @@ import {
|
||||
exploreInstalledAppsContract,
|
||||
exploreInstalledAppUninstallContract,
|
||||
} from './console/explore'
|
||||
import { notificationContract } from './console/notification'
|
||||
import { systemFeaturesContract } from './console/system'
|
||||
import {
|
||||
triggerOAuthConfigContract,
|
||||
@ -67,6 +68,7 @@ export const consoleRouterContract = {
|
||||
invoices: invoicesContract,
|
||||
bindPartnerStack: bindPartnerStackContract,
|
||||
},
|
||||
notification: notificationContract,
|
||||
triggers: {
|
||||
list: triggersContract,
|
||||
providerInfo: triggerProviderInfoContract,
|
||||
|
||||
Reference in New Issue
Block a user