-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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-9111] Extension: persist add/edit form #12236
base: main
Are you sure you want to change the base?
Conversation
…ccur on non-cached values
…t/pm-9111/persist-add-edit
No New Or Fixed Issues Found |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12236 +/- ##
=======================================
Coverage ? 33.58%
=======================================
Files ? 2927
Lines ? 91586
Branches ? 17405
=======================================
Hits ? 30760
Misses ? 58410
Partials ? 2416 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this approach worked out nicely, great work! I just noticed a few issues and had a few small suggestions.
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; | ||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; | ||
|
||
const POPUP_CIPHER_CACHE_KEY = "popup-cipher-cache"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎨 This service is used in the shared cipher form component and will be used in other clients besides Browser. I'm thinking we should make these keys client agnostic.
e.g. const CIPHER_FORM_CACHE_KEY = "cipher-form-cache";
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 cb5d689
|
||
@Injectable() | ||
export class CipherFormCacheService { | ||
private popupViewCacheService: ViewCacheService = inject(ViewCacheService); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎨 Similarly, we should just keep this as viewCacheService
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 cb5d689
libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts
Show resolved
Hide resolved
name: name ? name : (this.initialValues?.name ?? ""), | ||
organizationId: prefillCipher.organizationId, // We do not allow changing ownership of an existing cipher. | ||
folderId: folderId ? folderId : (this.initialValues?.folderId ?? null), | ||
collectionIds: prefillCipher.collectionIds, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
collectionIds
form control is actually the type SelectItemView[]
so this will cause collectionsIds
to have a map of undefined values (due to the missing .id
property that is referenced in the itemDetailsForm.valueChanges
subscription.
We can keep this with an empty []
and let the updateCollectionOptions()
call down below (which you already updated).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated! 7ef3a7b
this.initialValues?.collectionIds ?? | ||
(this.originalCipherView.collectionIds as CollectionId[]), | ||
); | ||
const prefillCollections = prefillCipher.collectionIds?.length |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
itemDetailsForm.setValue()
will actually update the reference to prefillCipher
resulting in the loss of the prefillCipher.collectionIds
value. If we pull out the collectionIds
at the top of this method (with name
and folderId
) that should get us an unchanging copy of the ids.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Catch! This scenario totally slipped by me.
Updated! 7ef3a7b
return cachedCipherView; | ||
} | ||
|
||
return this.updatedCipherView; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.originalCipherView
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is tripping me but I agree with you. I recall switching from originalCipherView
to updatedCipherView
during development but I can't for the life of me remember why.
- I should write better commit messages
- I'm betting it was necessary for one of my earlier solutions that didn't pan out. After testing
originalCipherView
works just fine and makes more sense. We're rolling.
} | ||
|
||
// Create a new shallow reference to force the signal to update | ||
this.cipherCache.set({ ...cipherView } as CipherView); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💭 I'm slightly worried this may not be a "deep" enough / separate clone.
For example the item details and auto fill option child forms update the form values during initialization that ends up making changes to the updatedCipherView
which we're caching here which in turn may be affecting subsequent child forms during initialization.
I did see the item details section having an impact which I described in my other comment about pulling the collectionIds
reference out. However, when I tried stepping through the auto fill options form with multiple URIs everything appeared to be working, which makes me suspicious that we may just be getting "lucky".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did some digging, Signals use Object.is
by default, any new object would fail that equality check update the signal value. Docs.
I think your concern is legitimate and I can't really find an answer to it. If we have a deeply nested object and there is a comparison done against and updated value would it still hold the same reference.
Do you think it's worth creating a deep clone for it? structuredClone is available but the browser support may be too narrow for our liking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, I added a comment about the default equality check but I'll update depending on which you think is a good path forward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💭 We should double check that this behaves as expected in the Web client that uses a NoopViewCacheService
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't seen any preserved values or anything unexpected in the web 🚀
…t/pm-9111/persist-add-edit
…t/pm-9111/persist-add-edit
🎟️ Tracking
PM-9111
Server PR (add feature flag - no logic)
📔 Objective
Use
ViewCacheService
to store the cipher that is being added/edited to repopulate the form when the extension loses focus and regains focus.cipher
in thesignal
as it is updated in the form.CipherFormCacheService
is an in-between consuming components and theViewCacheService
CipherFormComponent
:updatedCipherView
is set to the cached cipher if it is available.getInitialCipherView
allows each of the child components to get the cached cipher to populate their own forms with either the cached cipher or theupdatedCipherView
. This is a change from usingoriginalCipherView
- Clone
is only appended on the initial load of the cloned cipher.PM9111ExtensionPersistAddEditForm
andPersistPopupView
feature flag should be enabled to use.`PopupViewCacheService.formGroup`
PopupViewCacheService
has aformGroup
method that will store/create aformGroup
directly and update the value as the form updates. This didn't work for the cipher form as each child component registers their own individual form as they are initialized inregisterChildForm
. I wasn't able to successfully get this to work with the dynamic & nested form.Along the same lines, trying to store each child form in
PopupViewCacheService.formGroup
would require a cache entry for each individual form group alongside repopulating which strays from how the cipher form has a singular form.Initialization with
PopupViewCacheService.formGroup
didn't supportFormArray
s we definitely could overcome that with some logic change📸 Screenshots
Adding Ciphers
new-login-cached.mov
new-card-cached.mov
new-identity-cached.mov
new-note-cached.mov
Other scenarios
edit-cipher.mov
cloned-cipher.mov
🦮 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