Skip to content

Commit

Permalink
Merge pull request #70 from bitcoin-sv/feat-641-public-config
Browse files Browse the repository at this point in the history
feat(BUX-641): fetch config from server
  • Loading branch information
chris-4chain authored Mar 13, 2024
2 parents ff6d58c + 9f90cc9 commit 0c8cc40
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 29 deletions.
1 change: 0 additions & 1 deletion public/config.default.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"apiUrl": "http://localhost:3002",
"paymailDomain": "example.com",
"wsUrl": "ws://localhost:3002/api/websocket"
}
12 changes: 5 additions & 7 deletions release/README.DOCKER.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@ The configuration file `env-config.json` can contain all or part of the followin
```json
{
"apiUrl": "http://localhost:8180",
"wsUrl": "ws://localhost:8180/api/websocket",
"paymailDomain": "other.example.com"
"wsUrl": "ws://localhost:8180/api/websocket"
}
```

| Property | Description | Default Value |
|------------------|-------------------------------------------------------------------------------------------------------|-------------------------------------|
| `apiUrl` | The URL pointing to the running `spv-wallet-web-backend`, to which this frontend should connect. | `http://localhost:3002` |
| `wsUrl` | The URL pointing to endpoint in `spv-wallet-web-backend`, used for establishing websocket connection. | `ws://localhost:3002/api/websocket` |
| `paymailDomain` | The paymail domain used to provide a user of this app with their paymail address for BSV transfers. | `example.com` |
| Property | Description | Default Value |
| -------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `apiUrl` | The URL pointing to the running `spv-wallet-web-backend`, to which this frontend should connect. | `http://localhost:3002` |
| `wsUrl` | The URL pointing to endpoint in `spv-wallet-web-backend`, used for establishing websocket connection. | `ws://localhost:3002/api/websocket` |

You can customize these properties in the `env-config.json` file to fit your specific deployment environment.
2 changes: 0 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { LoginPage } from '@/views/LoginPage'
import { TermsAndConditions } from '@/views/TermsAndConditions'
import { PrivacyPolicy } from '@/views/PrivacyPolicy'
import { SignupPage } from '@/views/SignupPage'
import { GlobalStyles } from '@/styles'
import { Header } from '@/components/_layout/Header'
import { Main } from '@/components/_layout/Main'
import { Footer } from '@/components/_layout/Footer'
Expand Down Expand Up @@ -117,7 +116,6 @@ export const App = () => {

return (
<>
<GlobalStyles />
<ToastContainer theme="dark" />

{loading ? (
Expand Down
38 changes: 38 additions & 0 deletions src/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Component, ErrorInfo, PropsWithChildren } from 'react'
import { ErrorBar } from './components/ErrorBar'
import styled from '@emotion/styled'

type ErrorBoundaryState = {
hasError: boolean
}

export class ErrorBoundary extends Component<PropsWithChildren, ErrorBoundaryState> {
constructor(props: PropsWithChildren) {
super(props)
this.state = { hasError: false }
}

static getDerivedStateFromError(): ErrorBoundaryState | null {
return { hasError: true }
}

componentDidCatch(error: Error, info: ErrorInfo) {
console.error(error, info.componentStack)
}

render() {
if (this.state.hasError) {
return (
<Container>
<ErrorBar errorMsg="Something went wrong" withReloadButton />
</Container>
)
}

return this.props.children
}
}

const Container = styled.div`
padding: 20px;
`
8 changes: 8 additions & 0 deletions src/api/requests/GetServerConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import axios from 'axios'

export const getServerConfig = async (apiUrl: string) => {
const { data: response } = await axios.get(`${apiUrl}/config`, {
withCredentials: false,
})
return response
}
1 change: 1 addition & 0 deletions src/api/requests/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './GetUser'
export * from './LoginUser'
export * from './RegisterUser'
export * from './GetServerConfig'
1 change: 1 addition & 0 deletions src/api/types/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './user'
export * from './public_config'
8 changes: 8 additions & 0 deletions src/api/types/public_config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type ExperimentalConfig = {
pike_enabled: boolean
}

export type ServerConfig = {
paymail_domain: string
experimental_features: ExperimentalConfig
}
4 changes: 1 addition & 3 deletions src/components/ErrorBar/ErrorBar.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import ErrorIcon from '@mui/icons-material/Error'
import { FC } from 'react'
import { BarWrapper, ErrorText, ReloadButton } from '@/components/ErrorBar/ErrorProps.styles'
import { useNavigate } from 'react-router-dom'

interface ErrorProps {
errorMsg: string
withReloadButton?: boolean
}

export const ErrorBar: FC<ErrorProps> = ({ errorMsg, withReloadButton }) => {
const navigate = useNavigate()
const reloadHandler = () => {
navigate('/')
window.location.href = '/'
window.location.reload()
}

Expand Down
9 changes: 2 additions & 7 deletions src/hooks/usePaymailDomain.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { useConfig } from '@4chain-ag/react-configuration'
import { useServerConfig } from '@/providers/server_config/hooks'

export const usePaymailDomain = () => {
const { config } = useConfig()
const domain = config?.paymailDomain
if (typeof domain !== 'string') {
return 'example.com'
}
return domain
return useServerConfig().paymail_domain
}
26 changes: 17 additions & 9 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,26 @@ import { ConfigProvider } from '@4chain-ag/react-configuration'
import { BrowserRouter } from 'react-router-dom'
import { AutoupdateProvider } from '@/providers/autoupdate'
import { registerSW } from 'virtual:pwa-register'
import { ServerConfigProvider } from './providers/server_config/provider'
import { ErrorBoundary } from './ErrorBoundary'
import { GlobalStyles } from '@/styles'

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<ConfigProvider>
<AuthorizationProvider>
<AutoupdateProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</AutoupdateProvider>
</AuthorizationProvider>
</ConfigProvider>
<GlobalStyles />
<ErrorBoundary>
<ConfigProvider>
<ServerConfigProvider>
<AuthorizationProvider>
<AutoupdateProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</AutoupdateProvider>
</AuthorizationProvider>
</ServerConfigProvider>
</ConfigProvider>
</ErrorBoundary>
</React.StrictMode>
)

Expand Down
11 changes: 11 additions & 0 deletions src/providers/server_config/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useContext } from 'react'

import { ServerConfigContext } from './provider'

export const useServerConfig = () => {
const ctx = useContext(ServerConfigContext)
if (!ctx) {
throw new Error('ServerConfig provider is missing')
}
return ctx
}
37 changes: 37 additions & 0 deletions src/providers/server_config/provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ServerConfig } from '@/api'
import { useApiUrl } from '@/api/apiUrl'
import { getServerConfig } from '@/api/requests'
import { FC, PropsWithChildren, createContext, useEffect, useState } from 'react'

export const ServerConfigContext = createContext<ServerConfig>(null as never)

export const ServerConfigProvider: FC<PropsWithChildren> = ({ children }) => {
const apiUrl = useApiUrl()
const [loading, setLoading] = useState(true)
const [serverConfig, setServerConfig] = useState<ServerConfig | null>(null)

useEffect(() => {
if (!apiUrl) {
return
}

getServerConfig(apiUrl)
.then((response) => {
setServerConfig(response)
})
.catch((e) => {
console.error('Error during fetching server config', e)
})
.finally(() => setLoading(false))
}, [apiUrl])

if (loading) {
return null
}

if (!serverConfig) {
throw new Error('Cannot fetch server configuration. Please try again later.')
}

return <ServerConfigContext.Provider value={serverConfig}>{children}</ServerConfigContext.Provider>
}

0 comments on commit 0c8cc40

Please sign in to comment.