mirror of
https://github.com/status-im/status-web.git
synced 2026-08-31 06:11:10 +00:00
Add remote config functionality (#784)
Co-authored-by: Felicio <felicio@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@status-im/wallet': patch
|
||||
'wallet': patch
|
||||
'api': patch
|
||||
---
|
||||
|
||||
remote config env
|
||||
@@ -16,9 +16,7 @@
|
||||
# – https://vercel.com/docs/cli/env#exporting-development-environment-variables
|
||||
|
||||
INFURA_API_KEY=""
|
||||
ALCHEMY_API_KEY=""
|
||||
ALCHEMY_API_KEYS=""
|
||||
COINGECKO_API_KEY=""
|
||||
CRYPTOCOMPARE_API_KEY=""
|
||||
CRYPTOCOMPARE_API_KEYS=""
|
||||
MERCURYO_SECRET_KEY=""
|
||||
|
||||
@@ -18,9 +18,9 @@ export const envSchema = z.object({
|
||||
.optional(),
|
||||
INFURA_API_KEY: z.string(),
|
||||
MERCURYO_SECRET_KEY: z.string(),
|
||||
ALCHEMY_API_KEY: z.string(),
|
||||
ALCHEMY_API_KEYS: z.string(),
|
||||
COINGECKO_API_KEY: z.string(),
|
||||
CRYPTOCOMPARE_API_KEY: z.string(),
|
||||
CRYPTOCOMPARE_API_KEYS: z.string(),
|
||||
})
|
||||
|
||||
const result = envSchema.safeParse(process.env)
|
||||
|
||||
+2
-2
@@ -18,9 +18,9 @@
|
||||
INFURA_API_KEY=""
|
||||
NEXT_PUBLIC_INFURA_API_KEY=""
|
||||
MERCURYO_SECRET_KEY=""
|
||||
ALCHEMY_API_KEY=""
|
||||
ALCHEMY_API_KEYS=""
|
||||
COINGECKO_API_KEY=""
|
||||
CRYPTOCOMPARE_API_KEY=""
|
||||
CRYPTOCOMPARE_API_KEYS=""
|
||||
NEXT_PUBLIC_FEATURE_FLAG_WATCHED_ADDRESSES="true"
|
||||
#VERCEL_WEB_ANALYTICS_DISABLE_LOGS="true"
|
||||
#NEXT_PRIVATE_DEBUG_CACHE=1
|
||||
|
||||
@@ -17,9 +17,9 @@ export const envSchema = z.object({
|
||||
])
|
||||
.optional(),
|
||||
INFURA_API_KEY: z.string(),
|
||||
ALCHEMY_API_KEY: z.string(),
|
||||
ALCHEMY_API_KEYS: z.string(),
|
||||
COINGECKO_API_KEY: z.string(),
|
||||
CRYPTOCOMPARE_API_KEY: z.string(),
|
||||
CRYPTOCOMPARE_API_KEYS: z.string(),
|
||||
PORT: z.coerce.number().optional(),
|
||||
MERCURYO_SECRET_KEY: z.string(),
|
||||
})
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export const QUERY_STALE_TIME_MS = 15 * 1000 // 15 seconds
|
||||
export const QUERY_GC_TIME_MS = 60 * 60 * 1000 // 1 hour
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useInfiniteQuery } from '@tanstack/react-query'
|
||||
|
||||
import { QUERY_GC_TIME_MS, QUERY_STALE_TIME_MS } from './constants'
|
||||
import { useConfigEnv } from './use-config-env'
|
||||
|
||||
import type { Activity, NetworkType } from '@status-im/wallet/data'
|
||||
|
||||
@@ -57,6 +57,7 @@ const getTransfers = async (
|
||||
}
|
||||
|
||||
export const useActivities = ({ address }: Props) => {
|
||||
const { data: configEnv } = useConfigEnv()
|
||||
const searchParams = new URLSearchParams(window.location.search)
|
||||
|
||||
const networks = searchParams.get('networks')?.split(',') ?? ['ethereum']
|
||||
@@ -87,7 +88,7 @@ export const useActivities = ({ address }: Props) => {
|
||||
},
|
||||
|
||||
initialPageParam: {},
|
||||
staleTime: QUERY_STALE_TIME_MS,
|
||||
gcTime: QUERY_GC_TIME_MS,
|
||||
staleTime: configEnv.staleTimeMs,
|
||||
gcTime: configEnv.gcTimeMs,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
|
||||
import { QUERY_GC_TIME_MS, QUERY_STALE_TIME_MS } from './constants'
|
||||
import { useConfigEnv } from './use-config-env'
|
||||
|
||||
import type { ApiOutput } from '@status-im/wallet/data'
|
||||
import type { UseQueryResult } from '@tanstack/react-query'
|
||||
@@ -16,6 +16,7 @@ const useAssets = (
|
||||
props: Props,
|
||||
): UseQueryResult<ApiOutput['assets']['all'], Error> => {
|
||||
const { address, isWalletLoading } = props
|
||||
const { data: configEnv } = useConfigEnv()
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['assets', address],
|
||||
@@ -53,8 +54,8 @@ const useAssets = (
|
||||
return body.result.data.json
|
||||
},
|
||||
enabled: !!address && !isWalletLoading,
|
||||
staleTime: QUERY_STALE_TIME_MS,
|
||||
gcTime: QUERY_GC_TIME_MS,
|
||||
staleTime: configEnv.staleTimeMs,
|
||||
gcTime: configEnv.gcTimeMs,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useInfiniteQuery } from '@tanstack/react-query'
|
||||
|
||||
import { QUERY_GC_TIME_MS, QUERY_STALE_TIME_MS } from './constants'
|
||||
import { useConfigEnv } from './use-config-env'
|
||||
|
||||
import type { NetworkType } from '@status-im/wallet/data'
|
||||
|
||||
@@ -75,6 +75,7 @@ const getCollectibles = async (
|
||||
|
||||
const useCollectibles = (props: Props) => {
|
||||
const { address, isWalletLoading } = props
|
||||
const { data: configEnv } = useConfigEnv()
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search)
|
||||
const search = searchParams.get('search') ?? undefined
|
||||
@@ -116,8 +117,8 @@ const useCollectibles = (props: Props) => {
|
||||
lastPage.hasMore ? lastPage.nextPage : undefined,
|
||||
initialPageParam: 0,
|
||||
enabled: !!address && !isWalletLoading,
|
||||
staleTime: QUERY_STALE_TIME_MS,
|
||||
gcTime: QUERY_GC_TIME_MS,
|
||||
staleTime: configEnv.staleTimeMs,
|
||||
gcTime: configEnv.gcTimeMs,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
|
||||
import type { ApiOutput } from '@status-im/wallet/data'
|
||||
import type { UseQueryResult } from '@tanstack/react-query'
|
||||
|
||||
type ConfigEnv = ApiOutput['config']['env']
|
||||
|
||||
const DEFAULT_ENV: ConfigEnv = {
|
||||
refreshIntervalMs: 0,
|
||||
staleTimeMs: Infinity,
|
||||
gcTimeMs: Infinity,
|
||||
}
|
||||
|
||||
type UseConfigEnvResult = Omit<UseQueryResult<ConfigEnv, never>, 'data'> & {
|
||||
data: ConfigEnv
|
||||
}
|
||||
|
||||
const useConfigEnv = (): UseConfigEnvResult => {
|
||||
const query = useQuery<ConfigEnv, never>({
|
||||
queryKey: ['config-env'],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const url = new URL(
|
||||
`${import.meta.env.WXT_STATUS_API_URL}/api/trpc/config.env`,
|
||||
)
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
return DEFAULT_ENV
|
||||
}
|
||||
|
||||
const body = await response.json()
|
||||
return body.result.data.json
|
||||
} catch {
|
||||
return DEFAULT_ENV
|
||||
}
|
||||
},
|
||||
initialData: DEFAULT_ENV,
|
||||
staleTime: 5 * 60 * 1000, // 5 min
|
||||
gcTime: 60 * 60 * 1000, // 1 hour
|
||||
refetchOnWindowFocus: false,
|
||||
retry: false,
|
||||
})
|
||||
|
||||
return { ...query, data: query.data ?? DEFAULT_ENV }
|
||||
}
|
||||
|
||||
export { useConfigEnv }
|
||||
@@ -1,15 +1,20 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
|
||||
import { useConfigEnv } from './use-config-env'
|
||||
import { useRefetchToast } from './use-refetch-toast'
|
||||
|
||||
const REFRESH_INTERVAL_MS = 15 * 1000
|
||||
|
||||
export function useSynchronizedRefetch(address: string) {
|
||||
const queryClient = useQueryClient()
|
||||
const [isWindowActive, setIsWindowActive] = useState(true)
|
||||
const [isAutoRefreshing, setIsAutoRefreshing] = useState(false)
|
||||
const { data: configEnv } = useConfigEnv()
|
||||
|
||||
const refreshIntervalMs = useMemo(
|
||||
() => configEnv.refreshIntervalMs,
|
||||
[configEnv.refreshIntervalMs],
|
||||
)
|
||||
|
||||
const refetchQueries = useCallback(async () => {
|
||||
if (!address) return
|
||||
@@ -89,12 +94,12 @@ export function useSynchronizedRefetch(address: string) {
|
||||
}, [address, queryClient, refetchQueries])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isWindowActive) return
|
||||
if (!isWindowActive || refreshIntervalMs === 0) return
|
||||
|
||||
const interval = setInterval(refetchQueries, REFRESH_INTERVAL_MS)
|
||||
const interval = setInterval(refetchQueries, refreshIntervalMs)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [isWindowActive, address, queryClient, refetchQueries])
|
||||
}, [isWindowActive, address, queryClient, refetchQueries, refreshIntervalMs])
|
||||
}
|
||||
|
||||
function has429Error(
|
||||
|
||||
@@ -49,9 +49,9 @@ export default defineConfig({
|
||||
vite: () => ({
|
||||
define: {
|
||||
'process.env': {
|
||||
ALCHEMY_API_KEY: 'test',
|
||||
ALCHEMY_API_KEYS: 'test',
|
||||
INFURA_API_KEY: 'test',
|
||||
CRYPTOCOMPARE_API_KEY: 'test',
|
||||
CRYPTOCOMPARE_API_KEYS: 'test',
|
||||
COINGECKO_API_KEY: 'test',
|
||||
MERCURYO_SECRET_KEY: 'test',
|
||||
VERCEL: 'test',
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createCallerFactory, router } from './lib/trpc'
|
||||
import { activitiesRouter as activities } from './routers/activity'
|
||||
import { assetsRouter as assets } from './routers/assets'
|
||||
import { collectiblesRouter as collectibles } from './routers/collectibles'
|
||||
import { configRouter as config } from './routers/config'
|
||||
import { nodesRouter as nodes } from './routers/nodes'
|
||||
|
||||
export const apiRouter = router({
|
||||
@@ -9,6 +10,7 @@ export const apiRouter = router({
|
||||
collectibles,
|
||||
nodes,
|
||||
activities,
|
||||
config,
|
||||
})
|
||||
|
||||
export type ApiRouter = typeof apiRouter
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { cache } from 'react'
|
||||
|
||||
import { publicProcedure, router } from '../lib/trpc'
|
||||
|
||||
export const configRouter = router({
|
||||
env: publicProcedure.query(async () => {
|
||||
try {
|
||||
return await getEnvCached()
|
||||
} catch {
|
||||
console.error('Failed to load config env')
|
||||
return {
|
||||
refreshIntervalMs: 0,
|
||||
staleTimeMs: Infinity,
|
||||
gcTimeMs: Infinity,
|
||||
}
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
type ConfigEnv = {
|
||||
refreshIntervalMs: number
|
||||
staleTimeMs: number
|
||||
gcTimeMs: number
|
||||
}
|
||||
|
||||
async function loadEnv(): Promise<ConfigEnv> {
|
||||
return {
|
||||
refreshIntervalMs: 15 * 1000, // 15 seconds
|
||||
staleTimeMs: 15 * 1000, // 15 seconds
|
||||
gcTimeMs: 60 * 60 * 1000, // 1 hour
|
||||
}
|
||||
}
|
||||
|
||||
const getEnvCached = cache(loadEnv)
|
||||
Reference in New Issue
Block a user