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

[PM-3530] browser extension view cache #10437

Merged
merged 49 commits into from
Aug 26, 2024
Merged

[PM-3530] browser extension view cache #10437

merged 49 commits into from
Aug 26, 2024

Conversation

willmartian
Copy link
Contributor

@willmartian willmartian commented Aug 7, 2024

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-3530

📔 Objective

Split from #9556, see extra comments there.

Introduces a way to store temporary component state, for the purposes of persisting views between extension popup open and close.

  • ViewCacheService
const mySignal = this.viewCacheService.signal({
   key: "popup-search-text"
   initialValue: "foo"
});
this.loginDetailsForm = this.viewCacheService.formGroup({
      key: "vault-login-details-form",
      control: this.formBuilder.group({
        username: [""],
        password: [""],
        totp: [""],
      })
});

In both instances, the wrapped object's value is set with the previously cached value. Updates to the wrapped value are saved to the cache.

State is stored in the PopupViewCacheBackgroundService, which clears cached state on unlock, lock, tab change, & 2min after closing the popup window. State is also cleared when navigating between routes in the extension popup

Other approaches

I initially wanted to utilize a simpler, non-reactive API like the following, which accepts a cleanup function that runs when the popup's pagehide event occurs:

class Foo {
  name: string = getFromCache("foo-name", () => {
    return this.name; 
  });
}

However, pagehide (and visibilitychange/unload) do not consistently fire in Chrome extension windows. :(

Random thoughts

  • Why not a TS decorator? We might want to start watching the signal/form after performing other logic.
  • Do we need the deserializer field? See [PM-3530][PM-8588] persist extension route history #9556 (comment) but I could go either way.
  • We could unify the two methods into a single one: ViewCacheService.watch(formOrSignal, options)
    • I just didn't think of this during my first pass. Dunno if it is better or less clear. I defer to reviewer.

📸 Screenshots

Screen.Recording.2024-07-02.at.9.58.27.AM.mov

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

@willmartian willmartian changed the title Revert "remove view cache references to split PR" [PM-3530] browser extension view cache Aug 13, 2024
Copy link

codecov bot commented Aug 13, 2024

Codecov Report

Attention: Patch coverage is 61.42857% with 27 lines in your changes missing coverage. Please review.

Project coverage is 32.91%. Comparing base (19ef6ba) to head (ff6e42a).
Report is 64 commits behind head on main.

✅ All tests successful. No failed tests found.

Files Patch % Lines
...rm/services/popup-view-cache-background.service.ts 38.46% 8 Missing ⚠️
...tform/popup/view-cache/popup-view-cache.service.ts 86.36% 4 Missing and 2 partials ⚠️
apps/browser/src/popup/app.component.ts 0.00% 4 Missing ⚠️
...r/src/platform/services/noop-view-cache.service.ts 0.00% 4 Missing ⚠️
apps/browser/src/popup/services/services.module.ts 0.00% 2 Missing ⚠️
libs/angular/src/services/jslib-services.module.ts 0.00% 2 Missing ⚠️
...ar/src/platform/abstractions/view-cache.service.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #10437      +/-   ##
==========================================
+ Coverage   32.79%   32.91%   +0.11%     
==========================================
  Files        2670     2669       -1     
  Lines       81769    81824      +55     
  Branches    15414    15441      +27     
==========================================
+ Hits        26816    26929     +113     
+ Misses      52877    52805      -72     
- Partials     2076     2090      +14     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@willmartian willmartian added the hold do not merge, do not approve yet label Aug 20, 2024
@willmartian willmartian removed the hold do not merge, do not approve yet label Aug 20, 2024
@willmartian willmartian marked this pull request as ready for review August 20, 2024 21:15
@willmartian willmartian requested a review from a team as a code owner August 20, 2024 21:15
coroiu
coroiu previously approved these changes Aug 21, 2024
Copy link
Contributor

@coroiu coroiu left a comment

Choose a reason for hiding this comment

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

I wasn't around to review #9556 but from what I can see this looks good. It's gonna take some time to get used to signals, especially when used in a complex scenario like this

Comment on lines 61 to 68
/**
* - Initialize a form from a cached value
* - Save form value to cache when it changes
* - The form is marked dirty if the restored value is not `undefined`.
**/
formGroup<TFormGroup extends FormGroup>(options: FormCacheOptions<TFormGroup>): TFormGroup {
return options.control;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

question: This isn't actually true right? This is the behavior that's implemented by the popup version of this service, but not by this general service?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question @coroiu

Yes, the default implementation is noop. However, since we inject via the default implementation, I thought it would be more helpful to document the non-noop behavior so it will show up in IDE-hints.

Is this a bad idea, or is there a more clear way that you would suggest representing this?

Maybe just add Non browser extension implementations are noop. ?

Copy link
Member

Choose a reason for hiding this comment

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

I think adding your comment about non browser extensions is definitely a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added here 8ac50e4

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I reacted because this was documenting an implementation which was obviously not providing that behavior. I'm not sure if we normally use classes as interfaces AND default implementations like this. I feel like I mostly see the interface defined and documented separately. That way this noop implementation could clearly document the fact that it is not living up to the interface documentation, and why that doesn't matter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that is a good point! Added 86b05f8 and 556683a.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also provided the default implementation to other clients here: 1f22860

Copy link
Member

@justindbaur justindbaur left a comment

Choose a reason for hiding this comment

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

I think the only thing I need is a comment on the formGroup method mentioning its nuance.

@willmartian
Copy link
Contributor Author

Was discussing the feature flag strategy for this with @trmartin4 and we thought it might be nice to include the feature flag in the service logic instead of expecting consumers to flag it manually. Lmk what y'all think @justindbaur @coroiu.

811761b

Comment on lines 120 to 125
private updateState(key: string, value: string) {
this.messageSender.send(SAVE_VIEW_CACHE_COMMAND, {
key,
value,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

question: maybe we should disable this with the feature flag as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems reasonable to me. Added ff6e42a

@willmartian willmartian requested a review from coroiu August 22, 2024 14:33
@willmartian willmartian merged commit 6918606 into main Aug 26, 2024
66 checks passed
@willmartian willmartian deleted the ps/PM-3530/view-cache branch August 26, 2024 20:09
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.

3 participants