mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #2515 from omnivore-app/fix/push-notification
fix/push notification
This commit is contained in:
commit
0a227decdd
16 changed files with 241 additions and 106 deletions
|
|
@ -117,6 +117,7 @@
|
|||
"@types/highlightjs": "^9.12.2",
|
||||
"@types/intercom-client": "^2.11.8",
|
||||
"@types/jsonwebtoken": "^8.5.0",
|
||||
"@types/lodash.debounce": "^4.0.6",
|
||||
"@types/luxon": "^1.25.0",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@types/nanoid": "^3.0.0",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ export enum RuleActionType {
|
|||
SendNotification = 'SEND_NOTIFICATION',
|
||||
}
|
||||
|
||||
export enum RuleEventType {
|
||||
PageCreated = 'PAGE_CREATED',
|
||||
PageUpdated = 'PAGE_UPDATED',
|
||||
}
|
||||
|
||||
export interface RuleAction {
|
||||
type: RuleActionType
|
||||
params: string[]
|
||||
|
|
@ -42,6 +47,9 @@ export class Rule {
|
|||
@Column('text', { nullable: true })
|
||||
description?: string | null
|
||||
|
||||
@Column('text', { array: true })
|
||||
eventTypes!: RuleEventType[]
|
||||
|
||||
@Column('boolean', { default: true })
|
||||
enabled!: boolean
|
||||
|
||||
|
|
|
|||
|
|
@ -2140,6 +2140,7 @@ export type Rule = {
|
|||
actions: Array<RuleAction>;
|
||||
createdAt: Scalars['Date'];
|
||||
enabled: Scalars['Boolean'];
|
||||
eventTypes: Array<RuleEventType>;
|
||||
filter: Scalars['String'];
|
||||
id: Scalars['ID'];
|
||||
name: Scalars['String'];
|
||||
|
|
@ -2164,6 +2165,11 @@ export enum RuleActionType {
|
|||
SendNotification = 'SEND_NOTIFICATION'
|
||||
}
|
||||
|
||||
export enum RuleEventType {
|
||||
PageCreated = 'PAGE_CREATED',
|
||||
PageUpdated = 'PAGE_UPDATED'
|
||||
}
|
||||
|
||||
export type RulesError = {
|
||||
__typename?: 'RulesError';
|
||||
errorCodes: Array<RulesErrorCode>;
|
||||
|
|
@ -2529,6 +2535,7 @@ export type SetRuleInput = {
|
|||
actions: Array<RuleActionInput>;
|
||||
description?: InputMaybe<Scalars['String']>;
|
||||
enabled: Scalars['Boolean'];
|
||||
eventTypes: Array<RuleEventType>;
|
||||
filter: Scalars['String'];
|
||||
id?: InputMaybe<Scalars['ID']>;
|
||||
name: Scalars['String'];
|
||||
|
|
@ -3643,6 +3650,7 @@ export type ResolversTypes = {
|
|||
RuleAction: ResolverTypeWrapper<RuleAction>;
|
||||
RuleActionInput: RuleActionInput;
|
||||
RuleActionType: RuleActionType;
|
||||
RuleEventType: RuleEventType;
|
||||
RulesError: ResolverTypeWrapper<RulesError>;
|
||||
RulesErrorCode: RulesErrorCode;
|
||||
RulesResult: ResolversTypes['RulesError'] | ResolversTypes['RulesSuccess'];
|
||||
|
|
@ -5473,6 +5481,7 @@ export type RuleResolvers<ContextType = ResolverContext, ParentType extends Reso
|
|||
actions?: Resolver<Array<ResolversTypes['RuleAction']>, ParentType, ContextType>;
|
||||
createdAt?: Resolver<ResolversTypes['Date'], ParentType, ContextType>;
|
||||
enabled?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
eventTypes?: Resolver<Array<ResolversTypes['RuleEventType']>, ParentType, ContextType>;
|
||||
filter?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
|
|
|
|||
|
|
@ -1528,6 +1528,7 @@ type Rule {
|
|||
actions: [RuleAction!]!
|
||||
createdAt: Date!
|
||||
enabled: Boolean!
|
||||
eventTypes: [RuleEventType!]!
|
||||
filter: String!
|
||||
id: ID!
|
||||
name: String!
|
||||
|
|
@ -1551,6 +1552,11 @@ enum RuleActionType {
|
|||
SEND_NOTIFICATION
|
||||
}
|
||||
|
||||
enum RuleEventType {
|
||||
PAGE_CREATED
|
||||
PAGE_UPDATED
|
||||
}
|
||||
|
||||
type RulesError {
|
||||
errorCodes: [RulesErrorCode!]!
|
||||
}
|
||||
|
|
@ -1889,6 +1895,7 @@ input SetRuleInput {
|
|||
actions: [RuleActionInput!]!
|
||||
description: String
|
||||
enabled: Boolean!
|
||||
eventTypes: [RuleEventType!]!
|
||||
filter: String!
|
||||
id: ID
|
||||
name: String!
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import cors from 'cors'
|
||||
import express from 'express'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import debounce from 'lodash.debounce'
|
||||
import { env } from '../env'
|
||||
import { Claims } from '../resolvers/types'
|
||||
import { getDeviceTokensByUserId } from '../services/user_device_tokens'
|
||||
import { corsConfig } from '../utils/corsConfig'
|
||||
import {
|
||||
PushNotificationType,
|
||||
sendMulticastPushNotifications,
|
||||
} from '../utils/sendNotification'
|
||||
import cors from 'cors'
|
||||
import { corsConfig } from '../utils/corsConfig'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import { env } from '../env'
|
||||
import { Claims } from '../resolvers/types'
|
||||
|
||||
interface Notification {
|
||||
body: string
|
||||
|
|
@ -57,14 +58,17 @@ export function notificationRouter() {
|
|||
tokens: tokens.map((token) => token.token),
|
||||
}
|
||||
|
||||
const result = await sendMulticastPushNotifications(
|
||||
userId,
|
||||
message,
|
||||
notificationType || 'rule'
|
||||
)
|
||||
if (!result) {
|
||||
return res.status(400).send({ errorCode: 'SEND_NOTIFICATION_FAILED' })
|
||||
}
|
||||
// Debounce the sendMulticastPushNotifications function with a delay of 1 minute
|
||||
debounce(async () => {
|
||||
const result = await sendMulticastPushNotifications(
|
||||
userId,
|
||||
message,
|
||||
notificationType || 'rule'
|
||||
)
|
||||
if (!result) {
|
||||
return res.status(400).send({ errorCode: 'SEND_NOTIFICATION_FAILED' })
|
||||
}
|
||||
}, 60 * 1000)
|
||||
|
||||
res.send('OK')
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
import express from 'express'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { initModels } from '../../server'
|
||||
import { kx } from '../../datalayer/knex_config'
|
||||
import { setClaims } from '../../datalayer/helpers'
|
||||
import { sendEmail } from '../../utils/sendEmail'
|
||||
import { env, homePageURL } from '../../env'
|
||||
import { MulticastMessage } from 'firebase-admin/messaging'
|
||||
import { setClaims } from '../../datalayer/helpers'
|
||||
import { kx } from '../../datalayer/knex_config'
|
||||
import { createPubSubClient } from '../../datalayer/pubsub'
|
||||
import { updatePage } from '../../elastic/pages'
|
||||
import { UserDeviceToken } from '../../entity/user_device_tokens'
|
||||
import { env, homePageURL } from '../../env'
|
||||
import { ContentReader } from '../../generated/graphql'
|
||||
import { DataModels } from '../../resolvers/types'
|
||||
import { updatePage } from '../../elastic/pages'
|
||||
import { createPubSubClient } from '../../datalayer/pubsub'
|
||||
import { initModels } from '../../server'
|
||||
import { getPagesWithReminder, PageReminder } from '../../services/reminders'
|
||||
import { getDeviceTokensByUserId } from '../../services/user_device_tokens'
|
||||
import { analytics } from '../../utils/analytics'
|
||||
import { sendEmail } from '../../utils/sendEmail'
|
||||
import { sendMulticastPushNotifications } from '../../utils/sendNotification'
|
||||
|
||||
interface PageToNotify {
|
||||
title: string
|
||||
|
|
@ -106,19 +108,19 @@ export function remindersServiceRouter() {
|
|||
to: user.email,
|
||||
})
|
||||
|
||||
// // send push notifications
|
||||
// const deviceTokens = await getDeviceTokensByUserId(userId)
|
||||
// if (deviceTokens && deviceTokens.length > 0) {
|
||||
// const message = messageForPages(pageReminders, deviceTokens)
|
||||
// await sendMulticastPushNotifications(userId, message, 'reminder')
|
||||
// }
|
||||
//
|
||||
// if (!deviceTokens) {
|
||||
// console.log('Device tokens not set:', userId)
|
||||
//
|
||||
// res.status(400).send('Device token Not Found')
|
||||
// return
|
||||
// }
|
||||
// send push notifications
|
||||
const deviceTokens = await getDeviceTokensByUserId(userId)
|
||||
if (deviceTokens && deviceTokens.length > 0) {
|
||||
const message = messageForPages(pageReminders, deviceTokens)
|
||||
await sendMulticastPushNotifications(userId, message, 'reminder')
|
||||
}
|
||||
|
||||
if (!deviceTokens) {
|
||||
console.log('Device tokens not set:', userId)
|
||||
|
||||
res.status(400).send('Device token Not Found')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
await updateRemindersStatus(models, userId, pagesToUnarchive, remindAt)
|
||||
|
|
|
|||
|
|
@ -2066,6 +2066,7 @@ const schema = gql`
|
|||
enabled: Boolean!
|
||||
createdAt: Date!
|
||||
updatedAt: Date!
|
||||
eventTypes: [RuleEventType!]!
|
||||
}
|
||||
|
||||
type RuleAction {
|
||||
|
|
@ -2089,6 +2090,11 @@ const schema = gql`
|
|||
BAD_REQUEST
|
||||
}
|
||||
|
||||
enum RuleEventType {
|
||||
PAGE_CREATED
|
||||
PAGE_UPDATED
|
||||
}
|
||||
|
||||
input SetRuleInput {
|
||||
id: ID
|
||||
name: String!
|
||||
|
|
@ -2096,6 +2102,7 @@ const schema = gql`
|
|||
filter: String!
|
||||
actions: [RuleActionInput!]!
|
||||
enabled: Boolean!
|
||||
eventTypes: [RuleEventType!]!
|
||||
}
|
||||
|
||||
input RuleActionInput {
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ import { ContentReader } from '../generated/graphql'
|
|||
import { analytics } from '../utils/analytics'
|
||||
import { isBase64Image } from '../utils/helpers'
|
||||
import { fetchFavicon } from '../utils/parser'
|
||||
import { sendMulticastPushNotifications } from '../utils/sendNotification'
|
||||
import { addLabelToPage } from './labels'
|
||||
import { SaveContext, saveEmail, SaveEmailInput } from './save_email'
|
||||
import { saveSubscription } from './subscriptions'
|
||||
import { getDeviceTokensByUserId } from './user_device_tokens'
|
||||
|
||||
export interface NewsletterMessage {
|
||||
email: string
|
||||
|
|
@ -93,19 +95,19 @@ export const saveNewsletterEmail = async (
|
|||
})
|
||||
console.log('newsletter label added:', result)
|
||||
|
||||
// // sends push notification
|
||||
// const deviceTokens = await getDeviceTokensByUserId(newsletterEmail.user.id)
|
||||
// if (!deviceTokens) {
|
||||
// console.log('Device tokens not set:', newsletterEmail.user.id)
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// const multicastMessage = messageForLink(page, deviceTokens)
|
||||
// await sendMulticastPushNotifications(
|
||||
// newsletterEmail.user.id,
|
||||
// multicastMessage,
|
||||
// 'newsletter'
|
||||
// )
|
||||
// sends push notification
|
||||
const deviceTokens = await getDeviceTokensByUserId(newsletterEmail.user.id)
|
||||
if (!deviceTokens) {
|
||||
console.log('Device tokens not set:', newsletterEmail.user.id)
|
||||
return true
|
||||
}
|
||||
|
||||
const multicastMessage = messageForLink(page, deviceTokens)
|
||||
await sendMulticastPushNotifications(
|
||||
newsletterEmail.user.id,
|
||||
multicastMessage,
|
||||
'newsletter'
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ describe('Rules Resolver', () => {
|
|||
}`
|
||||
)}],
|
||||
enabled: ${enabled}
|
||||
eventTypes: [PAGE_CREATED, PAGE_UPDATED]
|
||||
}) {
|
||||
... on SetRuleSuccess {
|
||||
rule {
|
||||
|
|
@ -116,6 +117,7 @@ describe('Rules Resolver', () => {
|
|||
enabled
|
||||
createdAt
|
||||
updatedAt
|
||||
eventTypes
|
||||
}
|
||||
}
|
||||
... on RulesError {
|
||||
|
|
|
|||
21
packages/db/migrations/0117.do.add_event_types_to_rules.sql
Executable file
21
packages/db/migrations/0117.do.add_event_types_to_rules.sql
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
-- Type: DO
|
||||
-- Name: add_event_types_to_rules
|
||||
-- Description: Add event_types field to rules table
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE omnivore.rules ADD COLUMN event_types text[] NOT NULL DEFAULT '{PAGE_CREATED,PAGE_UPDATED}';
|
||||
|
||||
-- Add event_types to existing rules
|
||||
UPDATE omnivore.rules
|
||||
SET
|
||||
event_types = '{PAGE_CREATED}', filter = REPLACE (filter, 'event:created', '')
|
||||
WHERE
|
||||
filter LIKE '%event:created%';
|
||||
UPDATE omnivore.rules
|
||||
SET
|
||||
event_types = '{PAGE_UPDATED}', filter = REPLACE (filter, 'event:updated', '')
|
||||
WHERE
|
||||
filter LIKE '%event:updated%';
|
||||
|
||||
COMMIT;
|
||||
20
packages/db/migrations/0117.undo.add_event_types_to_rules.sql
Executable file
20
packages/db/migrations/0117.undo.add_event_types_to_rules.sql
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
-- Type: UNDO
|
||||
-- Name: add_event_types_to_rules
|
||||
-- Description: Add event_types field to rules table
|
||||
|
||||
BEGIN;
|
||||
|
||||
UPDATE omnivore.rules
|
||||
SET
|
||||
filter = CONCAT ('event:created ', filter)
|
||||
WHERE
|
||||
'PAGE_CREATED' = ALL (event_types);
|
||||
UPDATE omnivore.rules
|
||||
SET
|
||||
filter = CONCAT ('event:updated ', filter)
|
||||
WHERE
|
||||
'PAGE_UPDATED' = ALL (event_types);
|
||||
|
||||
ALTER TABLE omnivore.rules DROP COLUMN event_types;
|
||||
|
||||
COMMIT;
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import * as Sentry from '@sentry/serverless'
|
||||
import express, { Request, Response } from 'express'
|
||||
import * as dotenv from 'dotenv'
|
||||
import { getEnabledRules, triggerActions } from './rule'
|
||||
import { promisify } from 'util'
|
||||
import express, { Request, Response } from 'express'
|
||||
import * as jwt from 'jsonwebtoken'
|
||||
import { promisify } from 'util'
|
||||
import { getEnabledRules, RuleEventType, triggerActions } from './rule'
|
||||
|
||||
const signToken = promisify(jwt.sign)
|
||||
|
||||
|
|
@ -76,6 +76,10 @@ export const getAuthToken = async (
|
|||
return auth as string
|
||||
}
|
||||
|
||||
const ruleEventType = (eventType: string) => {
|
||||
return `PAGE_${eventType.toUpperCase()}` as RuleEventType
|
||||
}
|
||||
|
||||
export const ruleHandler = Sentry.GCPFunction.wrapHttpFunction(
|
||||
async (req: Request, res: Response) => {
|
||||
const apiEndpoint = process.env.REST_BACKEND_ENDPOINT
|
||||
|
|
@ -134,7 +138,7 @@ export const ruleHandler = Sentry.GCPFunction.wrapHttpFunction(
|
|||
data,
|
||||
apiEndpoint,
|
||||
jwtSecret,
|
||||
eventType
|
||||
ruleEventType(eventType)
|
||||
)
|
||||
if (triggeredActions.length === 0) {
|
||||
console.log('No actions triggered')
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import axios, { AxiosResponse } from 'axios'
|
|||
import { filterPage } from './filter'
|
||||
import { getAuthToken, PubSubData } from './index'
|
||||
import { setLabels } from './label'
|
||||
import { NotificationData, sendNotification } from './notification'
|
||||
import { archivePage, markPageAsRead } from './page'
|
||||
|
||||
export enum RuleActionType {
|
||||
|
|
@ -16,6 +17,11 @@ export interface RuleAction {
|
|||
params: string[]
|
||||
}
|
||||
|
||||
export enum RuleEventType {
|
||||
PageCreated = 'PAGE_CREATED',
|
||||
PageUpdated = 'PAGE_UPDATED',
|
||||
}
|
||||
|
||||
export interface Rule {
|
||||
id: string
|
||||
userId: string
|
||||
|
|
@ -26,10 +32,9 @@ export interface Rule {
|
|||
enabled: boolean
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
eventTypes: RuleEventType[]
|
||||
}
|
||||
|
||||
const EVENT_FILTERS = ['event:created', 'event:updated']
|
||||
|
||||
export const getEnabledRules = async (
|
||||
userId: string,
|
||||
apiEndpoint: string,
|
||||
|
|
@ -52,6 +57,7 @@ export const getEnabledRules = async (
|
|||
type
|
||||
params
|
||||
}
|
||||
eventTypes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,33 +81,22 @@ export const triggerActions = async (
|
|||
data: PubSubData,
|
||||
apiEndpoint: string,
|
||||
jwtSecret: string,
|
||||
eventType: string
|
||||
eventType: RuleEventType
|
||||
) => {
|
||||
const authToken = await getAuthToken(userId, jwtSecret)
|
||||
const actionPromises: Promise<AxiosResponse<any, any> | undefined>[] = []
|
||||
|
||||
for (const rule of rules) {
|
||||
let filter = rule.filter
|
||||
const filters = filter.split(' ')
|
||||
|
||||
// Check if the rule is enabled for the event type
|
||||
const eventFilterIndex = filters.findIndex((f) => EVENT_FILTERS.includes(f))
|
||||
if (eventFilterIndex !== -1) {
|
||||
const eventFilter = filters[eventFilterIndex]
|
||||
if (eventFilter !== `event:${eventType}`.toLowerCase()) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove the event filter from the filter string
|
||||
filters.splice(eventFilterIndex, 1)
|
||||
filter = filters.join(' ')
|
||||
if (!rule.eventTypes.includes(eventType)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const filteredPage = await filterPage(
|
||||
userId,
|
||||
apiEndpoint,
|
||||
authToken,
|
||||
filter,
|
||||
rule.filter,
|
||||
data.id
|
||||
)
|
||||
if (!filteredPage) {
|
||||
|
|
@ -140,25 +135,25 @@ export const triggerActions = async (
|
|||
filteredPage.readingProgressPercent < 100 &&
|
||||
actionPromises.push(markPageAsRead(apiEndpoint, authToken, data.id))
|
||||
)
|
||||
// case RuleActionType.SendNotification: {
|
||||
// const data: NotificationData = {
|
||||
// title: 'New page added to your library',
|
||||
// body: filteredPage.title,
|
||||
// image: filteredPage.image || undefined,
|
||||
// }
|
||||
case RuleActionType.SendNotification: {
|
||||
const data: NotificationData = {
|
||||
title: 'New page added to your library',
|
||||
body: filteredPage.title,
|
||||
image: filteredPage.image || undefined,
|
||||
}
|
||||
|
||||
// const params = action.params
|
||||
// if (params.length > 0) {
|
||||
// const param = JSON.parse(params[0]) as NotificationData
|
||||
// data.body = param.body || data.body
|
||||
// data.title = param.title || data.title
|
||||
// data.image = param.image || data.image
|
||||
// }
|
||||
const params = action.params
|
||||
if (params.length > 0) {
|
||||
const param = JSON.parse(params[0]) as NotificationData
|
||||
data.body = param.body || data.body
|
||||
data.title = param.title || data.title
|
||||
data.image = param.image || data.image
|
||||
}
|
||||
|
||||
// return actionPromises.push(
|
||||
// sendNotification(apiEndpoint, authToken, data)
|
||||
// )
|
||||
// }
|
||||
return actionPromises.push(
|
||||
sendNotification(apiEndpoint, authToken, data)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import { Rule, RuleAction } from '../queries/useGetRulesQuery'
|
||||
import { Rule, RuleAction, RuleEventType } from '../queries/useGetRulesQuery'
|
||||
|
||||
export type SetRuleInput = {
|
||||
id?: string
|
||||
|
|
@ -8,6 +8,7 @@ export type SetRuleInput = {
|
|||
filter: string
|
||||
actions: RuleAction[]
|
||||
enabled: boolean
|
||||
eventTypes: RuleEventType[]
|
||||
}
|
||||
|
||||
type SetRuleResult = {
|
||||
|
|
@ -35,6 +36,7 @@ export async function setRuleMutation(
|
|||
params
|
||||
}
|
||||
enabled
|
||||
eventTypes
|
||||
}
|
||||
}
|
||||
... on SetRuleError {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,12 @@ export enum RuleActionType {
|
|||
AddLabel = 'ADD_LABEL',
|
||||
Archive = 'ARCHIVE',
|
||||
MarkAsRead = 'MARK_AS_READ',
|
||||
// SendNotification = 'SEND_NOTIFICATION',
|
||||
SendNotification = 'SEND_NOTIFICATION',
|
||||
}
|
||||
|
||||
export enum RuleEventType {
|
||||
PAGE_CREATED = 'PAGE_CREATED',
|
||||
PAGE_UPDATED = 'PAGE_UPDATED',
|
||||
}
|
||||
|
||||
export interface Rule {
|
||||
|
|
@ -22,6 +27,7 @@ export interface Rule {
|
|||
enabled: boolean
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
eventTypes: RuleEventType[]
|
||||
}
|
||||
|
||||
interface RulesQueryResponse {
|
||||
|
|
@ -54,6 +60,7 @@ export function useGetRulesQuery(): RulesQueryResponse {
|
|||
enabled
|
||||
createdAt
|
||||
updatedAt
|
||||
eventTypes
|
||||
}
|
||||
}
|
||||
... on RulesError {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,23 @@
|
|||
import { Button, Form, Input, Modal, Select, Space, Table, Tag } from 'antd'
|
||||
// import 'antd/dist/antd.dark.css'
|
||||
import 'antd/dist/antd.compact.css'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
import { Box, HStack } from '../../components/elements/LayoutPrimitives'
|
||||
import { SettingsLayout } from '../../components/templates/SettingsLayout'
|
||||
import { Label } from '../../lib/networking/fragments/labelFragment'
|
||||
import { deleteRuleMutation } from '../../lib/networking/mutations/deleteRuleMutation'
|
||||
import { setRuleMutation } from '../../lib/networking/mutations/setRuleMutation'
|
||||
import { useGetLabelsQuery } from '../../lib/networking/queries/useGetLabelsQuery'
|
||||
import {
|
||||
Rule,
|
||||
RuleAction,
|
||||
RuleActionType,
|
||||
RuleEventType,
|
||||
useGetRulesQuery,
|
||||
} from '../../lib/networking/queries/useGetRulesQuery'
|
||||
|
||||
import { SettingsLayout } from '../../components/templates/SettingsLayout'
|
||||
import { Button, Space, Table, Form, Input, Modal, Tag, Select } from 'antd'
|
||||
|
||||
// import 'antd/dist/antd.dark.css'
|
||||
import 'antd/dist/antd.compact.css'
|
||||
|
||||
import { Box, HStack } from '../../components/elements/LayoutPrimitives'
|
||||
import { useGetLabelsQuery } from '../../lib/networking/queries/useGetLabelsQuery'
|
||||
import { Label } from '../../lib/networking/fragments/labelFragment'
|
||||
import { setRuleMutation } from '../../lib/networking/mutations/setRuleMutation'
|
||||
import { deleteRuleMutation } from '../../lib/networking/mutations/deleteRuleMutation'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
|
||||
type CreateRuleModalProps = {
|
||||
isModalOpen: boolean
|
||||
|
|
@ -34,11 +31,13 @@ const CreateRuleModal = (props: CreateRuleModalProps): JSX.Element => {
|
|||
const onOk = async (values: any) => {
|
||||
const name = form.getFieldValue('name')
|
||||
const filter = form.getFieldValue('filter')
|
||||
const eventTypes = form.getFieldValue('eventTypes')
|
||||
await setRuleMutation({
|
||||
name,
|
||||
filter,
|
||||
actions: [],
|
||||
enabled: true,
|
||||
eventTypes,
|
||||
})
|
||||
form.resetFields()
|
||||
props.setIsModalOpen(false)
|
||||
|
|
@ -55,7 +54,7 @@ const CreateRuleModal = (props: CreateRuleModalProps): JSX.Element => {
|
|||
<Modal
|
||||
title="Create Rule"
|
||||
open={props.isModalOpen}
|
||||
onOk={onOk}
|
||||
onOk={form.submit}
|
||||
onCancel={onCancel}
|
||||
destroyOnClose={true}
|
||||
>
|
||||
|
|
@ -64,7 +63,7 @@ const CreateRuleModal = (props: CreateRuleModalProps): JSX.Element => {
|
|||
name="createRule"
|
||||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
// onFinish={onFinish}
|
||||
onFinish={onOk}
|
||||
// onFinishFailed={onFinishFailed}
|
||||
autoComplete="off"
|
||||
>
|
||||
|
|
@ -83,6 +82,32 @@ const CreateRuleModal = (props: CreateRuleModalProps): JSX.Element => {
|
|||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="When"
|
||||
name="eventTypes"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select when the rule will be triggered',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
placeholder="Please select when the rule will be triggered"
|
||||
>
|
||||
{Object.keys(RuleEventType).map((key: string, index: number) => {
|
||||
const value = Object.values(RuleEventType)[index]
|
||||
return (
|
||||
<Select.Option key={key} value={value}>
|
||||
{key}
|
||||
</Select.Option>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
|
|
@ -115,6 +140,7 @@ const CreateActionModal = (props: CreateActionModalProps): JSX.Element => {
|
|||
params: params,
|
||||
},
|
||||
],
|
||||
eventTypes: props.rule.eventTypes,
|
||||
})
|
||||
form.resetFields()
|
||||
props.setIsModalOpen(false)
|
||||
|
|
@ -131,7 +157,7 @@ const CreateActionModal = (props: CreateActionModalProps): JSX.Element => {
|
|||
<Modal
|
||||
title="Create Action"
|
||||
open={props.rule != undefined}
|
||||
onOk={onOk}
|
||||
onOk={form.submit}
|
||||
onCancel={() => {
|
||||
form.resetFields()
|
||||
props.setIsModalOpen(false)
|
||||
|
|
@ -142,6 +168,7 @@ const CreateActionModal = (props: CreateActionModalProps): JSX.Element => {
|
|||
labelCol={{ span: 8 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
autoComplete="off"
|
||||
onFinish={onOk}
|
||||
>
|
||||
<Form.Item
|
||||
label="Action"
|
||||
|
|
@ -203,6 +230,7 @@ export default function Rules(): JSX.Element {
|
|||
name: rule.name,
|
||||
filter: rule.filter,
|
||||
actions: rule.actions,
|
||||
eventTypes: rule.eventTypes,
|
||||
}
|
||||
})
|
||||
}, [rules])
|
||||
|
|
@ -250,6 +278,20 @@ export default function Rules(): JSX.Element {
|
|||
dataIndex: 'filter',
|
||||
key: 'filter',
|
||||
},
|
||||
{
|
||||
title: 'When',
|
||||
render: (text: string, row: { eventTypes: RuleEventType[] }) => (
|
||||
<>
|
||||
{row.eventTypes.map((eventType: RuleEventType, index: number) => {
|
||||
return (
|
||||
<Tag color={'geekblue'} key={index}>
|
||||
{eventType}
|
||||
</Tag>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Actions',
|
||||
render: (text: string, row: { actions: RuleAction[] }) => (
|
||||
|
|
@ -324,7 +366,9 @@ export default function Rules(): JSX.Element {
|
|||
revalidate={revalidate}
|
||||
/>
|
||||
|
||||
<Box css={{ pt: '44px', px: '10%', '@smDown': { px: '0' } }}>
|
||||
<Box
|
||||
css={{ pt: '44px', px: '10%', '@smDown': { px: '0' }, width: '100%' }}
|
||||
>
|
||||
<HStack css={{ py: '16px' }} distribution="end">
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue