Skip to content

Commit

Permalink
handle destroy api, various fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Ross Bulat committed Mar 6, 2024
1 parent 27b87d7 commit d4df7c1
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 80 deletions.
75 changes: 42 additions & 33 deletions src/contexts/Api/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const Api = createContext<ApiContextInterface>(defaultApiContext);
export const useApi = () => useContext(Api);

export const ApiProvider = ({ children }: { children: ReactNode }) => {
const { getActiveTab, getChainTab, tabs } = useTabs();
const { getActiveTab, tabs } = useTabs();

// Store API connection status of each tab. NOTE: requires ref as it is used in event listener.
const [apiStatus, setApiStatusState] = useState<Record<number, ApiStatus>>(
Expand All @@ -31,6 +31,13 @@ export const ApiProvider = ({ children }: { children: ReactNode }) => {
setApiStatusState(status);
};

// Remove api status on tab close.
const removeApiStatus = (tabId: number) => {
const newApiStatus = { ...apiStatusRef.current };
delete newApiStatus[tabId];
setApiStatus(newApiStatus);
};

// Gets an api status based on a tab id.
const getApiStatus = (tabId: number): ApiStatus => apiStatus[tabId];

Expand All @@ -45,38 +52,36 @@ export const ApiProvider = ({ children }: { children: ReactNode }) => {
// Handle incoming api status updates.
const handleNewApiStatus = (e: Event): void => {
if (isCustomEvent(e)) {
const { chainId, event } = e.detail;
const tab = getChainTab(chainId);

if (tab) {
const tabId = tab.id;

switch (event) {
case 'ready':
setApiStatus({
...apiStatusRef.current,
[tabId]: 'ready',
});
break;
case 'connecting':
setApiStatus({ ...apiStatusRef.current, [tabId]: 'connecting' });
break;
case 'connected':
setApiStatus({ ...apiStatusRef.current, [tabId]: 'connected' });
break;
case 'disconnected':
setApiStatus({
...apiStatusRef.current,
[tabId]: 'disconnected',
});
break;
case 'error':
setApiStatus({
...apiStatusRef.current,
[tabId]: 'disconnected',
});
break;
}
const { tabId, event } = e.detail;

switch (event) {
case 'ready':
setApiStatus({
...apiStatusRef.current,
[tabId]: 'ready',
});
break;
case 'connecting':
setApiStatus({ ...apiStatusRef.current, [tabId]: 'connecting' });
break;
case 'connected':
setApiStatus({ ...apiStatusRef.current, [tabId]: 'connected' });
break;
case 'disconnected':
setApiStatus({
...apiStatusRef.current,
[tabId]: 'disconnected',
});
break;
case 'error':
setApiStatus({
...apiStatusRef.current,
[tabId]: 'disconnected',
});
break;
case 'destroyed':
removeApiStatus(tabId);
break;
}
}
};
Expand All @@ -90,12 +95,16 @@ export const ApiProvider = ({ children }: { children: ReactNode }) => {
tabs.forEach((tab) => {
if (tab?.chain && tab.autoConnect) {
const { id, provider } = tab.chain;

const endpoint = NetworkDirectory[id].providers[provider];
ApiController.instantiate(tab.id, id, endpoint);
}
});
}, []);

console.log(ApiController.instances);
console.log(apiStatus);

return (
<Api.Provider
value={{
Expand Down
27 changes: 16 additions & 11 deletions src/contexts/Tabs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const TabsContext =
export const useTabs = () => useContext(TabsContext);

export const TabsProvider = ({ children }: { children: ReactNode }) => {
const { autoConnect } = useSettings();
const { autoConnect, autoTabNaming } = useSettings();

// Created tabs.
const [tabs, setTabsState] = useState<Tabs>(local.getTabs() || defaultTabs);
Expand Down Expand Up @@ -128,6 +128,9 @@ export const TabsProvider = ({ children }: { children: ReactNode }) => {
if (activeTabIndex > index) {
setActiveTabIndex(activeTabIndex - 1);
}

// Destroy Api instance associated with tab.
ApiController.destroy(id);
};

// Rename a tab.
Expand All @@ -151,18 +154,20 @@ export const TabsProvider = ({ children }: { children: ReactNode }) => {
};

// Connect tab to an Api instance and update its chain data.
const connectTab = (tabId: number, chainId: ChainId, endpoint: string) => {
setTabs(
[...tabs].map((tab) =>
tab.id === tabId
? {
...tab,
chain: { id: chainId, provider: endpoint },
}
: tab
)
const connectTab = (tabId: number, chainId: ChainId, provider: string) => {
const newTabs = [...tabs].map((tab) =>
tab.id === tabId
? {
...tab,
name: autoTabNaming ? getAutoTabName(chainId) : tab.name,
chain: { id: chainId, provider },
}
: tab
);

const endpoint = NetworkDirectory[chainId].providers[provider];

setTabs(newTabs);
ApiController.instantiate(tabId, chainId, endpoint);
};

Expand Down
8 changes: 4 additions & 4 deletions src/controllers/ApiController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ export class ApiController {

// Instantiate a new `Api` instance with the supplied chain id and endpoint.
static async instantiate(tabId: number, chainId: ChainId, endpoint: string) {
if (!ApiController.instances[tabId]) {
ApiController.instances[tabId] = new Api(chainId, endpoint);
await ApiController.instances[tabId].initialize();
if (!this.instances[tabId]) {
this.instances[tabId] = new Api(tabId, chainId, endpoint);
await this.instances[tabId].initialize();
}
}

// Gracefully disconnect and then destroy an api instance.
static async destroy(tabId: number) {
const api = ApiController.instances[tabId];
const api = this.instances[tabId];

if (api) {
await api.disconnect();
Expand Down
21 changes: 13 additions & 8 deletions src/library/Tabs/Wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,27 +93,32 @@ export const TabWrapper = styled(motion.div)`
/* Main tab elements. */
.connection {
--connection-icon-size: 0.78rem;
--connection-icon-size: 0.95rem;
height: var(--tab-height);
position: absolute;
top: 0;
left: 0.4rem;
left: 0.3rem;
width: var(--connection-icon-size);
display: flex;
align-items: center;
> svg {
width: var(--connection-icon-size);
height: var(--connection-icon-size);
fill: var(--text-color-tertiary);
opacity: 0.6;
}
&.connected,
&.ready {
--connection-icon-size: 0.82rem;
left: 0.4rem;
> svg {
fill: var(--accent-color-secondary);
opacity: 1;
}
}
> svg {
width: var(--connection-icon-size);
height: var(--connection-icon-size);
fill: var(--text-color-tertiary);
}
}
> .icon {
margin-right: 0.25rem;
Expand Down
20 changes: 17 additions & 3 deletions src/model/Api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export class Api {
// Class members.
// ------------------------------------------------------

// The associated tab id for this api instance.
#tabId: number;

// The supplied chain id.
#chainId: ChainId;

Expand All @@ -31,6 +34,10 @@ export class Api {
// Getters.
// ------------------------------------------------------

get tabId() {
return this.#tabId;
}

get chainId() {
return this.#chainId;
}
Expand All @@ -51,9 +58,10 @@ export class Api {
// Constructor
// ------------------------------------------------------

constructor(chainId: ChainId, endpoint: string) {
this.#rpcEndpoint = endpoint;
constructor(tabId: number, chainId: ChainId, endpoint: string) {
this.#tabId = tabId;
this.#chainId = chainId;
this.#rpcEndpoint = endpoint;
}

// ------------------------------------------------------
Expand Down Expand Up @@ -109,7 +117,11 @@ export class Api {
err?: string;
}
) {
const detail: APIStatusEventDetail = { event, chainId: this.chainId };
const detail: APIStatusEventDetail = {
event,
tabId: this.tabId,
chainId: this.chainId,
};
if (options?.err) {
detail['err'] = options.err;
}
Expand All @@ -128,6 +140,7 @@ export class Api {
'disconnected',
'ready',
'error',
'destroyed',
];
if (eventStatus.includes(status)) {
return status as EventStatus;
Expand All @@ -151,5 +164,6 @@ export class Api {
this.unsubscribeProvider();
this.provider?.disconnect();
await this.api?.disconnect();
this.dispatchEvent(this.ensureEventStatus('destroyed'));
}
}
3 changes: 2 additions & 1 deletion src/model/Api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import type { ChainId } from 'config/networks';

export type ApiStatus = 'connecting' | 'connected' | 'disconnected' | 'ready';

export type EventStatus = ApiStatus | 'error';
export type EventStatus = ApiStatus | 'error' | 'destroyed';

export interface APIStatusEventDetail {
event: EventStatus;
tabId: number;
chainId: ChainId;
err?: string;
}
14 changes: 8 additions & 6 deletions src/screens/Default/Connect/ChainList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ export const ChainList = () => {
// Filter chains based on applied tags.
const appliedTags: [ChainId, TagItem][] = getAppliedTags(activeTabId);

let filtered = Object.fromEntries(
Object.entries(results).filter(([chain]) =>
appliedTags.some(([tagId]) =>
getTagsForChain(chain as ChainId).includes(tagId)
let filtered = appliedTags.length
? Object.fromEntries(
Object.entries(results).filter(([chain]) =>
appliedTags.some(([tagId]) =>
getTagsForChain(chain as ChainId).includes(tagId)
)
)
)
)
);
: results;

// Filter chains based on search term.
const searchTerm = getSearchTerm(activeTabId);
Expand Down
17 changes: 6 additions & 11 deletions src/screens/Default/Connect/ChainListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import type { TagId } from 'contexts/Tags/types';
import { type ChainId } from 'config/networks';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ConnectMenu } from './ConnectMenu';
import { useSettings } from 'contexts/Settings';
import { useTabs } from 'contexts/Tabs';

export interface ChainListItemProps {
Expand All @@ -22,11 +21,10 @@ export interface ChainListItemProps {
}

export const ChainListItem = ({ chainId, name }: ChainListItemProps) => {
const { openMenu } = useMenu();
const { autoTabNaming } = useSettings();
const { openMenu, closeMenu } = useMenu();
const { tags, getTagsForChain, addChainToTag, removeChainFromTag } =
useTags();
const { activeTabId, getAutoTabName, renameTab, connectTab } = useTabs();
const { activeTabId, connectTab } = useTabs();
const chainTags = getTagsForChain(chainId);

// Lazily load the icon for the chain.
Expand All @@ -39,14 +37,11 @@ export const ChainListItem = ({ chainId, name }: ChainListItemProps) => {
);

// Handle tag provider select. Connect to chain on successful selection.
const handleOnProviderSelect = (endpoint: string) => {
const handleOnProviderSelect = (provider: string) => {
// Update tab data and connect to Api instance.
connectTab(activeTabId, chainId, endpoint);

// If auto-renaming is turned on, rename tab to automated name.
if (autoTabNaming) {
renameTab(activeTabId, getAutoTabName(chainId));
}
connectTab(activeTabId, chainId, provider);
// Close menu.
closeMenu();
};

// Handle tag menu item select. Either add or remove a tag configs.
Expand Down
4 changes: 2 additions & 2 deletions src/screens/Default/Connect/ConnectMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const ConnectMenu = ({
onSelect,
}: {
chainId: ChainId;
onSelect: (providerId: string) => void;
onSelect: (provider: string) => void;
}) => {
// Provider search term.
const [providerSearchTerm, setProviderSearchTerm] = useState<string>('');
Expand Down Expand Up @@ -53,7 +53,7 @@ export const ConnectMenu = ({
<ListWrapper>
{filteredProviders.map(([name, url], index) => (
<li key={`provider_context_item_${index}`}>
<button onClick={() => onSelect(url)} />
<button onClick={() => onSelect(name)} />

<div className="inner">
<div className="none"></div>
Expand Down
2 changes: 1 addition & 1 deletion src/svg/Disconnect.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d4df7c1

Please sign in to comment.