mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
[PM-17663] Fix extension Fill button display on page load (#15359)
* [PM-17663] Convert vault-list-items-container inputs to signals - Cleaned up some grouping logic - Cleaned up strict null checks and removed eslint comment * [PM-17663] Prefer undefined over null * [PM-17663] Fix flashing Fill buttons
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
[title]="((currentURIIsBlocked$ | async) ? 'itemSuggestions' : 'autofillSuggestions') | i18n"
|
[title]="((currentURIIsBlocked$ | async) ? 'itemSuggestions' : 'autofillSuggestions') | i18n"
|
||||||
[showRefresh]="showRefresh"
|
[showRefresh]="showRefresh"
|
||||||
(onRefresh)="refreshCurrentTab()"
|
(onRefresh)="refreshCurrentTab()"
|
||||||
[description]="(showEmptyAutofillTip$ | async) ? ('autofillSuggestionsTip' | i18n) : null"
|
[description]="(showEmptyAutofillTip$ | async) ? ('autofillSuggestionsTip' | i18n) : undefined"
|
||||||
showAutofillButton
|
showAutofillButton
|
||||||
[disableDescriptionMargin]="showEmptyAutofillTip$ | async"
|
[disableDescriptionMargin]="showEmptyAutofillTip$ | async"
|
||||||
[primaryActionAutofill]="clickItemsToAutofillVaultView$ | async"
|
[primaryActionAutofill]="clickItemsToAutofillVaultView$ | async"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { toSignal } from "@angular/core/rxjs-interop";
|
import { toSignal } from "@angular/core/rxjs-interop";
|
||||||
import { combineLatest, map, Observable } from "rxjs";
|
import { combineLatest, map, Observable, startWith } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||||
@@ -41,7 +41,9 @@ export class AutofillVaultListItemsComponent {
|
|||||||
|
|
||||||
/** Flag indicating whether the login item should automatically autofill when clicked */
|
/** Flag indicating whether the login item should automatically autofill when clicked */
|
||||||
protected clickItemsToAutofillVaultView$: Observable<boolean> =
|
protected clickItemsToAutofillVaultView$: Observable<boolean> =
|
||||||
this.vaultSettingsService.clickItemsToAutofillVaultView$;
|
this.vaultSettingsService.clickItemsToAutofillVaultView$.pipe(
|
||||||
|
startWith(true), // Start with true to avoid flashing the fill button on first load
|
||||||
|
);
|
||||||
|
|
||||||
protected groupByType = toSignal(
|
protected groupByType = toSignal(
|
||||||
this.vaultPopupItemsService.hasFilterApplied$.pipe(map((hasFilter) => !hasFilter)),
|
this.vaultPopupItemsService.hasFilterApplied$.pipe(map((hasFilter) => !hasFilter)),
|
||||||
@@ -74,9 +76,7 @@ export class AutofillVaultListItemsComponent {
|
|||||||
private vaultPopupItemsService: VaultPopupItemsService,
|
private vaultPopupItemsService: VaultPopupItemsService,
|
||||||
private vaultPopupAutofillService: VaultPopupAutofillService,
|
private vaultPopupAutofillService: VaultPopupAutofillService,
|
||||||
private vaultSettingsService: VaultSettingsService,
|
private vaultSettingsService: VaultSettingsService,
|
||||||
) {
|
) {}
|
||||||
// TODO: Migrate logic to show Autofill policy toast PM-8144
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the current tab to re-populate the autofill ciphers.
|
* Refreshes the current tab to re-populate the autofill ciphers.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<bit-section
|
<bit-section
|
||||||
*ngIf="cipherGroups$().length > 0 || description"
|
*ngIf="cipherGroups().length > 0 || description()"
|
||||||
[disableMargin]="disableSectionMargin"
|
[disableMargin]="disableSectionMargin()"
|
||||||
>
|
>
|
||||||
<ng-container *ngIf="collapsibleKey">
|
<ng-container *ngIf="collapsibleKey()">
|
||||||
<button
|
<button
|
||||||
class="tw-group/vault-section-header hover:tw-bg-primary-100 tw-rounded-md tw-pl-1 tw-w-full tw-border-x-0 tw-border-t-0 tw-border-b tw-border-solid focus-visible:tw-outline-none focus-visible:tw-ring-inset focus-visible:tw-ring-2 focus-visible:tw-ring-primary-600"
|
class="tw-group/vault-section-header hover:tw-bg-primary-100 tw-rounded-md tw-pl-1 tw-w-full tw-border-x-0 tw-border-t-0 tw-border-b tw-border-solid focus-visible:tw-outline-none focus-visible:tw-ring-inset focus-visible:tw-ring-2 focus-visible:tw-ring-primary-600"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</bit-disclosure>
|
</bit-disclosure>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="!collapsibleKey">
|
<ng-container *ngIf="!collapsibleKey()">
|
||||||
<div class="tw-pl-1">
|
<div class="tw-pl-1">
|
||||||
<ng-container *ngTemplateOutlet="sectionHeader"></ng-container>
|
<ng-container *ngTemplateOutlet="sectionHeader"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,10 +34,10 @@
|
|||||||
<ng-template #sectionHeader>
|
<ng-template #sectionHeader>
|
||||||
<bit-section-header class="tw-p-0.5 -tw-mx-0.5">
|
<bit-section-header class="tw-p-0.5 -tw-mx-0.5">
|
||||||
<h2 bitTypography="h6">
|
<h2 bitTypography="h6">
|
||||||
{{ title }}
|
{{ title() }}
|
||||||
</h2>
|
</h2>
|
||||||
<button
|
<button
|
||||||
*ngIf="showRefresh"
|
*ngIf="showRefresh()"
|
||||||
bitIconButton="bwi-refresh"
|
bitIconButton="bwi-refresh"
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -48,13 +48,13 @@
|
|||||||
<span
|
<span
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
'group-hover/vault-section-header:tw-hidden group-focus-visible/vault-section-header:tw-hidden':
|
'group-hover/vault-section-header:tw-hidden group-focus-visible/vault-section-header:tw-hidden':
|
||||||
collapsibleKey && sectionOpenState(),
|
collapsibleKey() && sectionOpenState(),
|
||||||
'tw-hidden': collapsibleKey && !sectionOpenState(),
|
'tw-hidden': collapsibleKey() && !sectionOpenState(),
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ ciphers().length }}
|
{{ ciphers().length }}
|
||||||
</span>
|
</span>
|
||||||
<span class="tw-pr-1" *ngIf="collapsibleKey">
|
<span class="tw-pr-1" *ngIf="collapsibleKey()">
|
||||||
<i
|
<i
|
||||||
class="bwi tw-text-main"
|
class="bwi tw-text-main"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
@@ -71,18 +71,18 @@
|
|||||||
|
|
||||||
<ng-template #descriptionText>
|
<ng-template #descriptionText>
|
||||||
<div
|
<div
|
||||||
*ngIf="description"
|
*ngIf="description()"
|
||||||
class="tw-text-muted tw-px-1 tw-mb-2"
|
class="tw-text-muted tw-px-1 tw-mb-2"
|
||||||
[ngClass]="{ '!tw-mb-0': disableDescriptionMargin }"
|
[ngClass]="{ '!tw-mb-0': disableDescriptionMargin() }"
|
||||||
bitTypography="body2"
|
bitTypography="body2"
|
||||||
>
|
>
|
||||||
{{ description }}
|
{{ description() }}
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #itemGroup>
|
<ng-template #itemGroup>
|
||||||
<bit-item-group>
|
<bit-item-group>
|
||||||
<ng-container *ngFor="let group of cipherGroups$()">
|
<ng-container *ngFor="let group of cipherGroups()">
|
||||||
<ng-container *ngIf="group.subHeaderKey">
|
<ng-container *ngIf="group.subHeaderKey">
|
||||||
<h3 class="tw-text-muted tw-text-xs tw-font-semibold tw-pl-1 tw-mb-1 bit-compact:tw-m-0">
|
<h3 class="tw-text-muted tw-text-xs tw-font-semibold tw-pl-1 tw-mb-1 bit-compact:tw-m-0">
|
||||||
{{ group.subHeaderKey | i18n }}
|
{{ group.subHeaderKey | i18n }}
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
(click)="primaryActionOnSelect(cipher)"
|
(click)="primaryActionOnSelect(cipher)"
|
||||||
(dblclick)="launchCipher(cipher)"
|
(dblclick)="launchCipher(cipher)"
|
||||||
[appA11yTitle]="
|
[appA11yTitle]="
|
||||||
cipherItemTitleKey(cipher) | async | i18n: cipher.name : cipher.login.username
|
cipherItemTitleKey()(cipher) | i18n: cipher.name : cipher.login.username
|
||||||
"
|
"
|
||||||
class="{{ itemHeightClass }}"
|
class="{{ itemHeightClass }}"
|
||||||
>
|
>
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
*ngIf="cipher.organizationId"
|
*ngIf="cipher.organizationId"
|
||||||
slot="default-trailing"
|
slot="default-trailing"
|
||||||
appOrgIcon
|
appOrgIcon
|
||||||
[tierType]="cipher.organization.productTierType"
|
[tierType]="cipher.organization!.productTierType"
|
||||||
[size]="'small'"
|
[size]="'small'"
|
||||||
[appA11yTitle]="orgIconTooltip(cipher)"
|
[appA11yTitle]="orgIconTooltip(cipher)"
|
||||||
></i>
|
></i>
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ng-container slot="end">
|
<ng-container slot="end">
|
||||||
<bit-item-action *ngIf="!(hideAutofillButton$ | async)">
|
<bit-item-action *ngIf="!hideAutofillButton()">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
bitBadge
|
bitBadge
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
{{ "fill" | i18n }}
|
{{ "fill" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</bit-item-action>
|
</bit-item-action>
|
||||||
<bit-item-action *ngIf="!showAutofillButton && cipher.canLaunch">
|
<bit-item-action *ngIf="!showAutofillButton() && cipher.canLaunch">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
bitIconButton="bwi-external-link"
|
bitIconButton="bwi-external-link"
|
||||||
@@ -147,8 +147,8 @@
|
|||||||
<app-item-copy-actions [cipher]="cipher"></app-item-copy-actions>
|
<app-item-copy-actions [cipher]="cipher"></app-item-copy-actions>
|
||||||
<app-item-more-options
|
<app-item-more-options
|
||||||
[cipher]="cipher"
|
[cipher]="cipher"
|
||||||
[hideAutofillOptions]="hideAutofillOptions$ | async"
|
[hideAutofillOptions]="hideAutofillMenuOptions()"
|
||||||
[showViewOption]="primaryActionAutofill"
|
[showViewOption]="primaryActionAutofill()"
|
||||||
></app-item-more-options>
|
></app-item-more-options>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</bit-item>
|
</bit-item>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { CdkVirtualScrollViewport, ScrollingModule } from "@angular/cdk/scrolling";
|
import { CdkVirtualScrollViewport, ScrollingModule } from "@angular/cdk/scrolling";
|
||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import {
|
import {
|
||||||
@@ -8,18 +6,17 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
inject,
|
inject,
|
||||||
Input,
|
|
||||||
Output,
|
Output,
|
||||||
Signal,
|
Signal,
|
||||||
signal,
|
signal,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
computed,
|
computed,
|
||||||
OnInit,
|
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
input,
|
input,
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
|
import { toSignal } from "@angular/core/rxjs-interop";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { firstValueFrom, Observable, map } from "rxjs";
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
@@ -53,7 +50,10 @@ import {
|
|||||||
import { BrowserApi } from "../../../../../platform/browser/browser-api";
|
import { BrowserApi } from "../../../../../platform/browser/browser-api";
|
||||||
import BrowserPopupUtils from "../../../../../platform/browser/browser-popup-utils";
|
import BrowserPopupUtils from "../../../../../platform/browser/browser-popup-utils";
|
||||||
import { VaultPopupAutofillService } from "../../../services/vault-popup-autofill.service";
|
import { VaultPopupAutofillService } from "../../../services/vault-popup-autofill.service";
|
||||||
import { VaultPopupSectionService } from "../../../services/vault-popup-section.service";
|
import {
|
||||||
|
VaultPopupSectionService,
|
||||||
|
PopupSectionOpen,
|
||||||
|
} from "../../../services/vault-popup-section.service";
|
||||||
import { PopupCipherView } from "../../../views/popup-cipher.view";
|
import { PopupCipherView } from "../../../views/popup-cipher.view";
|
||||||
import { ItemCopyActionsComponent } from "../item-copy-action/item-copy-actions.component";
|
import { ItemCopyActionsComponent } from "../item-copy-action/item-copy-actions.component";
|
||||||
import { ItemMoreOptionsComponent } from "../item-more-options/item-more-options.component";
|
import { ItemMoreOptionsComponent } from "../item-more-options/item-more-options.component";
|
||||||
@@ -81,17 +81,25 @@ import { ItemMoreOptionsComponent } from "../item-more-options/item-more-options
|
|||||||
templateUrl: "vault-list-items-container.component.html",
|
templateUrl: "vault-list-items-container.component.html",
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
export class VaultListItemsContainerComponent implements AfterViewInit {
|
||||||
private compactModeService = inject(CompactModeService);
|
private compactModeService = inject(CompactModeService);
|
||||||
private vaultPopupSectionService = inject(VaultPopupSectionService);
|
private vaultPopupSectionService = inject(VaultPopupSectionService);
|
||||||
|
|
||||||
@ViewChild(CdkVirtualScrollViewport, { static: false }) viewPort: CdkVirtualScrollViewport;
|
@ViewChild(CdkVirtualScrollViewport, { static: false }) viewPort!: CdkVirtualScrollViewport;
|
||||||
@ViewChild(DisclosureComponent) disclosure: DisclosureComponent;
|
@ViewChild(DisclosureComponent) disclosure!: DisclosureComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether the section should be open or closed if collapsibleKey is provided
|
* Indicates whether the section should be open or closed if collapsibleKey is provided
|
||||||
*/
|
*/
|
||||||
protected sectionOpenState: Signal<boolean> | undefined;
|
protected sectionOpenState: Signal<boolean> = computed(() => {
|
||||||
|
if (!this.collapsibleKey()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
this.vaultPopupSectionService.getOpenDisplayStateForSection(this.collapsibleKey()!)() ?? true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class used to set the height of a bit item's inner content.
|
* The class used to set the height of a bit item's inner content.
|
||||||
@@ -115,7 +123,7 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
* Timeout used to add a small delay when selecting a cipher to allow for double click to launch
|
* Timeout used to add a small delay when selecting a cipher to allow for double click to launch
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private viewCipherTimeout: number | null;
|
private viewCipherTimeout?: number;
|
||||||
|
|
||||||
ciphers = input<PopupCipherView[]>([]);
|
ciphers = input<PopupCipherView[]>([]);
|
||||||
|
|
||||||
@@ -123,23 +131,26 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
* If true, we will group ciphers by type (Login, Card, Identity)
|
* If true, we will group ciphers by type (Login, Card, Identity)
|
||||||
* within subheadings in a single container, converted to a WritableSignal.
|
* within subheadings in a single container, converted to a WritableSignal.
|
||||||
*/
|
*/
|
||||||
groupByType = input<boolean>(false);
|
groupByType = input<boolean | undefined>(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computed signal for a grouped list of ciphers with an optional header
|
* Computed signal for a grouped list of ciphers with an optional header
|
||||||
*/
|
*/
|
||||||
cipherGroups$ = computed<
|
cipherGroups = computed<
|
||||||
{
|
{
|
||||||
subHeaderKey?: string | null;
|
subHeaderKey?: string;
|
||||||
ciphers: PopupCipherView[];
|
ciphers: PopupCipherView[];
|
||||||
}[]
|
}[]
|
||||||
>(() => {
|
>(() => {
|
||||||
const groups: { [key: string]: CipherView[] } = {};
|
// Not grouping by type, return a single group with all ciphers
|
||||||
|
if (!this.groupByType()) {
|
||||||
|
return [{ ciphers: this.ciphers() }];
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups: Record<string, PopupCipherView[]> = {};
|
||||||
|
|
||||||
this.ciphers().forEach((cipher) => {
|
this.ciphers().forEach((cipher) => {
|
||||||
let groupKey;
|
let groupKey = "all";
|
||||||
|
|
||||||
if (this.groupByType()) {
|
|
||||||
switch (cipher.type) {
|
switch (cipher.type) {
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
groupKey = "cards";
|
groupKey = "cards";
|
||||||
@@ -148,7 +159,6 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
groupKey = "identities";
|
groupKey = "identities";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!groups[groupKey]) {
|
if (!groups[groupKey]) {
|
||||||
groups[groupKey] = [];
|
groups[groupKey] = [];
|
||||||
@@ -157,17 +167,16 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
groups[groupKey].push(cipher);
|
groups[groupKey].push(cipher);
|
||||||
});
|
});
|
||||||
|
|
||||||
return Object.keys(groups).map((key) => ({
|
return Object.entries(groups).map(([key, ciphers]) => ({
|
||||||
subHeaderKey: this.groupByType ? key : "",
|
subHeaderKey: key != "all" ? key : undefined,
|
||||||
ciphers: groups[key],
|
ciphers: ciphers,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Title for the vault list item section.
|
* Title for the vault list item section.
|
||||||
*/
|
*/
|
||||||
@Input()
|
title = input<string | undefined>(undefined);
|
||||||
title: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optionally allow the items to be collapsed.
|
* Optionally allow the items to be collapsed.
|
||||||
@@ -175,21 +184,18 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
* The key must be added to the state definition in `vault-popup-section.service.ts` since the
|
* The key must be added to the state definition in `vault-popup-section.service.ts` since the
|
||||||
* collapsed state is stored locally.
|
* collapsed state is stored locally.
|
||||||
*/
|
*/
|
||||||
@Input()
|
collapsibleKey = input<keyof PopupSectionOpen | undefined>(undefined);
|
||||||
collapsibleKey: "favorites" | "allItems" | undefined;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional description for the vault list item section. Will be shown below the title even when
|
* Optional description for the vault list item section. Will be shown below the title even when
|
||||||
* no ciphers are available.
|
* no ciphers are available.
|
||||||
*/
|
*/
|
||||||
@Input()
|
description = input<string | undefined>(undefined);
|
||||||
description: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Option to show a refresh button in the section header.
|
* Option to show a refresh button in the section header.
|
||||||
*/
|
*/
|
||||||
@Input({ transform: booleanAttribute })
|
showRefresh = input(false, { transform: booleanAttribute });
|
||||||
showRefresh: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event emitted when the refresh button is clicked.
|
* Event emitted when the refresh button is clicked.
|
||||||
@@ -200,66 +206,61 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
/**
|
/**
|
||||||
* Flag indicating that the current tab location is blocked
|
* Flag indicating that the current tab location is blocked
|
||||||
*/
|
*/
|
||||||
currentURIIsBlocked$: Observable<boolean> =
|
currentURIIsBlocked = toSignal(this.vaultPopupAutofillService.currentTabIsOnBlocklist$);
|
||||||
this.vaultPopupAutofillService.currentTabIsOnBlocklist$;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolved i18n key to use for suggested cipher items
|
* Resolved i18n key to use for suggested cipher items
|
||||||
*/
|
*/
|
||||||
cipherItemTitleKey = (cipher: CipherView) =>
|
cipherItemTitleKey = computed(() => {
|
||||||
this.currentURIIsBlocked$.pipe(
|
return (cipher: CipherView) => {
|
||||||
map((uriIsBlocked) => {
|
|
||||||
const hasUsername = cipher.login?.username != null;
|
const hasUsername = cipher.login?.username != null;
|
||||||
const key = this.primaryActionAutofill && !uriIsBlocked ? "autofillTitle" : "viewItemTitle";
|
const key =
|
||||||
|
this.primaryActionAutofill() && !this.currentURIIsBlocked()
|
||||||
|
? "autofillTitle"
|
||||||
|
: "viewItemTitle";
|
||||||
return hasUsername ? `${key}WithField` : key;
|
return hasUsername ? `${key}WithField` : key;
|
||||||
}),
|
};
|
||||||
);
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Option to show the autofill button for each item.
|
* Option to show the autofill button for each item.
|
||||||
*/
|
*/
|
||||||
@Input({ transform: booleanAttribute })
|
showAutofillButton = input(false, { transform: booleanAttribute });
|
||||||
showAutofillButton: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag indicating whether the suggested cipher item autofill button should be shown or not
|
* Flag indicating whether the suggested cipher item autofill button should be shown or not
|
||||||
*/
|
*/
|
||||||
hideAutofillButton$ = this.currentURIIsBlocked$.pipe(
|
hideAutofillButton = computed(
|
||||||
map((uriIsBlocked) => !this.showAutofillButton || uriIsBlocked || this.primaryActionAutofill),
|
() => !this.showAutofillButton() || this.currentURIIsBlocked() || this.primaryActionAutofill(),
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag indicating whether the cipher item autofill options should be shown or not
|
* Flag indicating whether the cipher item autofill menu options should be shown or not
|
||||||
*/
|
*/
|
||||||
hideAutofillOptions$: Observable<boolean> = this.currentURIIsBlocked$.pipe(
|
hideAutofillMenuOptions = computed(() => this.currentURIIsBlocked() || this.showAutofillButton());
|
||||||
map((uriIsBlocked) => uriIsBlocked || this.showAutofillButton),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Option to perform autofill operation as the primary action for autofill suggestions.
|
* Option to perform autofill operation as the primary action for autofill suggestions.
|
||||||
*/
|
*/
|
||||||
@Input({ transform: booleanAttribute })
|
primaryActionAutofill = input(false, { transform: booleanAttribute });
|
||||||
primaryActionAutofill: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the bottom margin from the bit-section in this component
|
* Remove the bottom margin from the bit-section in this component
|
||||||
* (used for containers at the end of the page where bottom margin is not needed)
|
* (used for containers at the end of the page where bottom margin is not needed)
|
||||||
*/
|
*/
|
||||||
@Input({ transform: booleanAttribute })
|
disableSectionMargin = input(false, { transform: booleanAttribute });
|
||||||
disableSectionMargin: boolean = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the description margin
|
* Remove the description margin
|
||||||
*/
|
*/
|
||||||
@Input({ transform: booleanAttribute })
|
disableDescriptionMargin = input(false, { transform: booleanAttribute });
|
||||||
disableDescriptionMargin: boolean = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tooltip text for the organization icon for ciphers that belong to an organization.
|
* The tooltip text for the organization icon for ciphers that belong to an organization.
|
||||||
* @param cipher
|
* @param cipher
|
||||||
*/
|
*/
|
||||||
orgIconTooltip(cipher: PopupCipherView) {
|
orgIconTooltip(cipher: PopupCipherView) {
|
||||||
if (cipher.collectionIds.length > 1) {
|
if (cipher.collectionIds.length > 1 || !cipher.collections) {
|
||||||
return this.i18nService.t("nCollections", cipher.collectionIds.length);
|
return this.i18nService.t("nCollections", cipher.collectionIds.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,16 +280,6 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
if (!this.collapsibleKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sectionOpenState = this.vaultPopupSectionService.getOpenDisplayStateForSection(
|
|
||||||
this.collapsibleKey,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngAfterViewInit() {
|
async ngAfterViewInit() {
|
||||||
const autofillShortcut = await this.platformUtilsService.getAutofillKeyboardShortcut();
|
const autofillShortcut = await this.platformUtilsService.getAutofillKeyboardShortcut();
|
||||||
|
|
||||||
@@ -301,10 +292,8 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async primaryActionOnSelect(cipher: CipherView) {
|
primaryActionOnSelect(cipher: CipherView) {
|
||||||
const isBlocked = await firstValueFrom(this.currentURIIsBlocked$);
|
return this.primaryActionAutofill() && !this.currentURIIsBlocked()
|
||||||
|
|
||||||
return this.primaryActionAutofill && !isBlocked
|
|
||||||
? this.doAutofill(cipher)
|
? this.doAutofill(cipher)
|
||||||
: this.onViewCipher(cipher);
|
: this.onViewCipher(cipher);
|
||||||
}
|
}
|
||||||
@@ -320,7 +309,7 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
// If there is a view action pending, clear it
|
// If there is a view action pending, clear it
|
||||||
if (this.viewCipherTimeout != null) {
|
if (this.viewCipherTimeout != null) {
|
||||||
window.clearTimeout(this.viewCipherTimeout);
|
window.clearTimeout(this.viewCipherTimeout);
|
||||||
this.viewCipherTimeout = null;
|
this.viewCipherTimeout = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
@@ -363,7 +352,7 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
// Ensure the timeout is always cleared
|
// Ensure the timeout is always cleared
|
||||||
this.viewCipherTimeout = null;
|
this.viewCipherTimeout = undefined;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cipher.canLaunch ? 200 : 0,
|
cipher.canLaunch ? 200 : 0,
|
||||||
@@ -374,12 +363,12 @@ export class VaultListItemsContainerComponent implements OnInit, AfterViewInit {
|
|||||||
* Update section open/close state based on user action
|
* Update section open/close state based on user action
|
||||||
*/
|
*/
|
||||||
async toggleSectionOpen() {
|
async toggleSectionOpen() {
|
||||||
if (!this.collapsibleKey) {
|
if (!this.collapsibleKey()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.vaultPopupSectionService.updateSectionOpenStoredState(
|
await this.vaultPopupSectionService.updateSectionOpenStoredState(
|
||||||
this.collapsibleKey,
|
this.collapsibleKey()!,
|
||||||
this.disclosure.open,
|
this.disclosure.open,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user