Commit 725de03c authored by Alejandro Celaya's avatar Alejandro Celaya Committed by Alejandro Celaya

Delete PendingUpdatesButton and remove references to pending_updates_notification ff

parent 1b9e8c7a
...@@ -11,7 +11,6 @@ import { useSidebarStore } from '../store'; ...@@ -11,7 +11,6 @@ import { useSidebarStore } from '../store';
import NotebookFilters from './NotebookFilters'; import NotebookFilters from './NotebookFilters';
import NotebookResultCount from './NotebookResultCount'; import NotebookResultCount from './NotebookResultCount';
import PaginatedThreadList from './PaginatedThreadList'; import PaginatedThreadList from './PaginatedThreadList';
import PendingUpdatesButton from './PendingUpdatesButton';
import PendingUpdatesNotification from './PendingUpdatesNotification'; import PendingUpdatesNotification from './PendingUpdatesNotification';
import { useRootThread } from './hooks/use-root-thread'; import { useRootThread } from './hooks/use-root-thread';
...@@ -34,9 +33,6 @@ function NotebookView({ loadAnnotationsService, streamer }: NotebookViewProps) { ...@@ -34,9 +33,6 @@ function NotebookView({ loadAnnotationsService, streamer }: NotebookViewProps) {
const hasAppliedFilter = store.hasAppliedFilter(); const hasAppliedFilter = store.hasAppliedFilter();
const isLoading = store.isLoading(); const isLoading = store.isLoading();
const resultCount = store.annotationResultCount(); const resultCount = store.annotationResultCount();
const pendingUpdatesNotification = store.isFeatureEnabled(
'pending_updates_notification',
);
const { rootThread } = useRootThread(); const { rootThread } = useRootThread();
...@@ -143,14 +139,13 @@ function NotebookView({ loadAnnotationsService, streamer }: NotebookViewProps) { ...@@ -143,14 +139,13 @@ function NotebookView({ loadAnnotationsService, streamer }: NotebookViewProps) {
'right-[4rem]', 'right-[4rem]',
)} )}
> >
{pendingUpdatesNotification && <PendingUpdatesNotification />} <PendingUpdatesNotification />
</div> </div>
</div> </div>
<div className="justify-self-start"> <div className="justify-self-start">
<NotebookFilters /> <NotebookFilters />
</div> </div>
<div className="flex items-center lg:justify-self-end text-md font-medium"> <div className="flex items-center lg:justify-self-end text-md font-medium">
{!pendingUpdatesNotification && <PendingUpdatesButton />}
<NotebookResultCount <NotebookResultCount
forcedVisibleCount={forcedVisibleCount} forcedVisibleCount={forcedVisibleCount}
isFiltered={hasAppliedFilter} isFiltered={hasAppliedFilter}
......
import { IconButton, RefreshIcon } from '@hypothesis/frontend-shared';
import { useCallback, useEffect } from 'preact/hooks';
import { useShortcut } from '../../shared/shortcut';
import { withServices } from '../service-context';
import type { AnalyticsService } from '../services/analytics';
import type { StreamerService } from '../services/streamer';
import type { ToastMessengerService } from '../services/toast-messenger';
import { useSidebarStore } from '../store';
export type PendingUpdatesButtonProps = {
// Injected
analytics: AnalyticsService;
streamer: StreamerService;
toastMessenger: ToastMessengerService;
};
function PendingUpdatesButton({
analytics,
streamer,
toastMessenger,
}: PendingUpdatesButtonProps) {
const store = useSidebarStore();
const pendingUpdateCount = store.pendingUpdateCount();
const hasPendingUpdates = store.hasPendingUpdates();
const applyPendingUpdates = useCallback(() => {
streamer.applyPendingUpdates();
analytics.trackEvent('client.realtime.apply_updates');
}, [analytics, streamer]);
useShortcut('l', () => hasPendingUpdates && applyPendingUpdates());
useEffect(() => {
if (hasPendingUpdates) {
toastMessenger.notice('New annotations are available.', {
visuallyHidden: true,
});
toastMessenger.notice('Press "l" to load new annotations.', {
visuallyHidden: true,
delayed: true,
});
}
}, [hasPendingUpdates, toastMessenger]);
if (!hasPendingUpdates) {
return null;
}
return (
<IconButton
icon={RefreshIcon}
onClick={applyPendingUpdates}
size="xs"
variant="primary"
title={`Show ${pendingUpdateCount} new/updated ${
pendingUpdateCount === 1 ? 'annotation' : 'annotations'
}`}
/>
);
}
export default withServices(PendingUpdatesButton, [
'analytics',
'streamer',
'toastMessenger',
]);
...@@ -39,9 +39,6 @@ function SidebarView({ ...@@ -39,9 +39,6 @@ function SidebarView({
const focusedGroupId = store.focusedGroupId(); const focusedGroupId = store.focusedGroupId();
const isLoading = store.isLoading(); const isLoading = store.isLoading();
const isLoggedIn = store.isLoggedIn(); const isLoggedIn = store.isLoggedIn();
const pendingUpdatesNotification = store.isFeatureEnabled(
'pending_updates_notification',
);
const linkedAnnotationId = store.directLinkedAnnotationId(); const linkedAnnotationId = store.directLinkedAnnotationId();
const linkedAnnotation = linkedAnnotationId const linkedAnnotation = linkedAnnotationId
...@@ -133,19 +130,17 @@ function SidebarView({ ...@@ -133,19 +130,17 @@ function SidebarView({
return ( return (
<div className="relative"> <div className="relative">
<h2 className="sr-only">Annotations</h2> <h2 className="sr-only">Annotations</h2>
{pendingUpdatesNotification && ( <div
<div className={classnames(
className={classnames( 'fixed z-1',
'fixed z-1', // Setting 9px to the right instead of some standard tailwind size,
// Setting 9px to the right instead of some standard tailwind size, // so that it matches the padding of the sidebar's container.
// so that it matches the padding of the sidebar's container. // DEFAULT `.container` padding is defined in tailwind.conf.js
// DEFAULT `.container` padding is defined in tailwind.conf.js 'right-[9px] top-12',
'right-[9px] top-12', )}
)} >
> <PendingUpdatesNotification />
<PendingUpdatesNotification /> </div>
</div>
)}
{showFilterControls && <FilterControls withCardContainer />} {showFilterControls && <FilterControls withCardContainer />}
<LoginPromptPanel onLogin={onLogin} onSignUp={onSignUp} /> <LoginPromptPanel onLogin={onLogin} onSignUp={onSignUp} />
{hasDirectLinkedAnnotationError && ( {hasDirectLinkedAnnotationError && (
......
...@@ -8,7 +8,6 @@ import { withServices } from '../service-context'; ...@@ -8,7 +8,6 @@ import { withServices } from '../service-context';
import type { FrameSyncService } from '../services/frame-sync'; import type { FrameSyncService } from '../services/frame-sync';
import { useSidebarStore } from '../store'; import { useSidebarStore } from '../store';
import GroupList from './GroupList'; import GroupList from './GroupList';
import PendingUpdatesButton from './PendingUpdatesButton';
import SortMenu from './SortMenu'; import SortMenu from './SortMenu';
import TopBarToggleButton from './TopBarToggleButton'; import TopBarToggleButton from './TopBarToggleButton';
import UserMenu from './UserMenu'; import UserMenu from './UserMenu';
...@@ -50,9 +49,6 @@ function TopBar({ ...@@ -50,9 +49,6 @@ function TopBar({
const store = useSidebarStore(); const store = useSidebarStore();
const isLoggedIn = store.isLoggedIn(); const isLoggedIn = store.isLoggedIn();
const hasFetchedProfile = store.hasFetchedProfile(); const hasFetchedProfile = store.hasFetchedProfile();
const pendingUpdatesNotification = store.isFeatureEnabled(
'pending_updates_notification',
);
const toggleSharePanel = () => { const toggleSharePanel = () => {
store.toggleSidebarPanel('shareGroupAnnotations'); store.toggleSidebarPanel('shareGroupAnnotations');
...@@ -96,7 +92,6 @@ function TopBar({ ...@@ -96,7 +92,6 @@ function TopBar({
<div className="grow flex items-center justify-end"> <div className="grow flex items-center justify-end">
{isSidebar && ( {isSidebar && (
<> <>
{!pendingUpdatesNotification && <PendingUpdatesButton />}
<SearchIconButton /> <SearchIconButton />
<SortMenu /> <SortMenu />
<TopBarToggleButton <TopBarToggleButton
......
...@@ -36,7 +36,6 @@ describe('NotebookView', () => { ...@@ -36,7 +36,6 @@ describe('NotebookView', () => {
annotationResultCount: sinon.stub().returns(0), annotationResultCount: sinon.stub().returns(0),
setSortKey: sinon.stub(), setSortKey: sinon.stub(),
hasFetchedProfile: sinon.stub().returns(true), hasFetchedProfile: sinon.stub().returns(true),
isFeatureEnabled: sinon.stub().returns(false),
}; };
fakeStreamer = { fakeStreamer = {
...@@ -152,22 +151,6 @@ describe('NotebookView', () => { ...@@ -152,22 +151,6 @@ describe('NotebookView', () => {
assert.isTrue(wrapper.find('NotebookFilters').exists()); assert.isTrue(wrapper.find('NotebookFilters').exists());
}); });
[true, false].forEach(pendingUpdatesNotificationEnabled => {
it('shows expected pending updates component', () => {
fakeStore.isFeatureEnabled.returns(pendingUpdatesNotificationEnabled);
const wrapper = createComponent();
assert.equal(
wrapper.exists('PendingUpdatesNotification'),
pendingUpdatesNotificationEnabled,
);
assert.equal(
wrapper.exists('PendingUpdatesButton'),
!pendingUpdatesNotificationEnabled,
);
});
});
describe('pagination', () => { describe('pagination', () => {
it('passes the current pagination page to `PaginatedThreadList`', () => { it('passes the current pagination page to `PaginatedThreadList`', () => {
const wrapper = createComponent(); const wrapper = createComponent();
......
import { mount } from 'enzyme';
import PendingUpdatesButton, { $imports } from '../PendingUpdatesButton';
describe('PendingUpdatesButton', () => {
let fakeToastMessenger;
let fakeStore;
let fakeStreamer;
let fakeAnalytics;
beforeEach(() => {
fakeToastMessenger = {
notice: sinon.stub(),
};
fakeStore = {
pendingUpdateCount: sinon.stub().returns(0),
hasPendingUpdates: sinon.stub().returns(true),
};
fakeStreamer = {
applyPendingUpdates: sinon.stub(),
};
fakeAnalytics = {
trackEvent: sinon.stub(),
};
$imports.$mock({
'../store': { useSidebarStore: () => fakeStore },
});
});
afterEach(() => {
$imports.$restore();
});
const createButton = count => {
fakeStore.pendingUpdateCount.returns(count);
fakeStore.hasPendingUpdates.returns(count > 0);
return mount(
<PendingUpdatesButton
streamer={fakeStreamer}
toastMessenger={fakeToastMessenger}
analytics={fakeAnalytics}
/>,
);
};
it('shows an empty wrapper when there are no pending updates', () => {
const wrapper = createButton(0);
assert.isFalse(wrapper.find('IconButton').exists());
assert.notCalled(fakeToastMessenger.notice);
});
[1, 10, 50].forEach(pendingUpdateCount => {
it('shows the pending update count', () => {
const wrapper = createButton(pendingUpdateCount);
assert.isTrue(wrapper.find('IconButton').exists());
assert.calledWith(
fakeToastMessenger.notice,
'New annotations are available.',
{
visuallyHidden: true,
},
);
assert.calledWith(
fakeToastMessenger.notice,
'Press "l" to load new annotations.',
{
visuallyHidden: true,
delayed: true,
},
);
});
});
it('shows the pending update count', () => {
const wrapper = createButton(1);
assert.isTrue(wrapper.find('IconButton').exists());
});
it('applies updates when clicked', () => {
const wrapper = createButton(1);
const applyBtn = wrapper.find('IconButton');
applyBtn.props().onClick();
assert.called(fakeStreamer.applyPendingUpdates);
assert.calledWith(
fakeAnalytics.trackEvent,
'client.realtime.apply_updates',
);
});
it('applies updates when keyboard shortcut is pressed', () => {
createButton(1);
document.body.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'l',
bubbles: true,
}),
);
assert.called(fakeStreamer.applyPendingUpdates);
assert.calledWith(
fakeAnalytics.trackEvent,
'client.realtime.apply_updates',
);
});
});
...@@ -59,7 +59,6 @@ describe('SidebarView', () => { ...@@ -59,7 +59,6 @@ describe('SidebarView', () => {
profile: sinon.stub().returns({ userid: null }), profile: sinon.stub().returns({ userid: null }),
searchUris: sinon.stub().returns([]), searchUris: sinon.stub().returns([]),
toggleFocusMode: sinon.stub(), toggleFocusMode: sinon.stub(),
isFeatureEnabled: sinon.stub().returns(false),
}; };
fakeTabsUtil = { fakeTabsUtil = {
...@@ -277,19 +276,6 @@ describe('SidebarView', () => { ...@@ -277,19 +276,6 @@ describe('SidebarView', () => {
}); });
}); });
context('when pending_updates_notification is enabled', () => {
[true, false].forEach(pendingUpdatesNotificationEnabled => {
it('shows PendingUpdatesNotification', () => {
fakeStore.isFeatureEnabled.returns(pendingUpdatesNotificationEnabled);
const wrapper = createComponent();
assert.equal(
wrapper.exists('PendingUpdatesNotification'),
pendingUpdatesNotificationEnabled,
);
});
});
});
it( it(
'should pass a11y checks', 'should pass a11y checks',
checkAccessibility({ checkAccessibility({
......
...@@ -19,7 +19,6 @@ describe('TopBar', () => { ...@@ -19,7 +19,6 @@ describe('TopBar', () => {
isLoggedIn: sinon.stub().returns(false), isLoggedIn: sinon.stub().returns(false),
isSidebarPanelOpen: sinon.stub().returns(false), isSidebarPanelOpen: sinon.stub().returns(false),
toggleSidebarPanel: sinon.stub(), toggleSidebarPanel: sinon.stub(),
isFeatureEnabled: sinon.stub().returns(true),
}; };
fakeFrameSync = { fakeFrameSync = {
...@@ -194,17 +193,6 @@ describe('TopBar', () => { ...@@ -194,17 +193,6 @@ describe('TopBar', () => {
}); });
}); });
[true, false].forEach(pendingUpdatesNotificationEnabled => {
it('renders PendingUpdatesButton when pending_updates_notification feature is not enabled', () => {
fakeStore.isFeatureEnabled.returns(pendingUpdatesNotificationEnabled);
const wrapper = createTopBar();
assert.equal(
wrapper.exists('PendingUpdatesButton'),
!pendingUpdatesNotificationEnabled,
);
});
});
it( it(
'should pass a11y checks', 'should pass a11y checks',
checkAccessibility([ checkAccessibility([
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment