Files
Compass/web/components/about-settings.tsx
2026-02-23 14:48:03 +01:00

234 lines
6.3 KiB
TypeScript

import {App} from '@capacitor/app'
import {Capacitor} from '@capacitor/core'
import {githubRepo} from 'common/constants'
import {HOSTING_ENV, IS_VERCEL} from 'common/hosting/constants'
import {PrivateUser} from 'common/user'
import {useEffect, useState} from 'react'
import {Button} from 'web/components/buttons/button'
import {Col} from 'web/components/layout/col'
import {CustomLink} from 'web/components/links'
import {WithPrivateUser} from 'web/components/user/with-user'
import {api} from 'web/lib/api'
import {useT} from 'web/lib/locale'
export type WebBuild = {
gitSha?: string
gitMessage?: string
deploymentId?: string
environment?: string
}
export type LiveUpdateInfo = {
bundleId?: string | null
commitSha?: string
commitMessage?: string
commitDate?: string
}
export type Android = {
appVersion?: string
buildNumber?: string
liveUpdate?: LiveUpdateInfo
}
export type Backend = {
version?: string
gitSha?: string
gitMessage?: string
commitDate?: string
}
export type Runtime = {
platform: string
}
export type Diagnostics = {
web?: WebBuild
android?: Android
backend?: Backend
runtime: Runtime
}
function useDiagnostics() {
const [data, setData] = useState<Diagnostics | null>(null)
useEffect(() => {
const load = async () => {
const diagnostics: Diagnostics = {
runtime: {
platform: IS_VERCEL ? 'web' : Capacitor.isNativePlatform() ? 'android' : HOSTING_ENV,
},
}
if (IS_VERCEL) {
diagnostics.web = {
environment: process.env.NEXT_PUBLIC_VERCEL_ENV,
gitSha: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
gitMessage: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE,
deploymentId: process.env.NEXT_PUBLIC_VERCEL_DEPLOYMENT_ID,
}
}
if (Capacitor.isNativePlatform()) {
const appInfo = await App.getInfo()
// const bundle = await LiveUpdate.getCurrentBundle().catch(() => {
// return {bundleId: null}
// })
// const buildInfo = await getLiveUpdateInfo().catch(() => null)
diagnostics.android = {
appVersion: appInfo.version,
buildNumber: appInfo.build,
// liveUpdate: {
// bundleId: bundle?.bundleId,
// commitSha: buildInfo?.commitSha,
// commitMessage: buildInfo?.commitMessage,
// commitDate: buildInfo?.commitDate
// }
}
}
const backend = await api('health').catch(() => null)
if (backend) {
diagnostics.backend = {
version: backend.version,
gitSha: backend.git?.revision,
gitMessage: backend.git?.message,
commitDate: backend.git?.commitDate,
}
}
setData(diagnostics)
}
load()
}, [])
return data
}
function diagnosticsToText(d: Diagnostics): string {
const replacer = (key: string, value: any) => {
if (value === null) return 'null'
if (value === undefined) return 'undefined'
return value
}
return JSON.stringify(d, replacer, 2)
.replace(/ {2}"/g, '')
.replace(/["{}[\]]/g, '')
.replace(/^[ \t]*\n/gm, '')
.replace(/,\n/g, '\n')
.trim()
}
export const AboutSettings = () => (
<WithPrivateUser>{(user) => <LoadedAboutSettings privateUser={user} />}</WithPrivateUser>
)
const LoadedAboutSettings = (_props: {privateUser: PrivateUser}) => {
const [copyFeedback, setCopyFeedback] = useState('')
const t = useT()
const diagnostics = useDiagnostics()
if (!diagnostics) return null
const handleCopy = async () => {
if (!diagnostics) return
await navigator.clipboard.writeText(diagnosticsToText(diagnostics))
setCopyFeedback(t('about.settings.copied', 'Copied!'))
setTimeout(() => {
setCopyFeedback('')
}, 2000)
}
return (
<Col className={''}>
<RuntimeInfo info={diagnostics.runtime} />
<WebBuildInfo info={diagnostics.web} />
<AndroidInfo info={diagnostics.android} />
<BackendInfo info={diagnostics.backend} />
<Button onClick={handleCopy} className="w-fit mt-4">
{copyFeedback || t('about.settings.copy_info', 'Copy Info')}
</Button>
</Col>
)
}
const WebBuildInfo = (props: {info?: WebBuild}) => {
const {info} = props
if (!info) return
const env = info.environment
const gitMessage = info.gitMessage
const sha = info.gitSha
const deploymentId = info.deploymentId
const url = `${githubRepo}/commit/${sha}`
return (
<Col className={'custom-link'}>
<h3>Web build (Vercel)</h3>
<p>
Commit SHA: <CustomLink href={url}>{sha}</CustomLink>
</p>
<p>Commit message: {gitMessage}</p>
<p>Vercel deployment ID: {deploymentId}</p>
<p>Environment: {env}</p>
</Col>
)
}
const AndroidInfo = (props: {info?: Android}) => {
const {info} = props
if (!info) return
const sha = info.liveUpdate?.commitSha
const url = `${githubRepo}/commit/${sha}`
return (
<Col className={'custom-link'}>
<h3>Android (Capacitor)</h3>
<p>App version (Android): {info.appVersion}</p>
<p>Native build number (Android): {info.buildNumber}</p>
{info.liveUpdate && (
<>
<p>Live update build ID (Capawesome): {info.liveUpdate?.bundleId}</p>
<p>
Live update commit SHA (Capawesome): <CustomLink href={url}>{sha}</CustomLink>
</p>
<p>Live update commit message (Capawesome): {info.liveUpdate?.commitMessage}</p>
<p>Live update commit date (Capawesome): {info.liveUpdate?.commitDate}</p>
</>
)}
</Col>
)
}
const BackendInfo = (props: {info?: Backend}) => {
const {info} = props
if (!info) return
const sha = info.gitSha
const commitDate = info.commitDate
const commitMessage = info.gitMessage
const url = `${githubRepo}/commit/${sha}`
return (
<Col className={'custom-link'}>
<h3>Backend</h3>
<p>API version: {info.version}</p>
{sha && (
<p>
API commit SHA: <CustomLink href={url}>{sha}</CustomLink>
</p>
)}
{commitMessage && <p>API commit message: {commitMessage}</p>}
{commitDate && <p>API commit date: {commitDate}</p>}
</Col>
)
}
const RuntimeInfo = (props: {info?: Runtime}) => {
const {info} = props
if (!info) return
return (
<Col className={'custom-link'}>
<h3>Runtime</h3>
<p>Platform: {info.platform}</p>
</Col>
)
}