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

feat: improve windows managemet when switching to popup [LW 12347] #1728

Merged
merged 3 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const DropdownMenuOverlay: VFC<Props> = ({
if (activated) {
await posthog.sendEvent(PostHogAction.SettingsSwitchToNamiClick);
try {
await backgroundServices.handleOpenPopup();
await backgroundServices.closeAllTabsAndOpenPopup();
} catch (error) {
logger.warn(error);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { closeAllLaceWindows } from '@lib/scripts/background/util';
import { closeAllLaceOrNamiTabs } from '@lib/scripts/background/util';
import { MessageSender, NamiMessages } from '../shared/types';
import { logger } from '@lace/common';

Expand All @@ -8,7 +8,7 @@ export const createLaceMigrationOpenListener =
logger.debug('[NAMI MIGRATION] createLaceMigrationOpenListener', message, sender);
if (message === NamiMessages.open && sender.id === namiExtensionId) {
// First close all open lace tabs
await closeAllLaceWindows();
await closeAllLaceOrNamiTabs();
createTab({ url: `chrome-extension://${laceExtensionId}/app.html` });
}
};
6 changes: 3 additions & 3 deletions apps/browser-extension-wallet/src/hooks/useFatalError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ObservableWallet } from '@cardano-sdk/wallet';
import { useObservable } from '@lace/common';
import { logger, useObservable } from '@lace/common';
import { useBackgroundServiceAPIContext } from '@providers';
import { useWalletStore } from '@src/stores';
import { useMemo } from 'react';
Expand Down Expand Up @@ -57,11 +57,11 @@ export const useFatalError = (): FatalError | undefined => {
const walletError = useObservable(walletError$);

if (unhandledServiceWorkerError) {
console.error('useFatalError (service worker):', unhandledServiceWorkerError);
logger.error('useFatalError (service worker):', unhandledServiceWorkerError);
}

if (walletError) {
console.error('useFatalError (wallet):', walletError);
logger.error('useFatalError (wallet):', walletError);
}

return unhandledServiceWorkerError || walletError;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const backgroundServiceProperties: RemoteApiProperties<BackgroundService>
},
handleOpenBrowser: RemoteApiPropertyType.MethodReturningPromise,
handleOpenNamiBrowser: RemoteApiPropertyType.MethodReturningPromise,
handleOpenPopup: RemoteApiPropertyType.MethodReturningPromise,
closeAllTabsAndOpenPopup: RemoteApiPropertyType.MethodReturningPromise,
handleChangeTheme: RemoteApiPropertyType.MethodReturningPromise,
handleChangeMode: RemoteApiPropertyType.MethodReturningPromise,
clearBackgroundStorage: RemoteApiPropertyType.MethodReturningPromise,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-magic-numbers */
import { runtime, tabs, storage as webStorage } from 'webextension-polyfill';
import { runtime, tabs, storage as webStorage, windows, action, Tabs, Windows } from 'webextension-polyfill';
import {
BackgroundService,
BaseChannels,
Expand All @@ -22,12 +22,13 @@ import { backgroundServiceProperties } from '../config';
import { exposeApi } from '@cardano-sdk/web-extension';
import { Cardano } from '@cardano-sdk/core';
import { config } from '@src/config';
import { getADAPriceFromBackgroundStorage, closeAllLaceWindows } from '../util';
import { getADAPriceFromBackgroundStorage, closeAllLaceOrNamiTabs } from '../util';
import { currencies as currenciesMap, currencyCode } from '@providers/currency/constants';
import { clearBackgroundStorage, getBackgroundStorage, setBackgroundStorage } from '../storage';
import { laceFeaturesApiProperties, LACE_FEATURES_CHANNEL } from '../injectUtil';
import { getErrorMessage } from '@src/utils/get-error-message';
import { logger } from '@lace/common';
import { POPUP_WINDOW_NAMI_TITLE } from '@utils/constants';

export const requestMessage$ = new Subject<Message>();
export const backendFailures$ = new BehaviorSubject(0);
Expand Down Expand Up @@ -109,18 +110,43 @@ const handleOpenNamiBrowser = async (data: OpenNamiBrowserData) => {
await tabs.create({ url: `popup.html#${data.path}` }).catch((error) => logger.error(error));
};

const handleOpenPopup = async () => {
if (typeof chrome.action.openPopup !== 'function') return;
const enrichWithTabsDataIfMissing = (browserWindows: Windows.Window[]) => {
const promises = browserWindows.map(async (w) => ({
...w,
tabs: w.tabs || (await tabs.query({ windowId: w.id }))
}));
return Promise.all(promises);
};

// Yes, Nami mode can be rendered as tab
const isLaceOrNamiTab = (tab: Tabs.Tab) => ['Lace', POPUP_WINDOW_NAMI_TITLE].includes(tab.title);

type WindowWithTabsNotOptional = Windows.Window & {
tabs: Tabs.Tab[];
};
const doesWindowHaveOtherTabs = (browserWindow: WindowWithTabsNotOptional) =>
browserWindow.tabs.some((t) => !isLaceOrNamiTab(t));

const closeAllTabsAndOpenPopup = async () => {
try {
const [currentWindow] = await tabs.query({ currentWindow: true, title: 'Lace' });
await closeAllLaceWindows();
// behaves inconsistently if executed without setTimeout
setTimeout(async () => {
if (currentWindow?.windowId) {
await chrome.windows.update(currentWindow.windowId, { focused: true });
await chrome.action.openPopup();
}
}, 30);
const allWindows = await enrichWithTabsDataIfMissing(await windows.getAll());
if (allWindows.length === 0) return;

const windowsWith3rdPartyTabs = allWindows.filter((w) => doesWindowHaveOtherTabs(w));
const candidateWindowsWithPreferenceForCurrentlyFocused = windowsWith3rdPartyTabs.sort(
(w1, w2) => Number(w2.focused) - Number(w1.focused)
);

let nextFocusedWindow = candidateWindowsWithPreferenceForCurrentlyFocused[0];
const noSingleWindowWith3rdPartyTabsOpen = !nextFocusedWindow;
if (noSingleWindowWith3rdPartyTabsOpen) {
nextFocusedWindow = allWindows[0];
await tabs.create({ active: true, windowId: nextFocusedWindow.id });
}

await windows.update(nextFocusedWindow.id, { focused: true });
await closeAllLaceOrNamiTabs();
await action.openPopup();
} catch (error) {
// unable to programatically open the popup again
logger.error(error);
Expand Down Expand Up @@ -225,9 +251,9 @@ const toUnhandledError = (error: unknown, type: UnhandledError['type']): Unhandl
message: getErrorMessage(error)
});
const unhandledError$ = merge(
fromEvent(globalThis, 'error').pipe(map((e: ErrorEvent): UnhandledError => toUnhandledError(e, 'error'))),
fromEvent(globalThis, 'error').pipe(map((e: ErrorEvent): UnhandledError => toUnhandledError(e.error, 'error'))),
fromEvent(globalThis, 'unhandledrejection').pipe(
map((e: PromiseRejectionEvent): UnhandledError => toUnhandledError(e, 'unhandledrejection'))
map((e: PromiseRejectionEvent): UnhandledError => toUnhandledError(e.reason, 'unhandledrejection'))
)
);

Expand All @@ -238,7 +264,7 @@ exposeApi<BackgroundService>(
api$: of({
handleOpenBrowser,
handleOpenNamiBrowser,
handleOpenPopup,
closeAllTabsAndOpenPopup,
requestMessage$,
migrationState$,
coinPrices,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const getActiveWallet = async ({
return { wallet, account };
};

export const closeAllLaceWindows = async (shouldRemoveTab?: (url: string) => boolean): Promise<void> => {
export const closeAllLaceOrNamiTabs = async (shouldRemoveTab?: (url: string) => boolean): Promise<void> => {
const openTabs = await tabs.query({ title: 'Lace' });
const namiTabs = await tabs.query({ title: POPUP_WINDOW_NAMI_TITLE });
openTabs.push(...namiTabs);
Expand All @@ -135,7 +135,7 @@ export const closeAllLaceWindows = async (shouldRemoveTab?: (url: string) => boo
};

export const ensureUiIsOpenAndLoaded = async (url?: string): Promise<Tabs.Tab> => {
await closeAllLaceWindows((tabUrl) => DAPP_CONNECTOR_REGEX.test(tabUrl));
await closeAllLaceOrNamiTabs((tabUrl) => DAPP_CONNECTOR_REGEX.test(tabUrl));

const tab = await launchCip30Popup(url);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export type UnhandledError = {

export type BackgroundService = {
handleOpenBrowser: (data: OpenBrowserData, urlSearchParams?: string) => Promise<void>;
handleOpenPopup: () => Promise<void>;
closeAllTabsAndOpenPopup: () => Promise<void>;
handleOpenNamiBrowser: (data: OpenNamiBrowserData) => Promise<void>;
requestMessage$: Subject<Message>;
migrationState$: BehaviorSubject<MigrationState | undefined>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState, useEffect, useMemo } from 'react';
import { usePostHogClientContext } from '@providers/PostHogClientProvider';
import { cacheRequest } from '@views/browser/features/dapp/explorer/services/cache';
import { logger } from '@lace/common';

type FetchCategoriesResult = {
loading: boolean;
Expand Down Expand Up @@ -48,7 +49,7 @@ export const useCategoriesFetcher = (): FetchCategoriesResult => {
return result.categories;
});
} catch (error) {
console.error('Failed to fetch dapp categories.', error);
logger.error('Failed to fetch dapp categories.', error);
}

categories = categories.filter((category) => !disallowedDappCategories.has(category));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
import { ISectionCardItem } from '@views/browser/features/dapp/explorer/services/helpers/apis-formatter/types';
import { usePostHogClientContext } from '@providers/PostHogClientProvider';
import { cacheRequest } from '@views/browser/features/dapp/explorer/services/cache';
import { logger } from '@lace/common';

const dappRadarApiUrl = process.env.DAPP_RADAR_API_URL;
const dappRadarApiKey = process.env.DAPP_RADAR_API_KEY;
Expand Down Expand Up @@ -145,7 +146,7 @@ const useDAppFetcher = ({
return parsedResponse.results;
});
} catch (error) {
console.error('Failed to fetch dapp list.', error);
logger.error('Failed to fetch dapp list.', error);
}

setData(results);
Expand All @@ -155,7 +156,7 @@ const useDAppFetcher = ({

// eslint-disable-next-line unicorn/consistent-function-scoping
const fetchMore = () => {
console.error('Pagination not implemented!');
logger.error('Pagination not implemented!');
};

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { logger } from '@lace/common';

type CacheEntry<Data> = {
data: Data;
timestamp: number;
Expand Down Expand Up @@ -25,7 +27,7 @@ const parseRawCacheRepo = (rawCacheRepo: string) => {
cacheRepo = JSON.parse(rawCacheRepo);
}
} catch (error) {
console.error('Failed to parse dapp explorer data cache', error);
logger.error('Failed to parse dapp explorer data cache', error);
}
return cacheRepo;
};
Expand Down
Loading