Skip to content

Commit

Permalink
frontend api network switching
Browse files Browse the repository at this point in the history
  • Loading branch information
RetricSu committed Jan 20, 2025
1 parent 05f5faf commit 56aae0e
Show file tree
Hide file tree
Showing 18 changed files with 216 additions and 143 deletions.
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ TESTNET_WS_RPC_URL = "wss://testnet.ckb.dev/ws"
MAINNET_HTTP_RPC_URL = "https://mainnet.ckb.dev"
TESTNET_HTTP_RPC_URL = "https://testnet.ckb.dev"

API_HTTP_PORT = 3000
API_WS_PORT = 3001
API_TESTNET_PORT = 3000
API_MAINNET_PORT = 3001
ALLOW_ORIGIN = "*" # for multiple allow origins, separate with , eg: "domain1.com,domain2.com"
4 changes: 4 additions & 0 deletions frontend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
VITE_TESTNET_HTTP_API_URL = http://localhost:3000
VITE_TESTNET_WS_API_URL = ws://localhost:3000
VITE_MAINNET_HTTP_API_URL = http://localhost:3001
VITE_MAINNET_WS_API_URL = ws://localhost:3001
44 changes: 25 additions & 19 deletions frontend/src/components/elevator/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,42 @@ import ElevatorPanel from "./panel";
import ElevatorHeader from "./header";
import { useAtomValue } from "jotai";
import { ChainTheme, chainThemeAtom } from "../../states/atoms";
import { TipBlockResponse } from "../../service/type";
import { Network, TipBlockResponse } from "../../service/type";

export default function Elevator() {
const chainTheme = useAtomValue(chainThemeAtom);
const [tipBlock, setTipBlock] = useState<TipBlockResponse>(undefined);
const [doorClosing, setDoorClosing] = useState(false);

// subscribe to new block
// todo: need unscribe when component unmount
const subNewBlock = async () => {
ChainService.subscribeNewBlock((newBlock) => {
if (newBlock.blockHeader) {
setTipBlock((prev) => {
if (
prev == null ||
prev?.blockHeader?.block_number <
newBlock.blockHeader.block_number
) {
return newBlock || undefined;
} else {
return prev;
}
});
}
const network =
chainTheme === ChainTheme.mainnet
? Network.Mainnet
: Network.Testnet;
const chainService = new ChainService(network);
chainService.wsClient.connect(() => {
chainService.subscribeNewBlock((newBlock) => {
if (newBlock.blockHeader) {
setTipBlock((prev) => {
if (
prev == null ||
prev?.blockHeader?.block_number <
newBlock.blockHeader.block_number
) {
return newBlock || undefined;
} else {
return prev;
}
});
}
});
});
};
useEffect(() => {
ChainService.wsClient.connect(() => {
subNewBlock();
});
}, []);
subNewBlock();
}, [chainTheme]);

const bgElevatorFrame =
chainTheme === ChainTheme.mainnet
Expand Down
106 changes: 58 additions & 48 deletions frontend/src/components/pool/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { FunctionalComponent } from "preact";
import { useEffect, useState } from "preact/hooks";
import { ChainService } from "../../service/api";
import QueueComponent from "./queue";
import { Transaction } from "../../service/type";
import { Network, Transaction } from "../../service/type";
import { useAtomValue } from "jotai";
import { ChainTheme, chainThemeAtom } from "../../states/atoms";

type TxStatus = "pending" | "proposing" | "proposed" | "committed" | "none";

Expand All @@ -13,6 +15,8 @@ interface TxChange {
}

const Pool: FunctionalComponent = () => {
const chainTheme = useAtomValue(chainThemeAtom);

const [initProposedTxs, setInitProposedTxs] = useState<
Transaction[] | null
>(null);
Expand Down Expand Up @@ -41,62 +45,68 @@ const Pool: FunctionalComponent = () => {
const [stateChanges, setStateChanges] = useState<TxChange[]>([]); // 存储状态变化信息

const subNewSnapshot = async () => {
ChainService.subscribeNewSnapshot((newSnapshot) => {
const {
tipCommittedTransactions,
pendingTransactions,
proposingTransactions,
proposedTransactions,
} = newSnapshot;

if (initCommittedTxs == null) {
setInitCommittedTxs(tipCommittedTransactions);
}
if (initProposedTxs == null) {
setInitProposedTxs(proposedTransactions);
}
if (initPendingTxs == null) {
setInitPendingTxs(pendingTransactions);
}
if (initProposingTxs == null) {
setInitProposingTxs(proposingTransactions);
}

// 拿到所有tx
const newTxs = {
pending: pendingTransactions,
proposing: proposingTransactions,
proposed: proposedTransactions,
committed: tipCommittedTransactions,
};

// diff 数据
if (previousTxs) {
const changes = detectStateChanges(previousTxs, newTxs);
setStateChanges(changes);
}
const network =
chainTheme === ChainTheme.mainnet
? Network.Mainnet
: Network.Testnet;
const chainService = new ChainService(network);
chainService.wsClient.connect(() => {
chainService.subscribeNewSnapshot((newSnapshot) => {
const {
tipCommittedTransactions,
pendingTransactions,
proposingTransactions,
proposedTransactions,
} = newSnapshot;

if (initCommittedTxs == null) {
setInitCommittedTxs(tipCommittedTransactions);
}
if (initProposedTxs == null) {
setInitProposedTxs(proposedTransactions);
}
if (initPendingTxs == null) {
setInitPendingTxs(pendingTransactions);
}
if (initProposingTxs == null) {
setInitProposingTxs(proposingTransactions);
}

// 更新历史记录
// 使用函数式更新
setPreviousTxs((prev) => {
if (prev) {
const changes = detectStateChanges(prev, newTxs);
// 拿到所有tx
const newTxs = {
pending: pendingTransactions,
proposing: proposingTransactions,
proposed: proposedTransactions,
committed: tipCommittedTransactions,
};

// diff 数据
if (previousTxs) {
const changes = detectStateChanges(previousTxs, newTxs);
setStateChanges(changes);
}
return newTxs;
});

setPendingTxs(pendingTransactions);
setProposingTxs(proposingTransactions);
setProposedTxs(proposedTransactions);
setCommittedTxs(tipCommittedTransactions);
// 更新历史记录
// 使用函数式更新
setPreviousTxs((prev) => {
if (prev) {
const changes = detectStateChanges(prev, newTxs);
setStateChanges(changes);
}
return newTxs;
});

setPendingTxs(pendingTransactions);
setProposingTxs(proposingTransactions);
setProposedTxs(proposedTransactions);
setCommittedTxs(tipCommittedTransactions);
});
});
};

useEffect(() => {
ChainService.wsClient.connect();
subNewSnapshot();
}, []);
}, [chainTheme]);

// 状态变化检测
const detectStateChanges = (
Expand Down
71 changes: 41 additions & 30 deletions frontend/src/service/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,87 +4,100 @@ import { WsApiService } from "./ws";
import {
BlockHeader,
ChainSnapshot,
Network,
TipBlockResponse,
Transaction,
} from "./type";
import { Config } from "./config";

export class ChainService {
static wsClient: WsApiService = new WsApiService({});
wsClient: WsApiService;
httpClient: HttpApiService;

static async getPendingTransactions(): Promise<Transaction[]> {
constructor(network: Network) {
this.wsClient = new WsApiService({
url:
network === Network.Mainnet
? Config.mainnetApiWsUrl
: Config.testnetApiWsUrl,
});
this.httpClient = new HttpApiService(
network === Network.Mainnet
? Config.mainnetApiHttpUrl
: Config.testnetApiHttpUrl,
);
}

async getPendingTransactions(): Promise<Transaction[]> {
const response =
await HttpApiService.get<Transaction[]>("/pending-txs");
await this.httpClient.get<Transaction[]>("/pending-txs");
return response.data || [];
}

static async getProposingTransactions(): Promise<Transaction[]> {
async getProposingTransactions(): Promise<Transaction[]> {
const response =
await HttpApiService.get<Transaction[]>("/proposing-txs");
await this.httpClient.get<Transaction[]>("/proposing-txs");
return response.data || [];
}

static async getRejectedTransactions(): Promise<Transaction[]> {
async getRejectedTransactions(): Promise<Transaction[]> {
const response =
await HttpApiService.get<Transaction[]>("/rejected-txs");
await this.httpClient.get<Transaction[]>("/rejected-txs");
return response.data || [];
}

static async getProposedTransactions(): Promise<Transaction[]> {
async getProposedTransactions(): Promise<Transaction[]> {
const response =
await HttpApiService.get<Transaction[]>("/proposed-txs");
await this.httpClient.get<Transaction[]>("/proposed-txs");
return response.data || [];
}

static async getProposedTransactionsByBlock(
async getProposedTransactionsByBlock(
blockHash: Hex,
): Promise<Transaction[]> {
const response = await HttpApiService.get<Transaction[]>(
const response = await this.httpClient.get<Transaction[]>(
`/proposed-txs-by-block?blockHash=${blockHash}`,
);
return response.data || [];
}

static async getCommittedTransactions(
blockHash: Hex,
): Promise<Transaction[]> {
const response = await HttpApiService.get<Transaction[]>(
async getCommittedTransactions(blockHash: Hex): Promise<Transaction[]> {
const response = await this.httpClient.get<Transaction[]>(
`/committed-txs?blockHash=${blockHash}`,
);
return response.data || [];
}

static async getBlockHeader(blockHash: Hex): Promise<BlockHeader | null> {
const response = await HttpApiService.get<BlockHeader>(
async getBlockHeader(blockHash: Hex): Promise<BlockHeader | null> {
const response = await this.httpClient.get<BlockHeader>(
`/block-header?blockHash=${blockHash}`,
);
return response.data || null;
}
5;
static async getTipBlockHeader(): Promise<BlockHeader | null> {

async getTipBlockHeader(): Promise<BlockHeader | null> {
const response =
await HttpApiService.get<BlockHeader>("/tip-block-header");
await this.httpClient.get<BlockHeader>("/tip-block-header");
return response.data || null;
}

static async getTipBlockTransactions(): Promise<TipBlockResponse | null> {
async getTipBlockTransactions(): Promise<TipBlockResponse | null> {
const response =
await HttpApiService.get<TipBlockResponse>("/tip-block-txs");
await this.httpClient.get<TipBlockResponse>("/tip-block-txs");
return response.data || null;
}

static async getAllBlockHeaders(
async getAllBlockHeaders(
order: "ASC" | "DESC" = "DESC",
limit: number = 20,
): Promise<BlockHeader[]> {
const response = await HttpApiService.get<BlockHeader[]>(
const response = await this.httpClient.get<BlockHeader[]>(
`/all-block-headers?order=${order}&limit=${limit}`,
);
return response.data || [];
}

static async subscribeNewSnapshot(
onmessage: (_data: ChainSnapshot) => void,
) {
async subscribeNewSnapshot(onmessage: (_data: ChainSnapshot) => void) {
this.wsClient.send("newSnapshot", {});

this.wsClient.on("newSnapshot", (message: any) => {
Expand All @@ -93,9 +106,7 @@ export class ChainService {
});
}

static async subscribeNewBlock(
onmessage: (_data: TipBlockResponse) => void,
) {
async subscribeNewBlock(onmessage: (_data: TipBlockResponse) => void) {
this.wsClient.send("newBlock", {});

this.wsClient.on("newBlock", (message: any) => {
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/service/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export const Config = {
apiHttpUrl: import.meta.env.VITE_HTTP_API_URL,
apiWsUrl: import.meta.env.VITE_WS_API_URL,
testnetApiHttpUrl: import.meta.env.VITE_TESTNET_HTTP_API_URL,
testnetApiWsUrl: import.meta.env.VITE_TESTNET_WS_API_URL,
mainnetApiHttpUrl: import.meta.env.VITE_MAINNET_HTTP_API_URL,
mainnetApiWsUrl: import.meta.env.VITE_MAINNET_WS_API_URL,
};
Loading

0 comments on commit 56aae0e

Please sign in to comment.