Skip to content

Commit

Permalink
Merge pull request #1244 from oasisprotocol/csillag/white-label
Browse files Browse the repository at this point in the history
Support for white-labeling Explorer
  • Loading branch information
csillag authored Feb 13, 2024
2 parents e568823 + 0f39ef9 commit 64610cb
Show file tree
Hide file tree
Showing 16 changed files with 456 additions and 56 deletions.
1 change: 1 addition & 0 deletions .changelog/1244.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for white-labeling Explorer
13 changes: 13 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,16 @@ REACT_APP_BUILD_VERSION=
# REACT_APP_TESTNET_API=https://testnet.nexus.stg.oasis.io/v1/
REACT_APP_API=https://nexus.oasis.io/v1/
REACT_APP_TESTNET_API=https://testnet.nexus.oasis.io/v1/
REACT_APP_TITLE=Oasis Explorer
REACT_APP_DESC=Official explorer for the Oasis Network.
REACT_APP_SOCIAL_TELEGRAM=https://t.me/oasisprotocolcommunity
REACT_APP_SOCIAL_TWITTER=https://twitter.com/oasisprotocol
REACT_APP_SOCIAL_DISCORD=https://oasis.io/discord
REACT_APP_SOCIAL_YOUTUBE=https://www.youtube.com/channel/UC35UFPcZ2F1wjPxhPrSsESQ
REACT_APP_SOCIAL_REDDIT=https://www.reddit.com/r/oasisnetwork/
# REACT_APP_SOCIAL_LINKEDIN=https://www.linkedin.com/company/oasisprotocol
# REACT_APP_SOCIAL_DOCS=https://oasisprotocol.org/developers#overview
# REACT_APP_SOCIAL_HOME=https://oasisprotocol.org/
REACT_APP_PROD_URL=https://explorer.oasis.io, https://explorer.prd.oasis.io
REACT_APP_STAGING_URL=https://explorer.stg.oasis.io
REACT_APP_SHOW_BUILD_BANNERS=true
13 changes: 13 additions & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
REACT_APP_BUILD_DATETIME=
REACT_APP_BUILD_SHA=
REACT_APP_BUILD_VERSION=
REACT_APP_TITLE=Oasis Explorer
REACT_APP_DESC=Official explorer for the Oasis Network.
REACT_APP_SOCIAL_TELEGRAM=https://t.me/oasisprotocolcommunity
REACT_APP_SOCIAL_TWITTER=https://twitter.com/oasisprotocol
REACT_APP_SOCIAL_DISCORD=https://oasis.io/discord
REACT_APP_SOCIAL_YOUTUBE=https://www.youtube.com/channel/UC35UFPcZ2F1wjPxhPrSsESQ
REACT_APP_SOCIAL_REDDIT=https://www.reddit.com/r/oasisnetwork/
# REACT_APP_SOCIAL_LINKEDIN=https://www.linkedin.com/company/oasisprotocol
# REACT_APP_SOCIAL_DOCS=https://oasisprotocol.org/developers#overview
# REACT_APP_SOCIAL_HOME=https://oasisprotocol.org/
REACT_APP_PROD_URL=https://explorer.oasis.io, https://explorer.prd.oasis.io
REACT_APP_STAGING_URL=https://explorer.stg.oasis.io
REACT_APP_SHOW_BUILD_BANNERS=true
9 changes: 9 additions & 0 deletions .parcelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "@parcel/config-default",
"transformers": {
"*.html": [
"@plasmohq/parcel-transformer-inject-env",
"..."
]
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"@emotion/jest": "11.11.0",
"@parcel/packager-raw-url": "2.11.0",
"@parcel/transformer-webmanifest": "2.11.0",
"@plasmohq/parcel-transformer-inject-env": "^0.2.11",
"@storybook/addon-actions": "7.6.13",
"@storybook/addon-essentials": "7.6.13",
"@storybook/addon-interactions": "7.6.13",
Expand Down
8 changes: 4 additions & 4 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
<meta name="theme-color" content="#000000" />
<link rel="apple-touch-icon" href="./logo192.png" />
<link rel="manifest" href="./app.webmanifest" />
<meta property="og:title" content="Oasis Explorer" />
<meta property="og:description" content="Official explorer for the Oasis Network." />
<meta property="og:title" content="$REACT_APP_TITLE" />
<meta property="og:description" content="$REACT_APP_DESC" />
<meta property="og:image" content="./oasis-og-image.jpg" />
<meta property="og:type" content="website" />
<title>Oasis Explorer</title>
<meta name="description" content="Official explorer for the Oasis Network." />
<title>$REACT_APP_TITLE</title>
<meta name="description" content="$REACT_APP_DESC" />
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
10 changes: 6 additions & 4 deletions src/app/components/BuildBanner/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { deploys } from '../../../config'
import { deploys, getAppTitle } from '../../../config'
import { StickyAlert } from '../StickyAlert'

const useBuildBanners = process.env.REACT_APP_SHOW_BUILD_BANNERS === 'true'

export const BuildBanner: FC = () => {
const { t } = useTranslation()

if (window.location.origin === deploys.localhost) {
if (!useBuildBanners || window.location.origin === deploys.localhost) {
return null
}
if (deploys.production.includes(window.location.origin)) {
return null
}
if (window.location.origin === deploys.staging) {
if (deploys.staging.includes(window.location.origin)) {
return <StickyAlert severity="warning">{t('banner.buildStaging')}</StickyAlert>
}
return <StickyAlert severity="warning">{t('banner.buildPreview')}</StickyAlert>
return <StickyAlert severity="warning">{t('banner.buildPreview', { appTitle: getAppTitle() })}</StickyAlert>
}
5 changes: 2 additions & 3 deletions src/app/components/PageLayout/Logotype.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Box from '@mui/material/Box'
import { Link as RouterLink } from 'react-router-dom'
import { OasisIcon } from '../CustomIcons/OasisIcon'
import Typography from '@mui/material/Typography'
import { useTranslation } from 'react-i18next'
import { getAppTitle } from '../../../config'

interface LogotypeProps {
color?: string
Expand All @@ -22,7 +22,6 @@ export const HomePageLink: FC<LogotypeProps> = ({ color, showText }) => {
}

export const Logotype: FC<LogotypeProps> = ({ color, showText }) => {
const { t } = useTranslation()
const theme = useTheme()
const { isMobile } = useScreenSize()
const logoSize = isMobile ? 32 : 40
Expand All @@ -40,7 +39,7 @@ export const Logotype: FC<LogotypeProps> = ({ color, showText }) => {
<OasisIcon sx={{ fontSize: logoSize }} />
{showText && (
<Typography variant="h1" color={color || theme.palette.layout.main} sx={{ whiteSpace: 'nowrap' }}>
{t('pageTitle')}
{getAppTitle()}
</Typography>
)}
</Box>
Expand Down
3 changes: 2 additions & 1 deletion src/app/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { isValidBlockHeight } from '../../utils/helpers'
import { typingDelay } from '../../../styles/theme'
import { isValidMnemonic } from '../../utils/helpers'
import Collapse from '@mui/material/Collapse'
import { getAppTitle } from '../../../config'

export type SearchVariant = 'button' | 'icon' | 'expandable'

Expand Down Expand Up @@ -163,7 +164,7 @@ const SearchCmp: FC<SearchProps> = ({ scope, variant, disabled, onFocusChange: o
const hasError = !!errorMessage

const warningMessage = hasPrivacyProblem
? t('search.error.privacy', { appName: t('appName'), wordsOfPower })
? t('search.error.privacy', { appName: getAppTitle(), wordsOfPower })
: undefined
const hasWarning = !!warningMessage

Expand Down
106 changes: 77 additions & 29 deletions src/app/components/Social/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC } from 'react'
import React, { FC, ReactNode } from 'react'
import { useTranslation } from 'react-i18next'
import Box from '@mui/material/Box'
import Grid from '@mui/material/Unstable_Grid2'
Expand All @@ -11,17 +11,21 @@ import twitter from './images/twitter.svg'
import discord from './images/discord.svg'
import youtube from './images/youtube.svg'
import reddit from './images/reddit.svg'
import LinkedInIcon from '@mui/icons-material/LinkedIn'
import DocsIcon from '@mui/icons-material/MenuBook'
import HomeIcon from '@mui/icons-material/Cottage'
import { COLORS } from '../../../styles/theme/colors'
import { socialMedia } from '../../utils/externalLinks'

type SocialLinkProps = {
label: string
href: string
isMobile: boolean
img: string
imgSrc?: string
img?: ReactNode
}

const SocialLink: FC<SocialLinkProps> = ({ label, href, isMobile, img }) => {
const SocialLink: FC<SocialLinkProps> = ({ label, href, isMobile, imgSrc, img }) => {
return (
<Link
href={href}
Expand All @@ -38,13 +42,18 @@ const SocialLink: FC<SocialLinkProps> = ({ label, href, isMobile, img }) => {
target="_blank"
>
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 3 }}>
<img src={img} alt={label} height={40} />
<>
{imgSrc && <img src={imgSrc} alt={label} height={40} />}
{img}
</>
</Box>
<Typography sx={{ fontSize: 18, fontWeight: 700, mb: isMobile ? 4 : 0 }}>{label}</Typography>
</Link>
)
}

const iconProps = { sx: { fontSize: 50, margin: '-4px' } }

export const Social: FC = () => {
const { t } = useTranslation()
const { isMobile } = useScreenSize()
Expand Down Expand Up @@ -84,31 +93,70 @@ export const Social: FC = () => {
height: '100%',
}}
>
<SocialLink
isMobile={isMobile}
label={t('social.telegram')}
href={socialMedia.telegram}
img={telegram}
/>
<SocialLink
isMobile={isMobile}
label={t('social.twitter')}
href={socialMedia.twitter}
img={twitter}
/>
<SocialLink
isMobile={isMobile}
label={t('social.discord')}
href={socialMedia.discord}
img={discord}
/>
<SocialLink
isMobile={isMobile}
label={t('social.youtube')}
href={socialMedia.youtube}
img={youtube}
/>
<SocialLink isMobile={isMobile} label={t('social.reddit')} href={socialMedia.reddit} img={reddit} />
{socialMedia.telegram && (
<SocialLink
isMobile={isMobile}
label={t('social.telegram')}
href={socialMedia.telegram}
imgSrc={telegram}
/>
)}
{socialMedia.twitter && (
<SocialLink
isMobile={isMobile}
label={t('social.twitter')}
href={socialMedia.twitter}
imgSrc={twitter}
/>
)}
{socialMedia.discord && (
<SocialLink
isMobile={isMobile}
label={t('social.discord')}
href={socialMedia.discord}
imgSrc={discord}
/>
)}
{socialMedia.youtube && (
<SocialLink
isMobile={isMobile}
label={t('social.youtube')}
href={socialMedia.youtube}
imgSrc={youtube}
/>
)}
{socialMedia.reddit && (
<SocialLink
isMobile={isMobile}
label={t('social.reddit')}
href={socialMedia.reddit}
imgSrc={reddit}
/>
)}
{socialMedia.linkedin && (
<SocialLink
isMobile={isMobile}
label={t('social.linkedin')}
href={socialMedia.linkedin}
img={<LinkedInIcon {...iconProps} />}
/>
)}
{socialMedia.docs && (
<SocialLink
isMobile={isMobile}
label={t('social.docs')}
href={socialMedia.docs}
img={<DocsIcon {...iconProps} />}
/>
)}
{socialMedia.home && (
<SocialLink
isMobile={isMobile}
label={t('social.home')}
href={socialMedia.home}
img={<HomeIcon {...iconProps} />}
/>
)}
</Box>
</Grid>
</Grid>
Expand Down
7 changes: 5 additions & 2 deletions src/app/utils/__tests__/externalLinks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,20 @@ onlyRunOnCI('externalLinks', () => {
expect(Object.entries(linksGroup).length).toBeGreaterThan(0)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const [_linkName, url] of Object.entries(linksGroup)) {
if (url === undefined) continue
expect(typeof url).toBe('string')
expect(url).toMatch(/^(https|mailto):/)
}
}
})

describe('should be reachable', () => {
const { reddit, twitter } = externalLinksModule.socialMedia
for (const [linksGroupName, linksGroup] of Object.entries(externalLinksModule)) {
for (const [linkName, url] of Object.entries(linksGroup)) {
if (url.startsWith(externalLinksModule.socialMedia.reddit)) continue // Reddit often returns 504
if (url.startsWith(externalLinksModule.socialMedia.twitter)) continue // redirect loop
if (!url || typeof url !== 'string') continue
if (!!reddit && url.startsWith(reddit)) continue // Reddit often returns 504
if (!!twitter && url.startsWith(twitter)) continue // redirect loop
if (url.startsWith(externalLinksModule.referrals.coinGecko)) continue // CoinGecko has CloudFlare DDOS protection
if (url.startsWith(externalLinksModule.github.commit)) continue // We store only partial url in constants
if (url.startsWith(externalLinksModule.github.releaseTag)) continue // We store only partial url in constants
Expand Down
13 changes: 8 additions & 5 deletions src/app/utils/externalLinks.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { Layer } from '../../oasis-nexus/api'

export const socialMedia = {
telegram: 'https://t.me/oasisprotocolcommunity',
twitter: 'https://twitter.com/oasisprotocol',
discord: 'https://oasis.io/discord',
telegram: process.env.REACT_APP_SOCIAL_TELEGRAM,
twitter: process.env.REACT_APP_SOCIAL_TWITTER,
discord: process.env.REACT_APP_SOCIAL_DISCORD,
// This API link is for testing if invite is still valid.
isDiscordStillValid: 'https://oasis.io/discord/invite-api-check',
youtube: 'https://www.youtube.com/channel/UC35UFPcZ2F1wjPxhPrSsESQ',
reddit: 'https://www.reddit.com/r/oasisnetwork/',
youtube: process.env.REACT_APP_SOCIAL_YOUTUBE,
reddit: process.env.REACT_APP_SOCIAL_REDDIT,
linkedin: process.env.REACT_APP_SOCIAL_LINKEDIN,
docs: process.env.REACT_APP_SOCIAL_DOCS,
home: process.env.REACT_APP_SOCIAL_HOME,
}

const docsUrl = 'https://docs.oasis.io/'
Expand Down
14 changes: 12 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,21 @@ export const paraTimesConfig = {
[Layer.consensus]: null,
} satisfies LayersConfig

const parseUrl = (input: string | undefined): string[] =>
input
? input
.split(',')
.filter(s => !!s)
.map(s => s.trim())
: []

export const deploys = {
production: ['https://explorer.oasis.io', 'https://explorer.prd.oasis.io'],
staging: 'https://explorer.stg.oasis.io',
production: parseUrl(process.env.REACT_APP_PROD_URL),
staging: parseUrl(process.env.REACT_APP_STAGING_URL),
localhost: 'http://localhost:1234',
}

const stableDeploys = [...deploys.production, deploys.staging]
export const isStableDeploy = stableDeploys.some(url => window.location.origin === url)

export const getAppTitle = () => process.env.REACT_APP_TITLE
25 changes: 25 additions & 0 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test'
REACT_APP_BUILD_DATETIME: string
REACT_APP_BUILD_SHA: string
REACT_APP_BUILD_VERSION: string
REACT_APP_API: string
REACT_APP_TESTNET_API: string
REACT_APP_SHOW_BUILD_BANNERS?: 'true' | 'false'
REACT_APP_TITLE: string
REACT_APP_DESC: string
REACT_APP_SOCIAL_TELEGRAM?: string
REACT_APP_SOCIAL_TWITTER?: string
REACT_APP_SOCIAL_DISCORD?: string
REACT_APP_SOCIAL_YOUTUBE?: string
REACT_APP_SOCIAL_REDDIT?: string
READ_APP_SOCIAL_LINKEDIN?: string
READ_APP_SOCIAL_DOCS?: string
READ_APP_SOCIAL_HOME?: string
REACT_APP_PROD_URL: string
REACT_APP_STAGING_URL?: string
}
}
}
Loading

0 comments on commit 64610cb

Please sign in to comment.