-
Notifications
You must be signed in to change notification settings - Fork 439
feat: earn redesign #8831
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
base: x
Are you sure you want to change the base?
feat: earn redesign #8831
Changes from 85 commits
6c1372f
ea12dc9
df689ce
8ee45d7
b002d9a
cc62c9a
3758963
420f34f
3c6e385
6813b83
82372d8
78f0653
892f895
06d6a3b
8d54ae4
1510ca6
440263d
6e3b0df
1f49131
11f320e
710d59c
062003f
d0efd9b
e8e75da
5ce802c
9b117e2
0837483
0b5e4ee
e4405bb
cb89224
79d0234
98d0237
b9fa1e7
c142ee6
1cd756d
649313d
0f1b495
648f134
ee21a43
cb2765e
451f70c
0a5cb7e
e3183fd
4243162
ea0dde5
e97340e
4a801a0
5de2e24
d06f38c
118d5e9
bbb4f30
1351b9f
ff4ffec
8cdfc98
dfed9d1
a96b17a
cda0375
06c5939
83d266d
68ec1b4
f1a3fc3
b620084
749cb0b
d80b299
f5e5ec8
8b0de1c
07b4535
6efa192
77465ea
9f5f1f9
643aa22
2c8bc64
84db51d
4f6086d
1fccfb3
f52e71e
f08e362
fa2300a
a77ec0f
13be888
23f70b6
49dfcb3
c1c74f7
75ef05b
df3c6e4
a84502e
7e8d6ed
b622012
56746ac
24fd9d0
b17e359
022fb3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ import type { | |
| ECheckAmountActionType, | ||
| EInternalDappEnum, | ||
| IAllowanceOverview, | ||
| IApyHistoryResponse, | ||
| IAvailableAsset, | ||
| IBabylonPortfolioItem, | ||
| IBuildPermit2ApproveSignDataParams, | ||
|
|
@@ -45,14 +46,18 @@ import type { | |
| IEarnAccountResponse, | ||
| IEarnAccountToken, | ||
| IEarnAccountTokenResponse, | ||
| IEarnAirdropInvestmentItemV2, | ||
| IEarnBabylonTrackingItem, | ||
| IEarnEstimateAction, | ||
| IEarnEstimateFeeResp, | ||
| IEarnFAQList, | ||
| IEarnInvestmentItem, | ||
| IEarnInvestmentItemV2, | ||
| IEarnManagePageResponse, | ||
| IEarnPermit2ApproveSignData, | ||
| IEarnRegisterSignMessageResponse, | ||
| IEarnSummary, | ||
| IEarnSummaryV2, | ||
| IEarnUnbondingDelegationList, | ||
| IGetPortfolioParams, | ||
| IRecommendAsset, | ||
|
|
@@ -112,6 +117,20 @@ interface IAvailableAssetsResponse { | |
| data: { assets: IAvailableAsset[] }; | ||
| } | ||
|
|
||
| interface IAvailableAssetsResponseV2 { | ||
| code: string; | ||
| message?: string; | ||
| data: { | ||
| assets: { | ||
| type: 'normal' | 'airdrop'; | ||
| networkId: string; | ||
| provider: string; | ||
| symbol: string; | ||
| vault?: string; | ||
| }[]; | ||
| }; | ||
| } | ||
|
|
||
| @backgroundClass() | ||
| class ServiceStaking extends ServiceBase { | ||
| constructor({ backgroundApi }: { backgroundApi: any }) { | ||
|
|
@@ -163,6 +182,26 @@ class ServiceStaking extends ServiceBase { | |
| return response.data.data; | ||
| } | ||
|
|
||
| @backgroundMethod() | ||
| async getEarnSummaryV2({ | ||
| accountAddress, | ||
| networkId, | ||
| }: { | ||
| accountAddress: string; | ||
| networkId: string; | ||
| }): Promise<IEarnSummaryV2> { | ||
| const client = await this.getClient(EServiceEndpointEnum.Earn); | ||
| const response = await client.get<{ | ||
| data: IEarnSummaryV2; | ||
| }>('/earn/v2/rebate', { | ||
| params: { | ||
| accountAddress, | ||
| networkId, | ||
| }, | ||
| }); | ||
| return response.data.data; | ||
| } | ||
|
|
||
| @backgroundMethod() | ||
| public async fetchLocalStakingHistory({ | ||
| accountId, | ||
|
|
@@ -623,6 +662,32 @@ class ServiceStaking extends ServiceBase { | |
| return result as unknown as IStakeEarnDetail; | ||
| } | ||
|
|
||
| @backgroundMethod() | ||
| async getManagePage(params: { | ||
| networkId: string; | ||
| provider: string; | ||
| symbol: string; | ||
| vault?: string; | ||
| accountAddress: string; | ||
| publicKey?: string; | ||
| }) { | ||
| const client = await this.getClient(EServiceEndpointEnum.Earn); | ||
| const requestParams = { | ||
| networkId: params.networkId, | ||
| provider: params.provider.toLowerCase(), | ||
| symbol: params.symbol, | ||
| accountAddress: params.accountAddress, | ||
| ...(params.vault && { vault: params.vault }), | ||
| ...(params.publicKey && { publicKey: params.publicKey }), | ||
| }; | ||
|
|
||
| const resp = await client.get<{ data: IEarnManagePageResponse }>( | ||
| '/earn/v1/manage-page', | ||
| { params: requestParams }, | ||
| ); | ||
| return resp.data.data; | ||
| } | ||
|
|
||
| @backgroundMethod() | ||
| async getTransactionConfirmation(params: { | ||
| networkId: string; | ||
|
|
@@ -1028,6 +1093,44 @@ class ServiceStaking extends ServiceBase { | |
| return response.data.data; | ||
| } | ||
|
|
||
| @backgroundMethod() | ||
| async fetchInvestmentDetailV2(params: { | ||
| publicKey?: string | undefined; | ||
| vault?: string | undefined; | ||
| accountAddress: string; | ||
| networkId: string; | ||
| provider: string; | ||
| symbol: string; | ||
| }) { | ||
| const client = await this.getClient(EServiceEndpointEnum.Earn); | ||
|
|
||
| const response = await client.get<{ data: IEarnInvestmentItemV2 }>( | ||
| `/earn/v2/investment/detail`, | ||
| { params }, | ||
| ); | ||
|
|
||
| return response.data.data; | ||
| } | ||
|
|
||
| @backgroundMethod() | ||
| async fetchAirdropInvestmentDetail(params: { | ||
| publicKey?: string | undefined; | ||
| vault?: string | undefined; | ||
| accountAddress: string; | ||
| networkId: string; | ||
| provider: string; | ||
| symbol: string; | ||
| }) { | ||
| const client = await this.getClient(EServiceEndpointEnum.Earn); | ||
|
|
||
| const response = await client.get<{ data: IEarnAirdropInvestmentItemV2 }>( | ||
| `/earn/v1/investment/airdrop-detail`, | ||
| { params }, | ||
| ); | ||
|
|
||
| return response.data.data; | ||
| } | ||
|
|
||
|
Comment on lines
+1097
to
+1133
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lowercase provider before calling the v2 detail APIs. Both Apply this diff: @backgroundMethod()
async fetchInvestmentDetailV2(params: {
publicKey?: string | undefined;
vault?: string | undefined;
accountAddress: string;
networkId: string;
provider: string;
symbol: string;
}) {
const client = await this.getClient(EServiceEndpointEnum.Earn);
- const response = await client.get<{ data: IEarnInvestmentItemV2 }>(
- `/earn/v2/investment/detail`,
- { params },
- );
+ const requestParams = {
+ ...params,
+ provider: params.provider.toLowerCase(),
+ };
+
+ const response = await client.get<{ data: IEarnInvestmentItemV2 }>(
+ `/earn/v2/investment/detail`,
+ { params: requestParams },
+ );
return response.data.data;
}
@backgroundMethod()
async fetchAirdropInvestmentDetail(params: {
publicKey?: string | undefined;
vault?: string | undefined;
accountAddress: string;
networkId: string;
provider: string;
symbol: string;
}) {
const client = await this.getClient(EServiceEndpointEnum.Earn);
- const response = await client.get<{ data: IEarnAirdropInvestmentItemV2 }>(
- `/earn/v1/investment/airdrop-detail`,
- { params },
- );
+ const response = await client.get<{ data: IEarnAirdropInvestmentItemV2 }>(
+ `/earn/v1/investment/airdrop-detail`,
+ {
+ params: {
+ ...params,
+ provider: params.provider.toLowerCase(),
+ },
+ },
+ );
return response.data.data;
}🤖 Prompt for AI Agents |
||
| _getAvailableAssets = memoizee( | ||
| async ({ type }: { type?: EAvailableAssetsTypeEnum }) => { | ||
| const client = await this.getRawDataClient(EServiceEndpointEnum.Earn); | ||
|
|
@@ -1062,6 +1165,21 @@ class ServiceStaking extends ServiceBase { | |
| void this._getAvailableAssets.clear(); | ||
| } | ||
|
|
||
| @backgroundMethod() | ||
| async getAvailableAssetsV2() { | ||
| const client = await this.getRawDataClient(EServiceEndpointEnum.Earn); | ||
| const resp = await client.get< | ||
| IAvailableAssetsResponseV2, | ||
| IAxiosResponse<IAvailableAssetsResponseV2> | ||
| >(`/earn/v2/available-assets`); | ||
|
|
||
| this.handleServerError({ | ||
| ...resp.data, | ||
| requestId: resp.$requestId, | ||
| }); | ||
| return resp.data.data.assets; | ||
| } | ||
|
|
||
| handleServerError(data: { | ||
| code?: string | number; | ||
| message?: string; | ||
|
|
@@ -1322,11 +1440,11 @@ class ServiceStaking extends ServiceBase { | |
| } | ||
|
|
||
| @backgroundMethod() | ||
| fetchEarnHomePageData() { | ||
| return this._fetchEarnHomePageData(); | ||
| fetchEarnHomePageBannerList() { | ||
| return this._fetchEarnHomePageBannerList(); | ||
| } | ||
|
|
||
| _fetchEarnHomePageData = memoizee( | ||
| _fetchEarnHomePageBannerList = memoizee( | ||
| async () => { | ||
| const client = await this.getClient(EServiceEndpointEnum.Utility); | ||
| const res = await client.get<{ data: IDiscoveryBanner[] }>( | ||
|
|
@@ -1705,6 +1823,39 @@ class ServiceStaking extends ServiceBase { | |
| return null; | ||
| } | ||
| } | ||
|
|
||
| @backgroundMethod() | ||
| async getApyHistory(params: { | ||
| networkId: string; | ||
| provider: string; | ||
| symbol: string; | ||
| vault?: string; | ||
| }) { | ||
| const client = await this.getClient(EServiceEndpointEnum.Earn); | ||
| const requestParams: { | ||
| networkId: string; | ||
| provider: string; | ||
| symbol: string; | ||
| vault?: string; | ||
| } = { | ||
| networkId: params.networkId, | ||
| provider: params.provider.toLowerCase(), | ||
| symbol: params.symbol, | ||
| }; | ||
|
|
||
| if (params.vault) { | ||
| requestParams.vault = params.vault; | ||
| } | ||
|
|
||
| const response = await client.get<IApyHistoryResponse>( | ||
| '/earn/v1/apy/history', | ||
| { | ||
| params: requestParams, | ||
| }, | ||
| ); | ||
|
|
||
| return response.data.data; | ||
| } | ||
| } | ||
|
|
||
| export default ServiceStaking; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; | ||
|
|
||
| import { View } from 'react-native'; | ||
| import WebView from 'react-native-webview'; | ||
|
|
||
| import { Stack } from '@onekeyhq/components'; | ||
|
|
||
| import { useChartConfig } from './hooks/useChartConfig'; | ||
| import { generateChartHTML } from './utils/htmlTemplate'; | ||
|
|
||
| import type { IChartMessage, ILightweightChartProps } from './types'; | ||
| import type { WebViewMessageEvent } from 'react-native-webview'; | ||
|
|
||
| export function LightweightChart({ | ||
| data, | ||
| height, | ||
| lineColor, | ||
| topColor, | ||
| bottomColor, | ||
| onHover, | ||
| }: ILightweightChartProps) { | ||
| const webViewRef = useRef<WebView>(null); | ||
| const [webViewReady, setWebViewReady] = useState(false); | ||
|
|
||
| const chartConfig = useChartConfig({ | ||
| data, | ||
| lineColor, | ||
| topColor, | ||
| bottomColor, | ||
| }); | ||
| const htmlContent = useMemo( | ||
| () => generateChartHTML(chartConfig), | ||
| [chartConfig], | ||
| ); | ||
|
|
||
| const handleMessage = useCallback( | ||
| (event: WebViewMessageEvent) => { | ||
| try { | ||
| const message = JSON.parse(event.nativeEvent.data) as IChartMessage; | ||
|
|
||
| if (message.type === 'ready') { | ||
| setWebViewReady(true); | ||
| } else if (message.type === 'hover' && onHover) { | ||
| onHover({ | ||
| time: message.time ? Number(message.time) : undefined, | ||
| price: message.price ? Number(message.price) : undefined, | ||
| x: message.x, | ||
| y: message.y, | ||
| }); | ||
| } | ||
| } catch (error) { | ||
| console.error( | ||
| 'LightweightChart: Error parsing WebView message:', | ||
| error, | ||
| ); | ||
| } | ||
| }, | ||
| [onHover], | ||
| ); | ||
|
|
||
| // Update chart when data changes | ||
| useEffect(() => { | ||
| if (webViewReady && webViewRef.current) { | ||
| const updateScript = ` | ||
| (function() { | ||
| const newConfig = ${JSON.stringify(chartConfig)}; | ||
| if (window.series) { | ||
| window.series.setData(newConfig.data); | ||
| window.chart.timeScale().fitContent(); | ||
| } | ||
| })(); | ||
| true; | ||
| `; | ||
| webViewRef.current.injectJavaScript(updateScript); | ||
| } | ||
| }, [chartConfig, webViewReady]); | ||
|
Comment on lines
+62
to
+76
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainCheck serialized config size and injection performance. Line 66 serializes the entire Consider:
🏁 Script executed: #!/bin/bash
# Check typical usage patterns of LightweightChart to assess data size
rg -A 5 -B 2 '<LightweightChart' --type=tsx --type=tsLength of output: 27 Length of output: 812 🏁 Script executed: #!/bin/bash
# Look for error handling patterns around injectJavaScript
rg -n 'injectJavaScript' -C 4 -g '*.ts' -g '*.tsx'Length of output: 7246 🏁 Script executed: #!/bin/bash
# Check if debounce or throttle is used anywhere in the component file
rg -n 'debounce|throttle|memo|useMemo|useCallback' packages/kit/src/components/LightweightChart/LightweightChart.native.tsxLength of output: 150 🏁 Script executed: #!/bin/bash
# Examine the full LightweightChart.native.tsx file around the useEffect
cat packages/kit/src/components/LightweightChart/LightweightChart.native.tsx | head -100Length of output: 2758 Add error handling for Line 74 lacks a try-catch wrapper. Other For JSON serialization and update frequency: the component already uses 🤖 Prompt for AI Agents |
||
|
|
||
| return ( | ||
| <Stack position="relative" height={height} width="100%"> | ||
| <View style={{ flex: 1 }}> | ||
| <WebView | ||
| ref={webViewRef} | ||
| source={{ html: htmlContent }} | ||
| onMessage={handleMessage} | ||
| scrollEnabled={false} | ||
| showsVerticalScrollIndicator={false} | ||
| showsHorizontalScrollIndicator={false} | ||
| bounces={false} | ||
| style={{ backgroundColor: 'transparent' }} | ||
| androidLayerType="hardware" | ||
| originWhitelist={['*']} | ||
| allowFileAccess={false} | ||
| allowUniversalAccessFromFileURLs={false} | ||
| mixedContentMode="never" | ||
| /> | ||
| </View> | ||
| </Stack> | ||
| ); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.