Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make highlight visibility state work in a multi-guest world #3830

Merged
merged 4 commits into from
Oct 19, 2021

Conversation

robertknight
Copy link
Member

@robertknight robertknight commented Oct 14, 2021

Make the client's highlight visibility state work in a sane way in
scenarios where there are multiple guests, or the main guest frame is not the
host frame. Previously there were several problems in these scenarios:

  • It was unclear which component of the application "owned" the
    highlight visibility state. The state in the sidebar could be changed
    as a result of the highlightsVisibleChanged event emitted by a
    Guest, as well as the user toggling highlight controls in the
    sidebar. This confusion created scenarios where the state could get out of sync with the sidebar in some guests.

  • In the sidebar's open and close methods it directly set the
    highlight visibility in the main guest if the showHighlights
    setting was set to whenSidebarOpen. This meant that it didn't work
    for guests that were not in the main frame.

  • Guest frames could be configured with different showHighlights
    settings. In this case it was unclear what should happen.

This commit resolves this by making the Sidebar class in the host
frame the central owner of this state. It handles configuring the
initial state based on the showHighlights configuration setting, and
reflecting this state to the sidebar application which in turn reflects
it to guest frames. The initial visibility of highlights in a guest
frame is synchronized with this state when the guest frame connects to
the sidebar. This state is updated by the Sidebar class when:

  • The user toggles the highlight visibility control in the sidebar
  • A new highlight or annotation is created in a guest frame
  • The sidebar opens and closes, if the showHighlights configuration
    was set to whenSidebarOpen

Additionally the inconsistency of setHighlightsVisible vs
setVisibleHighlights in identifier and event names has been resolved by
using setHighlightsVisible/highlightsVisible everywhere.

Part of #3798


Testing:

  1. Go to http://localhost:3000/document/parent-frame and make annotations in the parent and child frame.
  2. Toggle highlight visibility in the sidebar. The visibility should be updated in both frames
  3. Add the following snippet to dev-server/documents/html/parent-frame.mustache and verify that visibility of highlights correctly changes when opening and closing the sidebar:
<script type="application/json" class="js-hypothesis-config">
  { "showHighlights": "whenSidebarOpen" }
</script>
  1. Disable highlights and then make a highlight or annotation in either the child or parent frames. This should cause highlights to become visible and the toolbar controls should update to reflect this.

A related scenario that doesn't work yet is that making a highlight or annotation in the child frame when highlights are hidden does not show highlights. (Edit: This is now resolved. See second commit.)

@codecov
Copy link

codecov bot commented Oct 14, 2021

Codecov Report

Merging #3830 (e63c074) into master (0b757c6) will increase coverage by 0.02%.
The diff coverage is 100.00%.

❗ Current head e63c074 differs from pull request most recent head 7f2c0b8. Consider uploading reports for the commit 7f2c0b8 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3830      +/-   ##
==========================================
+ Coverage   98.96%   98.98%   +0.02%     
==========================================
  Files         211      211              
  Lines        7903     7906       +3     
  Branches     1867     1868       +1     
==========================================
+ Hits         7821     7826       +5     
+ Misses         82       80       -2     
Impacted Files Coverage Δ
src/annotator/config/index.js 100.00% <ø> (ø)
src/sidebar/store/modules/viewer.js 100.00% <ø> (ø)
src/annotator/guest.js 99.15% <100.00%> (+0.41%) ⬆️
src/annotator/sidebar.js 98.23% <100.00%> (+0.50%) ⬆️
src/sidebar/services/frame-sync.js 100.00% <100.00%> (ø)
src/shared/bridge.js 100.00% <0.00%> (ø)
src/annotator/features.js 100.00% <0.00%> (ø)
src/annotator/annotation-sync.js 100.00% <0.00%> (ø)
src/sidebar/components/TopBar.js 100.00% <0.00%> (ø)
src/sidebar/services/features.js 100.00% <0.00%> (ø)
... and 6 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0b757c6...7f2c0b8. Read the comment docs.

// Ensure that the highlight for the newly-created annotation is visible.
// Currently we only support a single, shared visibility state for all highlights
// in all frames, so this will make all existing highlights visible too.
this._hostRPC.call('showHighlights');
Copy link
Member Author

@robertknight robertknight Oct 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To recap the sequence of events here when creating a new highlight while the "Show highlights" control is disabled:

  1. The guest will create a new annotation and send it to the sidebar via a beforeCreateAnnotation request
  2. The sidebar sends a showHighlights request to the host frame
  3. The host frame updates the "Show highlights" control and then sends a setHighlightsVisible request back to the sidebar app
  4. The sidebar app will relay the setHighlightsVisible request to all connected guests, so that the highlight visibility is the same in all of them
  5. The guests handle the setHighlightsVisible request and show or hide highlights in their respective frames

This might seem rather roundabout, but steps 3-5 are the same as when toggling the "Show highlights" control manually, so we avoid having multiple code paths to handle keeping the state in sync across all frames.

In future if we have a direct guest <-> host channel for each guest, we might be able to eliminate the relaying of the setHighlightsVisible message through the sidebar app. CC @esanzgar.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have been thinking about guest-host channel too, while working on #3838 and realising about the relaying of certain messages host -> sidebar -> guest.

I would support the creation of guest-host channel.

@@ -239,6 +244,9 @@ export default class Sidebar {
sidebarTrigger(document.body, () => this.open());
features.init(this._sidebarRPC);

this._sidebarRPC.on('showHighlights', () =>
Copy link
Contributor

@esanzgar esanzgar Oct 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

showHighlights is a new event type that is sent from the sidebar to the host frame.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. These lines reported errors as expected after a rebase and I've updated the types.

setHighlightsVisible(visible) {
this.toolbar.highlightsVisible = visible;

// Notify sidebar app of change which will in turn reflect state to guest frames.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Notify sidebar app of change which will in turn reflect state to guest frames.
// Notify sidebar frame of change which will in turn reflect state to guest frames.


const call = fakeBridge.call
.getCalls()
.find(args => args[0] === 'setHighlightsVisible' && args[1] === true);
Copy link
Contributor

@esanzgar esanzgar Oct 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fakeBridge.call is not invoked at all, with either true or false argument:

Suggested change
.find(args => args[0] === 'setHighlightsVisible' && args[1] === true);
.find(args => args[0] === 'setHighlightsVisible');

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fakeBridge.call is not invoked at all, with either true or false argument:

It is called with the sidebarOpened message. I discovered there is a neverCalledWith assertion which can simplify this check though.

@@ -67,6 +67,9 @@ export class FrameSyncService {
// Set of tags of annotations that are currently loaded into the frame
const inFrame = new Set();

/** Whether highlights are visible in guest frames. */
this._highlightsVisible = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A single state for all the guest frames.

Comment on lines -11 to -22
describe('#setShowHighlights', () => {
it('sets a flag indicating that highlights are visible', () => {
store.setShowHighlights(true);
assert.isTrue(store.getState().viewer.visibleHighlights);
});

it('sets a flag indicating that highlights are not visible', () => {
store.setShowHighlights(false);
assert.isFalse(store.getState().viewer.visibleHighlights);
});
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a little confuse by the previous uses of the store. Was this old code or has the reliance of the store been disable in the PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was old code that was no longer used.

Copy link
Contributor

@esanzgar esanzgar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tested the PR and it works as explained.

@robertknight robertknight force-pushed the redo-highlight-visibility branch from 94fb864 to 47e35ef Compare October 19, 2021 13:03
There was an action to set this state but it was never read anywhere.
Make the client's highlight visibility state work in a sane way in
scenarios where there are multiple guests, or the main guest frame is not the
host frame. Previously there were several problems in these scenarios:

 - It was unclear which component of the application "owned" the
   highlight visibility state. The state in the sidebar could be changed
   as a result of the `highlightsVisibleChanged` event emitted by a
   Guest, as well as the user toggling highlight controls in the
   sidebar.

 - In the sidebar's `open` and `close` methods it directly set the
   highlight visibility in the main guest if the `showHighlights`
   setting was set to `whenSidebarOpen`. This meant that it didn't work
   for guests that were not in the main frame.

 - Guest frames could be configured with different `showHighlights`
   settings. In this case it was unclear what should happen.

This commit resolves this by making the `Sidebar` class in the host
frame the central owner of this state. It handles configuring the
initial state based on the `showHighlights` configuration setting, and
reflecting this state to the sidebar application which in turn reflects
it to guest frames. The initial visibility of highlights in a guest
frame is synchronized with this state when the guest frame connects to
the sidebar. This state is updated by the `Sidebar` class when:

 - The user toggles the highlight visibility control in the sidebar
 - A new highlight or annotation is created in a guest frame
 - The sidebar opens and closes, if the `showHighlights` configuration
   was set to `whenSidebarOpen`

Additionally the inconsistency of `setHighlightsVisible` vs
`setVisibleHighlights` in identifier and event names has been resolved by
using `setHighlightsVisible`/`highlightsVisible` everywhere.

Part of #3798
Change the logic for making highlights visible when creating a new
annotation, so that it works if the annotation was created in a non-host
frame.

This is done by moving the logic from the `beforeAnnotationCreated`
handler in the Sidebar class, which was only called for annotations in
the host frame, to the `beforeCreateAnnotation` handler in
FrameSyncService, which is called for all frames. The sidebar app will
then send a request to show highlights to the host frame, which will
update the sidebar's controls and then relay the request to guest
frames.
@robertknight robertknight force-pushed the redo-highlight-visibility branch from e63c074 to 7f2c0b8 Compare October 19, 2021 14:25
@robertknight robertknight merged commit 5707862 into master Oct 19, 2021
@robertknight robertknight deleted the redo-highlight-visibility branch October 19, 2021 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants