mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	chore: use new signal meta properties in event timeline (#8421)
https://linear.app/unleash/issue/2-2796/better-signals-integration Adds support to the following signal payload meta properties: - `unleash_title` - `unleash_description` - `unleash_icon` - `unleash_variant` Follows a logic similar to what we currently have for banners. E.g. [custom icon](https://docs.getunleash.io/reference/banners#custom-icon). ## Call signal endpoints   ## View signals in event timeline  
This commit is contained in:
		
							parent
							
								
									24b9e4987b
								
							
						
					
					
						commit
						7c5fab518f
					
				@ -14,12 +14,14 @@ export type TimelineEventType = 'signal' | EventSchemaType;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type RawTimelineEvent = EventSchema | ISignalQuerySignal;
 | 
					type RawTimelineEvent = EventSchema | ISignalQuerySignal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TimelineEvent = {
 | 
					export type TimelineEvent = {
 | 
				
			||||||
    id: number;
 | 
					    id: number;
 | 
				
			||||||
    timestamp: number;
 | 
					    timestamp: number;
 | 
				
			||||||
    type: TimelineEventType;
 | 
					    type: TimelineEventType;
 | 
				
			||||||
    label: string;
 | 
					    label: string;
 | 
				
			||||||
    summary: string;
 | 
					    summary: string;
 | 
				
			||||||
 | 
					    icon?: string;
 | 
				
			||||||
 | 
					    variant?: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type TimelineEventGroup = TimelineEvent[];
 | 
					export type TimelineEventGroup = TimelineEvent[];
 | 
				
			||||||
@ -111,6 +113,9 @@ const getTimestamp = (event: RawTimelineEvent) => {
 | 
				
			|||||||
const isInRange = (timestamp: number, startTime: number, endTime: number) =>
 | 
					const isInRange = (timestamp: number, startTime: number, endTime: number) =>
 | 
				
			||||||
    timestamp >= startTime && timestamp <= endTime;
 | 
					    timestamp >= startTime && timestamp <= endTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isValidString = (str: unknown): str is string =>
 | 
				
			||||||
 | 
					    typeof str === 'string' && str.trim().length > 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getTimelineEvent = (
 | 
					const getTimelineEvent = (
 | 
				
			||||||
    event: RawTimelineEvent,
 | 
					    event: RawTimelineEvent,
 | 
				
			||||||
    timestamp: number,
 | 
					    timestamp: number,
 | 
				
			||||||
@ -122,10 +127,19 @@ const getTimelineEvent = (
 | 
				
			|||||||
            sourceName = 'unknown source',
 | 
					            sourceName = 'unknown source',
 | 
				
			||||||
            sourceDescription,
 | 
					            sourceDescription,
 | 
				
			||||||
            tokenName,
 | 
					            tokenName,
 | 
				
			||||||
 | 
					            payload: {
 | 
				
			||||||
 | 
					                experimental_unleash_title,
 | 
				
			||||||
 | 
					                experimental_unleash_description,
 | 
				
			||||||
 | 
					                experimental_unleash_icon,
 | 
				
			||||||
 | 
					                experimental_unleash_variant,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
        } = event;
 | 
					        } = event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const label = `Signal: ${sourceName}`;
 | 
					        const title = experimental_unleash_title || sourceName;
 | 
				
			||||||
        const summary = `Signal originated from **[${sourceName} (${tokenName})](/integrations/signals)** endpoint${sourceDescription ? `: ${sourceDescription}` : ''}`;
 | 
					        const label = `Signal: ${title}`;
 | 
				
			||||||
 | 
					        const summary = experimental_unleash_description
 | 
				
			||||||
 | 
					            ? `Signal: **[${title}](/integrations/signals)** ${experimental_unleash_description}`
 | 
				
			||||||
 | 
					            : `Signal originated from **[${sourceName} (${tokenName})](/integrations/signals)** endpoint${sourceDescription ? `: ${sourceDescription}` : ''}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
            id,
 | 
					            id,
 | 
				
			||||||
@ -133,6 +147,12 @@ const getTimelineEvent = (
 | 
				
			|||||||
            type: 'signal',
 | 
					            type: 'signal',
 | 
				
			||||||
            label,
 | 
					            label,
 | 
				
			||||||
            summary,
 | 
					            summary,
 | 
				
			||||||
 | 
					            ...(isValidString(experimental_unleash_icon)
 | 
				
			||||||
 | 
					                ? { icon: experimental_unleash_icon }
 | 
				
			||||||
 | 
					                : {}),
 | 
				
			||||||
 | 
					            ...(isValidString(experimental_unleash_variant)
 | 
				
			||||||
 | 
					                ? { variant: experimental_unleash_variant }
 | 
				
			||||||
 | 
					                : {}),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,14 +4,18 @@ import FlagOutlinedIcon from '@mui/icons-material/FlagOutlined';
 | 
				
			|||||||
import ExtensionOutlinedIcon from '@mui/icons-material/ExtensionOutlined';
 | 
					import ExtensionOutlinedIcon from '@mui/icons-material/ExtensionOutlined';
 | 
				
			||||||
import SegmentsIcon from '@mui/icons-material/DonutLargeOutlined';
 | 
					import SegmentsIcon from '@mui/icons-material/DonutLargeOutlined';
 | 
				
			||||||
import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
 | 
					import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
 | 
				
			||||||
import { styled } from '@mui/material';
 | 
					import { Icon, styled } from '@mui/material';
 | 
				
			||||||
import type { TimelineEventGroup, TimelineEventType } from '../EventTimeline';
 | 
					import type {
 | 
				
			||||||
 | 
					    TimelineEvent,
 | 
				
			||||||
 | 
					    TimelineEventGroup,
 | 
				
			||||||
 | 
					    TimelineEventType,
 | 
				
			||||||
 | 
					} from '../EventTimeline';
 | 
				
			||||||
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
 | 
					import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
 | 
				
			||||||
import type { HTMLAttributes } from 'react';
 | 
					import type { HTMLAttributes } from 'react';
 | 
				
			||||||
import SensorsIcon from '@mui/icons-material/Sensors';
 | 
					import SensorsIcon from '@mui/icons-material/Sensors';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DefaultEventVariant = 'secondary';
 | 
					type DefaultEventVariant = 'secondary';
 | 
				
			||||||
type CustomEventVariant = 'success' | 'neutral' | 'warning';
 | 
					type CustomEventVariant = 'success' | 'neutral' | 'warning' | 'error';
 | 
				
			||||||
type EventVariant = DefaultEventVariant | CustomEventVariant;
 | 
					type EventVariant = DefaultEventVariant | CustomEventVariant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledEventCircle = styled('div', {
 | 
					const StyledEventCircle = styled('div', {
 | 
				
			||||||
@ -26,17 +30,26 @@ const StyledEventCircle = styled('div', {
 | 
				
			|||||||
    alignItems: 'center',
 | 
					    alignItems: 'center',
 | 
				
			||||||
    justifyContent: 'center',
 | 
					    justifyContent: 'center',
 | 
				
			||||||
    transition: 'transform 0.2s',
 | 
					    transition: 'transform 0.2s',
 | 
				
			||||||
    '& svg': {
 | 
					    '& svg, span': {
 | 
				
			||||||
        color: theme.palette[variant].main,
 | 
					        color: theme.palette[variant].main,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    '& svg': {
 | 
				
			||||||
        height: theme.spacing(2.5),
 | 
					        height: theme.spacing(2.5),
 | 
				
			||||||
        width: theme.spacing(2.5),
 | 
					        width: theme.spacing(2.5),
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    '& span': {
 | 
				
			||||||
 | 
					        fontSize: theme.fontSizes.bodySize,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    '&:hover': {
 | 
					    '&:hover': {
 | 
				
			||||||
        transform: 'scale(1.5)',
 | 
					        transform: 'scale(1.5)',
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getEventIcon = (type: TimelineEventType) => {
 | 
					const getEventIcon = ({ icon, type }: Pick<TimelineEvent, 'icon' | 'type'>) => {
 | 
				
			||||||
 | 
					    if (icon) {
 | 
				
			||||||
 | 
					        return <Icon>{icon}</Icon>;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (type === 'signal') {
 | 
					    if (type === 'signal') {
 | 
				
			||||||
        return <SensorsIcon />;
 | 
					        return <SensorsIcon />;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -72,6 +85,10 @@ const customEventVariants: Partial<
 | 
				
			|||||||
    'feature-archived': 'neutral',
 | 
					    'feature-archived': 'neutral',
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isValidVariant = (variant?: string): variant is EventVariant =>
 | 
				
			||||||
 | 
					    variant !== undefined &&
 | 
				
			||||||
 | 
					    ['secondary', 'success', 'neutral', 'warning', 'error'].includes(variant);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IEventTimelineEventCircleProps
 | 
					interface IEventTimelineEventCircleProps
 | 
				
			||||||
    extends HTMLAttributes<HTMLDivElement> {
 | 
					    extends HTMLAttributes<HTMLDivElement> {
 | 
				
			||||||
    group: TimelineEventGroup;
 | 
					    group: TimelineEventGroup;
 | 
				
			||||||
@ -83,16 +100,23 @@ export const EventTimelineEventCircle = ({
 | 
				
			|||||||
}: IEventTimelineEventCircleProps) => {
 | 
					}: IEventTimelineEventCircleProps) => {
 | 
				
			||||||
    if (
 | 
					    if (
 | 
				
			||||||
        group.length === 1 ||
 | 
					        group.length === 1 ||
 | 
				
			||||||
        !group.some(({ type }) => type !== group[0].type)
 | 
					        !group.some(
 | 
				
			||||||
 | 
					            ({ type, icon }) =>
 | 
				
			||||||
 | 
					                type !== group[0].type || icon !== group[0].icon,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        const event = group[0];
 | 
					        const event = group[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <StyledEventCircle
 | 
					            <StyledEventCircle
 | 
				
			||||||
                variant={customEventVariants[event.type]}
 | 
					                variant={
 | 
				
			||||||
 | 
					                    isValidVariant(event.variant)
 | 
				
			||||||
 | 
					                        ? event.variant
 | 
				
			||||||
 | 
					                        : customEventVariants[event.type]
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                {...props}
 | 
					                {...props}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
                {getEventIcon(event.type)}
 | 
					                {getEventIcon(event)}
 | 
				
			||||||
            </StyledEventCircle>
 | 
					            </StyledEventCircle>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -50,6 +50,7 @@ const StyledTooltipItem = styled('div')(({ theme }) => ({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const StyledEventTimelineEventCircle = styled(EventTimelineEventCircle)(
 | 
					const StyledEventTimelineEventCircle = styled(EventTimelineEventCircle)(
 | 
				
			||||||
    ({ theme }) => ({
 | 
					    ({ theme }) => ({
 | 
				
			||||||
 | 
					        flexShrink: 0,
 | 
				
			||||||
        marginTop: theme.spacing(0.125),
 | 
					        marginTop: theme.spacing(0.125),
 | 
				
			||||||
        height: theme.spacing(2.5),
 | 
					        height: theme.spacing(2.5),
 | 
				
			||||||
        width: theme.spacing(2.5),
 | 
					        width: theme.spacing(2.5),
 | 
				
			||||||
@ -57,6 +58,9 @@ const StyledEventTimelineEventCircle = styled(EventTimelineEventCircle)(
 | 
				
			|||||||
        '& > svg': {
 | 
					        '& > svg': {
 | 
				
			||||||
            height: theme.spacing(1.75),
 | 
					            height: theme.spacing(1.75),
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        '& > span': {
 | 
				
			||||||
 | 
					            fontSize: theme.fontSizes.smallBody,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        '&:hover': {
 | 
					        '&:hover': {
 | 
				
			||||||
            transform: 'none',
 | 
					            transform: 'none',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user