Skip to content

Commit ccc1485

Browse files
authored
Fix newsfeed unread notifications always on when reloading Kibana (#100357)
* fix the implementation * add unit tests * add API unit tests * fix public interface * address review comments * name convertItem to localizeItem * use fetch instead of core.http and add tests
1 parent 8cb3dbc commit ccc1485

File tree

15 files changed

+936
-866
lines changed

15 files changed

+936
-866
lines changed

src/plugins/newsfeed/common/constants.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@
77
*/
88

99
export const NEWSFEED_FALLBACK_LANGUAGE = 'en';
10-
export const NEWSFEED_FALLBACK_FETCH_INTERVAL = 86400000; // 1 day
11-
export const NEWSFEED_FALLBACK_MAIN_INTERVAL = 120000; // 2 minutes
12-
export const NEWSFEED_LAST_FETCH_STORAGE_KEY = 'newsfeed.lastfetchtime';
13-
export const NEWSFEED_HASH_SET_STORAGE_KEY = 'newsfeed.hashes';
14-
1510
export const NEWSFEED_DEFAULT_SERVICE_BASE_URL = 'https://feeds.elastic.co';
1611
export const NEWSFEED_DEV_SERVICE_BASE_URL = 'https://feeds-staging.elastic.co';
1712
export const NEWSFEED_DEFAULT_SERVICE_PATH = '/kibana/v{VERSION}.json';

src/plugins/newsfeed/public/components/newsfeed_header_nav_button.tsx

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,71 +6,69 @@
66
* Side Public License, v 1.
77
*/
88

9-
import React, { useState, Fragment, useEffect } from 'react';
10-
import * as Rx from 'rxjs';
9+
import React, { useState, useEffect, useMemo, useCallback } from 'react';
1110
import { EuiHeaderSectionItemButton, EuiIcon } from '@elastic/eui';
1211
import { i18n } from '@kbn/i18n';
12+
import type { NewsfeedApi } from '../lib/api';
1313
import { NewsfeedFlyout } from './flyout_list';
1414
import { FetchResult } from '../types';
1515

1616
export interface INewsfeedContext {
1717
setFlyoutVisible: React.Dispatch<React.SetStateAction<boolean>>;
1818
newsFetchResult: FetchResult | void | null;
1919
}
20-
export const NewsfeedContext = React.createContext({} as INewsfeedContext);
2120

22-
export type NewsfeedApiFetchResult = Rx.Observable<void | FetchResult | null>;
21+
export const NewsfeedContext = React.createContext({} as INewsfeedContext);
2322

2423
export interface Props {
25-
apiFetchResult: NewsfeedApiFetchResult;
24+
newsfeedApi: NewsfeedApi;
2625
}
2726

28-
export const NewsfeedNavButton = ({ apiFetchResult }: Props) => {
29-
const [showBadge, setShowBadge] = useState<boolean>(false);
27+
export const NewsfeedNavButton = ({ newsfeedApi }: Props) => {
3028
const [flyoutVisible, setFlyoutVisible] = useState<boolean>(false);
3129
const [newsFetchResult, setNewsFetchResult] = useState<FetchResult | null | void>(null);
30+
const hasNew = useMemo(() => {
31+
return newsFetchResult ? newsFetchResult.hasNew : false;
32+
}, [newsFetchResult]);
3233

3334
useEffect(() => {
34-
function handleStatusChange(fetchResult: FetchResult | void | null) {
35-
if (fetchResult) {
36-
setShowBadge(fetchResult.hasNew);
37-
}
38-
setNewsFetchResult(fetchResult);
39-
}
40-
41-
const subscription = apiFetchResult.subscribe((res) => handleStatusChange(res));
35+
const subscription = newsfeedApi.fetchResults$.subscribe((results) => {
36+
setNewsFetchResult(results);
37+
});
4238
return () => subscription.unsubscribe();
43-
}, [apiFetchResult]);
39+
}, [newsfeedApi]);
4440

45-
function showFlyout() {
46-
setShowBadge(false);
41+
const showFlyout = useCallback(() => {
42+
if (newsFetchResult) {
43+
newsfeedApi.markAsRead(newsFetchResult.feedItems.map((item) => item.hash));
44+
}
4745
setFlyoutVisible(!flyoutVisible);
48-
}
46+
}, [newsfeedApi, newsFetchResult, flyoutVisible]);
4947

5048
return (
5149
<NewsfeedContext.Provider value={{ setFlyoutVisible, newsFetchResult }}>
52-
<Fragment>
50+
<>
5351
<EuiHeaderSectionItemButton
5452
data-test-subj="newsfeed"
5553
aria-controls="keyPadMenu"
5654
aria-expanded={flyoutVisible}
5755
aria-haspopup="true"
5856
aria-label={
59-
showBadge
57+
hasNew
6058
? i18n.translate('newsfeed.headerButton.unreadAriaLabel', {
6159
defaultMessage: 'Newsfeed menu - unread items available',
6260
})
6361
: i18n.translate('newsfeed.headerButton.readAriaLabel', {
6462
defaultMessage: 'Newsfeed menu - all items read',
6563
})
6664
}
67-
notification={showBadge ? true : null}
65+
notification={hasNew ? true : null}
6866
onClick={showFlyout}
6967
>
7068
<EuiIcon type="cheer" size="m" />
7169
</EuiHeaderSectionItemButton>
7270
{flyoutVisible ? <NewsfeedFlyout /> : null}
73-
</Fragment>
71+
</>
7472
</NewsfeedContext.Provider>
7573
);
7674
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import { storageMock } from './storage.mock';
10+
import { driverMock } from './driver.mock';
11+
12+
export const storageInstanceMock = storageMock.create();
13+
jest.doMock('./storage', () => ({
14+
NewsfeedStorage: jest.fn().mockImplementation(() => storageInstanceMock),
15+
}));
16+
17+
export const driverInstanceMock = driverMock.create();
18+
jest.doMock('./driver', () => ({
19+
NewsfeedApiDriver: jest.fn().mockImplementation(() => driverInstanceMock),
20+
}));

0 commit comments

Comments
 (0)