Skip to content

Commit

Permalink
Merge pull request #4916 from cowprotocol/release/26-09-2024
Browse files Browse the repository at this point in the history
Release/26-09-2024
  • Loading branch information
shoom3301 authored Sep 30, 2024
2 parents 7503565 + 533eeec commit 4278fae
Show file tree
Hide file tree
Showing 233 changed files with 10,983 additions and 1,512 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ on:
- permit-utils
- widget-lib
- widget-react
- iframe-transport
- hook-dapp-lib
# Add more publishable libs here
tag:
description: NPM package tag
Expand Down
31 changes: 31 additions & 0 deletions apps/cow-fi/components/ArticlesList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react'
import { clickOnKnowledgeBase } from 'modules/analytics'
import { LinkItem, LinkColumn } from '@/styles/styled'
import { Article } from 'services/cms'

interface ArticlesListProps {
articles: Article[]
}

const ARTICLES_PATH = '/learn/articles/'

export const ArticlesList: React.FC<ArticlesListProps> = ({ articles }) => (
<LinkColumn>
{articles.map((article) => {
if (!article.attributes) return null

const { slug, title } = article.attributes

return (
<LinkItem
key={article.id}
href={`${ARTICLES_PATH}${slug}`}
onClick={() => clickOnKnowledgeBase(`click-article-${title}`)}
>
{title}
<span></span>
</LinkItem>
)
})}
</LinkColumn>
)
109 changes: 109 additions & 0 deletions apps/cow-fi/components/CategoryLinks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React from 'react'
import styled from 'styled-components/macro'
import { clickOnKnowledgeBase } from 'modules/analytics'
import { Color, Media } from '@cowprotocol/ui'
interface Category {
name: string
slug: string
}

interface CategoryLinksProps {
allCategories: Category[]
noDivider?: boolean
}

const CategoryLinksWrapper = styled.ul<{ noDivider?: boolean }>`
display: flex;
align-items: center;
justify-content: center;
gap: 32px;
padding: 0;
margin: 0;
list-style: none;
font-size: 16px;
font-weight: 500;
color: ${Color.neutral50};
width: 100%;
scrollbar-width: thin;
scrollbar-color: ${Color.neutral70} ${Color.neutral90};
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-track {
background: ${Color.neutral90};
border-radius: 10px;
}
&::-webkit-scrollbar-thumb {
background: ${Color.neutral70};
border-radius: 10px;
}
&::-webkit-scrollbar-thumb:hover {
background: ${Color.neutral50};
}
${Media.upToMedium()} {
overflow-x: auto;
overflow-y: hidden;
flex-flow: row nowrap;
justify-content: flex-start;
gap: 24px;
padding: 16px 34px 16px 16px;
}
li {
display: flex;
align-items: center;
justify-content: center;
&:first-child {
margin-right: ${({ noDivider }) => (noDivider ? '0' : '-32px')};
}
&:first-child::after {
content: ${({ noDivider }) => (noDivider ? 'none' : "'|'")};
margin: 0 16px;
display: flex;
height: 100%;
width: 16px;
align-items: center;
justify-content: center;
}
}
a {
color: ${Color.neutral40};
text-decoration: none;
transition: color 0.2s ease-in-out;
white-space: nowrap;
line-height: 1;
&:hover {
color: ${Color.neutral0};
}
}
`

export const CategoryLinks: React.FC<CategoryLinksProps> = ({ allCategories, noDivider }) => (
<CategoryLinksWrapper noDivider={noDivider}>
<li>
<a href="/learn" onClick={() => clickOnKnowledgeBase('click-categories-home')}>
Knowledge Base
</a>
</li>
{allCategories.map((category) => (
<li key={category.slug}>
<a
href={`/learn/topic/${category.slug}`}
onClick={() => clickOnKnowledgeBase(`click-categories-${category.name}`)}
>
{category.name}
</a>
</li>
))}
</CategoryLinksWrapper>
)
66 changes: 66 additions & 0 deletions apps/cow-fi/components/LazySVG.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useEffect, useRef, useState } from 'react'
import SVG, { Props as SVGProps } from 'react-inlinesvg'

interface LazySVGProps extends Omit<SVGProps, 'loader'> {
src: string
loader?: React.ReactNode
rootMargin?: string
}

const LazySVG: React.FC<LazySVGProps> = ({
src,
loader = <div>Loading SVG...</div>,
rootMargin = '100px',
width,
height,
...props
}) => {
const [isInView, setIsInView] = useState(false)
const wrapperRef = useRef<HTMLSpanElement>(null)

useEffect(() => {
if (!wrapperRef.current) {
return
}

const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setIsInView(true)
observer.disconnect()
}
})
},
{
root: null,
rootMargin: rootMargin,
threshold: 0,
},
)

observer.observe(wrapperRef.current)

return () => {
observer.disconnect()
}
}, [rootMargin, src])

return (
<span ref={wrapperRef}>
{isInView ? (
<SVG
src={src}
{...props}
loader={loader}
width={typeof width === 'number' ? width : undefined}
height={typeof height === 'number' ? height : undefined}
/>
) : (
loader
)}
</span>
)
}

export default LazySVG
6 changes: 3 additions & 3 deletions apps/cow-fi/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const Input = styled.input`
min-height: 56px;
border: 2px solid transparent;
font-size: 21px;
color: ${Color.neutral50};
color: ${Color.neutral40};
width: 100%;
background: ${Color.neutral90};
border-radius: 56px;
Expand Down Expand Up @@ -215,7 +215,7 @@ export const SearchBar: React.FC<SearchBarProps> = ({ articles }) => {
useEffect(() => {
if (query.trim()) {
const filtered = (articles || []).filter((article) =>
article.attributes?.title?.toLowerCase().includes(query.toLowerCase())
article.attributes?.title?.toLowerCase().includes(query.toLowerCase()),
)
setFilteredArticles(filtered)
} else {
Expand All @@ -230,7 +230,7 @@ export const SearchBar: React.FC<SearchBarProps> = ({ articles }) => {
<HighlightedText key={index}>{part}</HighlightedText>
) : (
<span key={index}>{part}</span>
)
),
)
}

Expand Down
23 changes: 0 additions & 23 deletions apps/cow-fi/const/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,27 +55,4 @@ interface MetaTokenDetails extends TokenInfoExcluded {
change24hTrimmed: string
}

export const META_DESCRIPTION_TEMPLATES = [
({ name, symbol, priceUsd, change24hTrimmed, volume, marketCap }: MetaTokenDetails) =>
`Moo-ve over to ${name} (${symbol})! Grazing at $${priceUsd} with ${change24hTrimmed}% change in 24h. Trading volume: $${volume}. Their market cap: $${marketCap}. Learn about ${symbol}'s pasture.`,

({ name, symbol, priceUsd, change24hTrimmed, volume, marketCap }: MetaTokenDetails) =>
`The grass is greener with ${name} (${symbol})! At $${priceUsd}, with ${change24hTrimmed}% change in 24h. Trading volume: $${volume}. Their market cap: $${marketCap}. Discover more about ${symbol}.`,

({ name, symbol, priceUsd, change24hTrimmed, volume, marketCap }: MetaTokenDetails) =>
`Interested in ${name} (${symbol})? Priced at $${priceUsd}, with ${change24hTrimmed}% change in 24h. Trading volume: $${volume}. Their market cap: $${marketCap}. Learn more about ${symbol}!`,

({ name, symbol, priceUsd, change24hTrimmed, volume, marketCap }: MetaTokenDetails) =>
`Graze on this: ${name} (${symbol}) at $${priceUsd}, with ${change24hTrimmed}% change in 24h. Trading volume: $${volume}. They boast a market cap of $${marketCap}. Learn about ${symbol}.`,

({ name, symbol, priceUsd, change24hTrimmed, volume, marketCap }: MetaTokenDetails) =>
`Ever heard of ${name} (${symbol})? At $${priceUsd}, they've marked a ${change24hTrimmed}% change in 24h. Trading volume: $${volume}. Their market cap: $${marketCap}. Get to know them.`,

({ name, symbol, priceUsd, change24hTrimmed, volume, marketCap }: MetaTokenDetails) =>
`Check out ${name} (${symbol})! Grazing at $${priceUsd}, with ${change24hTrimmed}% change in 24h. Trading volume: $${volume}. Their market cap: $${marketCap}. Discover ${symbol}'s secrets.`,

({ name, symbol, priceUsd, change24hTrimmed, volume, marketCap }: MetaTokenDetails) =>
`Latest on ${name} (${symbol}): priced at $${priceUsd}. Experienced a ${change24hTrimmed}% change in 24h. Trading volume: $${volume}. Their market cap: $${marketCap}. Learn more.`,
]

export type SiteConfig = typeof CONFIG
108 changes: 108 additions & 0 deletions apps/cow-fi/hooks/useLazyLoadImages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { useEffect, useRef, useCallback, FC, ImgHTMLAttributes, memo } from 'react'

let observer: IntersectionObserver | null = null
const callbacks: ((entry: IntersectionObserverEntry) => void)[] = []

const LAZY_LOADING_CONFIG = {
rootMargin: '25px',
placeholderColor: '#f0f0f0',
fadeInDuration: '0.3s',
minHeight: '100px',
}

// Utility function to generate placeholder src
const getPlaceholderSrc = (placeholderColor: string): string => {
const encodedColor = encodeURIComponent(placeholderColor)
return `data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%' viewBox='0 0 1 1' preserveAspectRatio='none'><rect width='1' height='1' fill='${encodedColor}' /></svg>`
}

const PLACEHOLDER_SRC = getPlaceholderSrc(LAZY_LOADING_CONFIG.placeholderColor)

const initObserver = () => {
if (observer) return

observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
callbacks.forEach((cb) => cb(entry))
})
},
{ rootMargin: LAZY_LOADING_CONFIG.rootMargin },
)
}

const addCallback = (cb: (entry: IntersectionObserverEntry) => void) => {
callbacks.push(cb)
initObserver()
}

const removeCallback = (cb: (entry: IntersectionObserverEntry) => void) => {
const index = callbacks.indexOf(cb)
if (index > -1) {
callbacks.splice(index, 1)
}
// If no callbacks remain, disconnect the observer
if (callbacks.length === 0 && observer) {
observer.disconnect()
observer = null
}
}

export function useLazyLoadImages() {
const replaceImageUrls = useCallback((html: string): string => {
return html.replace(/<img([^>]*)src="([^"]*)"([^>]*)>/g, (_, before, src, after) => {
return `<img${before}data-src="${src}" src="${PLACEHOLDER_SRC}"${after}>`
})
}, [])

const LazyImage: FC<ImgHTMLAttributes<HTMLImageElement>> = ({ src, alt = '', width, height, style, ...props }) => {
const imgRef = useRef<HTMLImageElement>(null)

useEffect(() => {
if (!imgRef.current || !src) return

const handleIntersect = (entry: IntersectionObserverEntry) => {
const img = entry.target as HTMLImageElement
if (entry.isIntersecting && img.dataset.src) {
img.src = img.dataset.src
img.style.opacity = '1'
observer?.unobserve(img)
}
}

addCallback(handleIntersect)

observer?.observe(imgRef.current)

return () => {
if (imgRef.current) {
observer?.unobserve(imgRef.current)
}

removeCallback(handleIntersect)
}
}, [src])

return (
<img
ref={imgRef}
src={PLACEHOLDER_SRC}
data-src={src}
alt={alt}
width={width}
height={height}
{...props}
style={{
minHeight: LAZY_LOADING_CONFIG.minHeight,
opacity: 0,
transition: `opacity ${LAZY_LOADING_CONFIG.fadeInDuration}`,
...style,
}}
/>
)
}

const MemoizedLazyImage = memo(LazyImage)

return { replaceImageUrls, LazyImage: MemoizedLazyImage }
}
3 changes: 2 additions & 1 deletion apps/cow-fi/modules/analytics/events.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { initCowAnalyticsGoogle } from '@cowprotocol/analytics'

export const cowAnalytics = initCowAnalyticsGoogle()
// Loads Analytics with GTM
export const cowAnalytics = initCowAnalyticsGoogle(true)

export enum Category {
HOME = 'Homepage',
Expand Down
Loading

0 comments on commit 4278fae

Please sign in to comment.