mirror of
https://github.com/bitwarden/browser
synced 2026-02-09 05:00:10 +00:00
PM-23733 - Add filter + force refresh
This commit is contained in:
@@ -2099,6 +2099,12 @@
|
||||
"featureFlags": {
|
||||
"message": "Feature flags"
|
||||
},
|
||||
"flagName": {
|
||||
"message": "Flag name"
|
||||
},
|
||||
"flagValue": {
|
||||
"message": "Flag value"
|
||||
},
|
||||
"domainRules": {
|
||||
"message": "Domain rules"
|
||||
},
|
||||
|
||||
@@ -3,20 +3,30 @@
|
||||
<i class="bwi bwi-spinner bwi-spin tw-text-2xl" aria-hidden="true"></i>
|
||||
</div>
|
||||
} @else {
|
||||
<!-- // TODO: add forced update to config -->
|
||||
<!-- // TODO: add filtering inline for table -->
|
||||
<button buttonType="primary" type="button" bitButton (click)="refresh()" class="tw-mb-4">
|
||||
{{ "refresh" | i18n }}
|
||||
</button>
|
||||
|
||||
<bit-table [dataSource]="tableDataSource">
|
||||
<ng-container header>
|
||||
<th bitCell bitSortable="key" default>key</th>
|
||||
<th bitCell bitSortable="value" default>value</th>
|
||||
</ng-container>
|
||||
<bit-search
|
||||
id="search"
|
||||
placeholder="{{ 'filter' | i18n }}"
|
||||
[(ngModel)]="searchText"
|
||||
(ngModelChange)="onSearchTextChanged($event)"
|
||||
/>
|
||||
|
||||
<ng-template body let-rows$>
|
||||
<tr bitRow *ngFor="let r of rows$ | async">
|
||||
<td bitCell>{{ r.key }}</td>
|
||||
<td bitCell>{{ r.value }}</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
<div class="tw-flex tw-flex-col">
|
||||
<bit-table [dataSource]="tableDataSource">
|
||||
<ng-container header>
|
||||
<th bitCell bitSortable="key" default>{{ "flagName" | i18n }}</th>
|
||||
<th bitCell bitSortable="value" default>{{ "flagValue" | i18n }}</th>
|
||||
</ng-container>
|
||||
|
||||
<ng-template body let-rows$>
|
||||
<tr bitRow *ngFor="let r of rows$ | async">
|
||||
<td bitCell>{{ r.key }}</td>
|
||||
<td bitCell>{{ r.value }}</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,21 +1,41 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, DestroyRef, OnInit } from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
import { map } from "rxjs";
|
||||
|
||||
import { AllowedFeatureFlagTypes } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { TableDataSource, TableModule } from "@bitwarden/components";
|
||||
import {
|
||||
ButtonModule,
|
||||
FormFieldModule,
|
||||
InputModule,
|
||||
SearchModule,
|
||||
TableDataSource,
|
||||
TableModule,
|
||||
} from "@bitwarden/components";
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
|
||||
@Component({
|
||||
selector: "app-feature-flags",
|
||||
templateUrl: "./feature-flags.component.html",
|
||||
imports: [CommonModule, TableModule],
|
||||
imports: [
|
||||
CommonModule,
|
||||
TableModule,
|
||||
ButtonModule,
|
||||
I18nPipe,
|
||||
FormsModule,
|
||||
FormFieldModule,
|
||||
InputModule,
|
||||
SearchModule,
|
||||
],
|
||||
})
|
||||
export class FeatureFlagsComponent implements OnInit {
|
||||
loading = true;
|
||||
tableDataSource = new TableDataSource<{ key: string; value: AllowedFeatureFlagTypes }>();
|
||||
|
||||
searchText = "";
|
||||
|
||||
constructor(
|
||||
private destroyRef: DestroyRef,
|
||||
private configService: ConfigService,
|
||||
@@ -42,4 +62,13 @@ export class FeatureFlagsComponent implements OnInit {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
onSearchTextChanged(searchText: string) {
|
||||
this.searchText = searchText;
|
||||
this.tableDataSource.filter = searchText;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.configService.refreshServerConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,4 +60,9 @@ export abstract class ConfigService {
|
||||
* Triggers a check that the config for the currently active user is up-to-date. If it is not, it will be fetched from the server and stored.
|
||||
*/
|
||||
abstract ensureConfigFetched(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Refreshes the server config, forcing a new retrieval from the server.
|
||||
*/
|
||||
abstract refreshServerConfig(): void;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Observable,
|
||||
of,
|
||||
shareReplay,
|
||||
startWith,
|
||||
Subject,
|
||||
switchMap,
|
||||
tap,
|
||||
@@ -57,6 +58,7 @@ export const GLOBAL_SERVER_CONFIGURATIONS = KeyDefinition.record<ServerConfig, A
|
||||
// FIXME: currently we are limited to api requests for active users. Update to accept a UserId and APIUrl once ApiService supports it.
|
||||
export class DefaultConfigService implements ConfigService {
|
||||
private failedFetchFallbackSubject = new Subject<ServerConfig>();
|
||||
private manualRefresh$ = new Subject<void>();
|
||||
|
||||
serverConfig$: Observable<ServerConfig>;
|
||||
|
||||
@@ -82,22 +84,28 @@ export class DefaultConfigService implements ConfigService {
|
||||
userId$,
|
||||
this.environmentService.environment$,
|
||||
authStatus$,
|
||||
// must have default value so combineLatest will emit once others have emitted
|
||||
this.manualRefresh$.pipe(startWith(false)),
|
||||
]).pipe(
|
||||
switchMap(([userId, environment, authStatus]) => {
|
||||
switchMap(([userId, environment, authStatus, manualRefresh]) => {
|
||||
if (userId == null || authStatus !== AuthenticationStatus.Unlocked) {
|
||||
return this.globalConfigFor$(environment.getApiUrl()).pipe(
|
||||
map((config) => [config, null, environment] as const),
|
||||
map((config) => [config, null, environment, manualRefresh] as const),
|
||||
);
|
||||
}
|
||||
|
||||
return this.userConfigFor$(userId).pipe(
|
||||
map((config) => [config, userId, environment] as const),
|
||||
map((config) => [config, userId, environment, manualRefresh] as const),
|
||||
);
|
||||
}),
|
||||
tap(async (rec) => {
|
||||
const [existingConfig, userId, environment] = rec;
|
||||
const [existingConfig, userId, environment, manualRefresh] = rec;
|
||||
// Grab new config if older retrieval interval
|
||||
if (!existingConfig || this.olderThanRetrievalInterval(existingConfig.utcDate)) {
|
||||
if (
|
||||
!existingConfig ||
|
||||
this.olderThanRetrievalInterval(existingConfig.utcDate) ||
|
||||
manualRefresh
|
||||
) {
|
||||
await this.renewConfig(existingConfig, userId, environment);
|
||||
}
|
||||
}),
|
||||
@@ -128,6 +136,10 @@ export class DefaultConfigService implements ConfigService {
|
||||
);
|
||||
}
|
||||
|
||||
refreshServerConfig() {
|
||||
this.manualRefresh$.next();
|
||||
}
|
||||
|
||||
getFeatureFlag$<Flag extends FeatureFlag>(key: Flag) {
|
||||
return this.serverConfig$.pipe(map((serverConfig) => getFeatureFlagValue(serverConfig, key)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user