diff --git a/packages/synapse-interface/components/ChangelogFeatures.tsx b/packages/synapse-interface/components/ChangelogFeatures.tsx new file mode 100644 index 0000000000..6f0bc170a2 --- /dev/null +++ b/packages/synapse-interface/components/ChangelogFeatures.tsx @@ -0,0 +1,139 @@ +import React, { useState, useEffect, useRef } from 'react' +import { SpeakerphoneIcon } from '@heroicons/react/outline' + +import { titleCase } from '@/utils/titleCase' +import useCloseOnOutsideClick from '@/utils/hooks/useCloseOnOutsideClick' + +const CHANGELOG_RAW_URL = + 'https://raw.githubusercontent.com/synapsecns/sanguine/fe-release/packages/synapse-interface/CHANGELOG.md' + +const CHANGELOG_VISIBLE_URL = + 'https://github.com/synapsecns/sanguine/blob/fe-release/packages/synapse-interface/CHANGELOG.md' + +interface Feature { + version: string + description: string +} + +export const ToggleChangelogButton = () => { + const ref = useRef(null) + const [isChangelogVisible, setIsChangelogVisible] = useState(false) + const [features, setFeatures] = useState([]) + const [isNewVersion, setIsNewVersion] = useState(false) + + useEffect(() => { + fetch(CHANGELOG_RAW_URL) + .then((response) => response.text()) + .then((text) => { + const f = parseChangelog(text) + setFeatures(f) + const lastSeenVersion = localStorage.getItem('lastSeenVersion') + if (f.length > 0 && f[0].version !== lastSeenVersion) { + setIsNewVersion(true) + } + }) + }, []) + + const toggleChangelog = () => { + setIsChangelogVisible(!isChangelogVisible) + if (!isChangelogVisible && features.length > 0) { + localStorage.setItem('lastSeenVersion', features[0].version) + setIsNewVersion(false) + } + } + + useCloseOnOutsideClick(ref, () => setIsChangelogVisible(false)) + + return ( +
+ + {isChangelogVisible && ( +
+ +
+ )} +
+ ) +} +const ChangelogFeatures = ({ features }: { features: Feature[] }) => { + return ( +
+
+ Latest Updates +
+ + + See all changes + +
+ ) +} + +function parseChangelog(changelogText) { + const features = [] + const lines = changelogText.split('\n') + + let currentVersion = null + let currentDescription = null + + for (const line of lines) { + const versionMatch = line.match(/^# \[(\d+\.\d+\.\d+)\]/) + if (versionMatch) { + if (currentVersion && currentDescription) { + features.push({ + version: currentVersion, + description: currentDescription.trim(), + }) + } + currentVersion = versionMatch[1] + currentDescription = '' + } else if (line.startsWith('* **synapse-interface:**')) { + const description = line.replace('* **synapse-interface:**', '').trim() + const cleanedDescription = description.replace( + /\((\[#\d+\]|\[\w+\])\(https?:\/\/[^)]+\)\)/g, + '' + ) + currentDescription += cleanedDescription.trim() + ' ' + } + } + + if (currentVersion && currentDescription) { + features.push({ + version: currentVersion, + description: currentDescription.trim(), + }) + } + + return features +} diff --git a/packages/synapse-interface/components/Wallet.tsx b/packages/synapse-interface/components/Wallet.tsx index bd6426881d..0d5e6596e7 100644 --- a/packages/synapse-interface/components/Wallet.tsx +++ b/packages/synapse-interface/components/Wallet.tsx @@ -6,6 +6,8 @@ import { CoinbaseWalletIcon } from '@icons/WalletIcons/CoinbaseWalletIcon' import { WalletConnectIcon } from '@icons/WalletIcons/WalletConnectIcon' import { IconProps, WalletId } from '@utils/types' import Spinner from './icons/Spinner' +import { SpeakerphoneIcon } from '@heroicons/react/outline' +import { ToggleChangelogButton } from './ChangelogFeatures' const WALLETS = [ { diff --git a/packages/synapse-interface/components/layouts/LandingPageWrapper/index.tsx b/packages/synapse-interface/components/layouts/LandingPageWrapper/index.tsx index 7c033f624c..0f56b0356d 100644 --- a/packages/synapse-interface/components/layouts/LandingPageWrapper/index.tsx +++ b/packages/synapse-interface/components/layouts/LandingPageWrapper/index.tsx @@ -27,6 +27,7 @@ import { PageFooter } from './PageFooter' import { joinClassNames } from '@/utils/joinClassNames' import { MaintenanceBanners } from '@/components/Maintenance/Maintenance' import { AnnouncementBanner } from '@/components/Maintenance/components/AnnouncementBanner' +import { ToggleChangelogButton } from '@/components/ChangelogFeatures' const wrapperClassName = joinClassNames({ textColor: 'text-zinc-800 dark:text-zinc-200', @@ -88,6 +89,7 @@ export function LandingNav() {
+ {({ open }) => ( diff --git a/packages/synapse-interface/utils/titleCase.ts b/packages/synapse-interface/utils/titleCase.ts new file mode 100644 index 0000000000..f32c97b74b --- /dev/null +++ b/packages/synapse-interface/utils/titleCase.ts @@ -0,0 +1,3 @@ +export const titleCase = (string: string): string => { + return string[0].toUpperCase() + string.slice(1).toLowerCase() +}