import { css, cx } from '@emotion/css'; import React, { PureComponent } from 'react'; import { Unsubscribable } from 'rxjs'; import { PanelProps, DataFrameView, dateTimeFormat, GrafanaTheme2, textUtil } from '@grafana/data'; import { RefreshEvent } from '@grafana/runtime'; import { CustomScrollbar, stylesFactory } from '@grafana/ui'; import config from 'app/core/config'; import { DEFAULT_FEED_URL } from './constants'; import { loadFeed } from './feed'; import { PanelOptions } from './models.gen'; import { NewsItem } from './types'; import { feedToDataFrame } from './utils'; interface Props extends PanelProps {} interface State { news?: DataFrameView; isError?: boolean; } export class NewsPanel extends PureComponent { private refreshSubscription: Unsubscribable; constructor(props: Props) { super(props); this.refreshSubscription = this.props.eventBus.subscribe(RefreshEvent, this.loadChannel.bind(this)); this.state = {}; } componentDidMount(): void { this.loadChannel(); } componentWillUnmount(): void { this.refreshSubscription.unsubscribe(); } componentDidUpdate(prevProps: Props): void { if (this.props.options.feedUrl !== prevProps.options.feedUrl) { this.loadChannel(); } } async loadChannel() { const { options } = this.props; try { const url = options.feedUrl || DEFAULT_FEED_URL; const feed = await loadFeed(url); const frame = feedToDataFrame(feed); this.setState({ news: new DataFrameView(frame), isError: false, }); } catch (err) { console.error('Error Loading News', err); this.setState({ news: undefined, isError: true, }); } } render() { const { width } = this.props; const { showImage } = this.props.options; const { isError, news } = this.state; const styles = getStyles(config.theme2); const useWideLayout = width > 600; if (isError) { return
Error loading RSS feed.
; } if (!news) { return
Loading...
; } return ( {news.map((item, index) => { return (
{showImage && item.ogImage && ( {item.title} )}

{item.title}

); })}
); } } const getStyles = stylesFactory((theme: GrafanaTheme2) => ({ container: css` height: 100%; `, item: css` display: flex; padding: ${theme.spacing(1)}; position: relative; margin-bottom: 4px; margin-right: ${theme.spacing(1)}; border-bottom: 2px solid ${theme.colors.border.weak}; background: ${theme.colors.background.primary}; flex-direction: column; flex-shrink: 0; `, itemWide: css` flex-direction: row; `, body: css` display: flex; flex-direction: column; `, socialImage: css` display: flex; align-items: center; margin-bottom: ${theme.spacing(1)}; > img { width: 100%; border-radius: ${theme.shape.borderRadius(2)} ${theme.shape.borderRadius(2)} 0 0; } `, socialImageWide: css` margin-right: ${theme.spacing(2)}; margin-bottom: 0; > img { width: 250px; border-radius: ${theme.shape.borderRadius()}; } `, link: css` color: ${theme.colors.text.link}; display: inline-block; &:hover { color: ${theme.colors.text.link}; text-decoration: underline; } `, title: css` font-size: 16px; margin-bottom: ${theme.spacing(0.5)}; `, content: css` p { margin-bottom: 4px; color: ${theme.colors.text}; } `, date: css` margin-bottom: ${theme.spacing(0.5)}; font-weight: 500; border-radius: 0 0 0 3px; color: ${theme.colors.text.secondary}; `, }));