mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 21:50:15 +00:00
feat: add experimental version of a callback solution
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { combineLatest, distinctUntilChanged, mergeMap, of, Subject, switchMap } from "rxjs";
|
||||
import { combineLatest, distinctUntilChanged, mergeMap, Subject } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service";
|
||||
@@ -6,7 +6,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
|
||||
import { BadgeService } from "../../platform/badge/badge.service";
|
||||
import { BadgeService, StateSetting } from "../../platform/badge/badge.service";
|
||||
import { BadgeStatePriority } from "../../platform/badge/priority";
|
||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||
|
||||
@@ -24,89 +24,129 @@ export class AutofillBadgeUpdaterService {
|
||||
private badgeSettingsService: BadgeSettingsServiceAbstraction,
|
||||
private logService: LogService,
|
||||
) {
|
||||
const cipherViews$ = this.accountService.activeAccount$.pipe(
|
||||
switchMap((account) => (account?.id ? this.cipherService.cipherViews$(account?.id) : of([]))),
|
||||
);
|
||||
|
||||
combineLatest({
|
||||
account: this.accountService.activeAccount$,
|
||||
enableBadgeCounter:
|
||||
this.badgeSettingsService.enableBadgeCounter$.pipe(distinctUntilChanged()),
|
||||
ciphers: cipherViews$,
|
||||
})
|
||||
.pipe(
|
||||
mergeMap(async ({ account, enableBadgeCounter, ciphers }) => {
|
||||
if (!account) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tabs = await BrowserApi.tabsQuery({});
|
||||
for (const tab of tabs) {
|
||||
if (!tab.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (enableBadgeCounter) {
|
||||
await this.setTabState(tab, account.id);
|
||||
} else {
|
||||
await this.clearTabState(tab.id);
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
combineLatest({
|
||||
account: this.accountService.activeAccount$,
|
||||
enableBadgeCounter: this.badgeSettingsService.enableBadgeCounter$,
|
||||
replaced: this.tabReplaced$,
|
||||
ciphers: cipherViews$,
|
||||
})
|
||||
.pipe(
|
||||
mergeMap(async ({ account, enableBadgeCounter, replaced }) => {
|
||||
this.badgeService.setDynamicState("autofill-state", (tab) => {
|
||||
return combineLatest({
|
||||
account: this.accountService.activeAccount$,
|
||||
enableBadgeCounter:
|
||||
this.badgeSettingsService.enableBadgeCounter$.pipe(distinctUntilChanged()),
|
||||
}).pipe(
|
||||
mergeMap(async ({ account, enableBadgeCounter }) => {
|
||||
if (!account || !enableBadgeCounter) {
|
||||
return;
|
||||
return {
|
||||
priority: BadgeStatePriority.Default,
|
||||
state: {},
|
||||
tabId: tab.id,
|
||||
} satisfies StateSetting;
|
||||
}
|
||||
|
||||
await this.clearTabState(replaced.removedTabId);
|
||||
await this.setTabState(replaced.addedTab, account.id);
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
const ciphers = tab.url
|
||||
? await this.cipherService.getAllDecryptedForUrl(tab.url, account.id)
|
||||
: [];
|
||||
const cipherCount = ciphers.length;
|
||||
|
||||
combineLatest({
|
||||
account: this.accountService.activeAccount$,
|
||||
enableBadgeCounter: this.badgeSettingsService.enableBadgeCounter$,
|
||||
tab: this.tabUpdated$,
|
||||
ciphers: cipherViews$,
|
||||
})
|
||||
.pipe(
|
||||
mergeMap(async ({ account, enableBadgeCounter, tab }) => {
|
||||
if (!account || !enableBadgeCounter) {
|
||||
return;
|
||||
if (cipherCount === 0) {
|
||||
return {
|
||||
priority: BadgeStatePriority.Default,
|
||||
state: { text: "0" },
|
||||
tabId: tab.id,
|
||||
};
|
||||
}
|
||||
|
||||
await this.setTabState(tab, account.id);
|
||||
const countText = cipherCount > 9 ? "9+" : cipherCount.toString();
|
||||
return {
|
||||
priority: BadgeStatePriority.Default,
|
||||
state: {
|
||||
text: countText,
|
||||
},
|
||||
tabId: tab.id,
|
||||
};
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
);
|
||||
});
|
||||
|
||||
combineLatest({
|
||||
account: this.accountService.activeAccount$,
|
||||
enableBadgeCounter: this.badgeSettingsService.enableBadgeCounter$,
|
||||
tabId: this.tabRemoved$,
|
||||
ciphers: cipherViews$,
|
||||
})
|
||||
.pipe(
|
||||
mergeMap(async ({ account, enableBadgeCounter, tabId }) => {
|
||||
if (!account || !enableBadgeCounter) {
|
||||
return;
|
||||
}
|
||||
// const cipherViews$ = this.accountService.activeAccount$.pipe(
|
||||
// switchMap((account) => (account?.id ? this.cipherService.cipherViews$(account?.id) : of([]))),
|
||||
// );
|
||||
|
||||
await this.clearTabState(tabId);
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
// combineLatest({
|
||||
// account: this.accountService.activeAccount$,
|
||||
// enableBadgeCounter:
|
||||
// this.badgeSettingsService.enableBadgeCounter$.pipe(distinctUntilChanged()),
|
||||
// ciphers: cipherViews$,
|
||||
// })
|
||||
// .pipe(
|
||||
// mergeMap(async ({ account, enableBadgeCounter, ciphers }) => {
|
||||
// if (!account) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const tabs = await BrowserApi.tabsQuery({});
|
||||
// for (const tab of tabs) {
|
||||
// if (!tab.id) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// if (enableBadgeCounter) {
|
||||
// await this.setTabState(tab, account.id);
|
||||
// } else {
|
||||
// await this.clearTabState(tab.id);
|
||||
// }
|
||||
// }
|
||||
// }),
|
||||
// )
|
||||
// .subscribe();
|
||||
|
||||
// combineLatest({
|
||||
// account: this.accountService.activeAccount$,
|
||||
// enableBadgeCounter: this.badgeSettingsService.enableBadgeCounter$,
|
||||
// replaced: this.tabReplaced$,
|
||||
// ciphers: cipherViews$,
|
||||
// })
|
||||
// .pipe(
|
||||
// mergeMap(async ({ account, enableBadgeCounter, replaced }) => {
|
||||
// if (!account || !enableBadgeCounter) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// await this.clearTabState(replaced.removedTabId);
|
||||
// await this.setTabState(replaced.addedTab, account.id);
|
||||
// }),
|
||||
// )
|
||||
// .subscribe();
|
||||
|
||||
// combineLatest({
|
||||
// account: this.accountService.activeAccount$,
|
||||
// enableBadgeCounter: this.badgeSettingsService.enableBadgeCounter$,
|
||||
// tab: this.tabUpdated$,
|
||||
// ciphers: cipherViews$,
|
||||
// })
|
||||
// .pipe(
|
||||
// mergeMap(async ({ account, enableBadgeCounter, tab }) => {
|
||||
// if (!account || !enableBadgeCounter) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// await this.setTabState(tab, account.id);
|
||||
// }),
|
||||
// )
|
||||
// .subscribe();
|
||||
|
||||
// combineLatest({
|
||||
// account: this.accountService.activeAccount$,
|
||||
// enableBadgeCounter: this.badgeSettingsService.enableBadgeCounter$,
|
||||
// tabId: this.tabRemoved$,
|
||||
// ciphers: cipherViews$,
|
||||
// })
|
||||
// .pipe(
|
||||
// mergeMap(async ({ account, enableBadgeCounter, tabId }) => {
|
||||
// if (!account || !enableBadgeCounter) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// await this.clearTabState(tabId);
|
||||
// }),
|
||||
// )
|
||||
// .subscribe();
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
import { concatMap, firstValueFrom, Subscription, withLatestFrom } from "rxjs";
|
||||
import {
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
concatMap,
|
||||
EMPTY,
|
||||
firstValueFrom,
|
||||
map,
|
||||
Observable,
|
||||
Subscription,
|
||||
switchMap,
|
||||
withLatestFrom,
|
||||
} from "rxjs";
|
||||
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import {
|
||||
@@ -8,23 +19,28 @@ import {
|
||||
StateProvider,
|
||||
} from "@bitwarden/common/platform/state";
|
||||
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
|
||||
import { BadgeBrowserApi, RawBadgeState } from "./badge-browser-api";
|
||||
import { DefaultBadgeState } from "./consts";
|
||||
import { BadgeStatePriority } from "./priority";
|
||||
import { BadgeState, Unset } from "./state";
|
||||
|
||||
interface StateSetting {
|
||||
export interface StateSetting {
|
||||
priority: BadgeStatePriority;
|
||||
state: BadgeState;
|
||||
tabId?: number;
|
||||
}
|
||||
|
||||
const BADGE_STATES = new KeyDefinition(BADGE_MEMORY, "badgeStates", {
|
||||
deserializer: (value: Record<string, StateSetting>) => value ?? { states: {} },
|
||||
deserializer: (value: Record<string, StateSetting>) => value ?? {},
|
||||
});
|
||||
|
||||
export class BadgeService {
|
||||
private serviceState: GlobalState<Record<string, StateSetting>>;
|
||||
private stateCalculators = new BehaviorSubject<
|
||||
Record<string, (tab: chrome.tabs.Tab) => Observable<StateSetting>>
|
||||
>({});
|
||||
|
||||
constructor(
|
||||
private stateProvider: StateProvider,
|
||||
@@ -40,11 +56,32 @@ export class BadgeService {
|
||||
*/
|
||||
startListening(): Subscription {
|
||||
// React to tab changes
|
||||
return this.badgeApi.activeTab$
|
||||
return combineLatest([this.badgeApi.activeTab$, this.stateCalculators])
|
||||
.pipe(
|
||||
concatMap(async ([activeTab, stateCalculators]) => {
|
||||
if (activeTab == undefined) {
|
||||
return { tab: undefined, stateCalculators };
|
||||
}
|
||||
|
||||
return { activeTab: await BrowserApi.getTab(activeTab.tabId), stateCalculators };
|
||||
}),
|
||||
switchMap(({ activeTab, stateCalculators }) => {
|
||||
if (activeTab == undefined) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
return combineLatest(
|
||||
Object.values(stateCalculators).map((calculator) => calculator(activeTab)),
|
||||
).pipe(map((states) => ({ activeTab, states })));
|
||||
}),
|
||||
withLatestFrom(this.serviceState.state$),
|
||||
concatMap(async ([activeTab, serviceState]) => {
|
||||
await this.updateBadge(activeTab, serviceState, activeTab?.tabId);
|
||||
concatMap(async ([{ activeTab, states }, serviceState]) => {
|
||||
const allStates = [...Object.values(serviceState ?? {}), ...states];
|
||||
await this.updateBadge(
|
||||
{ tabId: activeTab.id ?? 0, windowId: activeTab.windowId },
|
||||
allStates,
|
||||
activeTab?.id,
|
||||
);
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
@@ -75,6 +112,16 @@ export class BadgeService {
|
||||
await this.updateBadge(activeTab, newServiceState, tabId);
|
||||
}
|
||||
|
||||
setDynamicState(
|
||||
name: string,
|
||||
calculatorFactory: (tab: chrome.tabs.Tab) => Observable<StateSetting>,
|
||||
) {
|
||||
this.stateCalculators.next({
|
||||
...this.stateCalculators.value,
|
||||
[name]: calculatorFactory,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the state with the given name.
|
||||
*
|
||||
@@ -145,7 +192,7 @@ export class BadgeService {
|
||||
*/
|
||||
private async updateBadge(
|
||||
activeTab: chrome.tabs.TabActiveInfo | null | undefined,
|
||||
serviceState: Record<string, StateSetting> | null | undefined,
|
||||
serviceState: Record<string, StateSetting> | null | undefined | Array<StateSetting>,
|
||||
tabId: number | undefined,
|
||||
) {
|
||||
if (activeTab === undefined) {
|
||||
|
||||
Reference in New Issue
Block a user