From 181c9c103d671161c877ec236305e65dfe8ef681 Mon Sep 17 00:00:00 2001 From: hugolxt <87241914+hugolxt@users.noreply.github.com> Date: Thu, 19 Dec 2024 18:32:26 +0100 Subject: [PATCH] introduction of I18n and fixes (#49) * introduction of I18n and fixes * fix * fix in useCampaign campaign name * lint --- merkl.config.ts | 23 ++++++++----- src/I18n/en.ts | 14 ++++++++ src/I18n/index.ts | 34 +++++++++++++++++++ .../element/functions/SearchBar.tsx | 4 ++- .../opportunity/OpportunityTableRow.tsx | 7 +++- src/components/element/tvl/TvlSection.tsx | 2 +- src/components/layout/Header.tsx | 4 +-- src/config/type.ts | 6 ++++ src/hooks/resources/useOpportunity.tsx | 6 +++- src/routes/_merkl.(home).tsx | 5 +-- .../_merkl.opportunities.$chain.$type.$id.tsx | 25 +++++++++----- 11 files changed, 104 insertions(+), 26 deletions(-) create mode 100644 src/I18n/en.ts create mode 100644 src/I18n/index.ts diff --git a/merkl.config.ts b/merkl.config.ts index e23d601f..60d58695 100644 --- a/merkl.config.ts +++ b/merkl.config.ts @@ -47,9 +47,9 @@ export default createConfig({ appName: "Merkl", modes: ["dark", "light"], defaultTheme: "ignite", - tags: [], opportunityNavigationMode: "direct", rewardsNavigationMode: "chain", + opprtunityPercentage: true, deposit: true, themes: { ignite: { @@ -95,16 +95,16 @@ export default createConfig({ route: "/", key: uuidv4(), }, - opportunities: { - icon: "RiPlanetFill", - route: "/opportunities", - key: uuidv4(), - }, - // protocols: { - // icon: "RiVipCrown2Fill", - // route: "/protocols", + // opportunities: { + // icon: "RiPlanetFill", + // route: "/opportunities", // key: uuidv4(), // }, + protocols: { + icon: "RiVipCrown2Fill", + route: "/protocols", + key: uuidv4(), + }, // bridge: { // icon: "RiCompassesLine", // route: "/bridge", @@ -132,6 +132,11 @@ export default createConfig({ // key: uuidv4(), // }, }, + header: { + searchbar: { + enabled: true, + }, + }, socials: { discord: "", telegram: "https://t.me/+2T0RNabX2ANkMzAx", diff --git a/src/I18n/en.ts b/src/I18n/en.ts new file mode 100644 index 00000000..839693e2 --- /dev/null +++ b/src/I18n/en.ts @@ -0,0 +1,14 @@ +const en = { + pages: { + opportunities: { + title: "Explore opportunities", + description: "Browse opportunities, compare rewards, and earn tokens", + }, + campaings: { + title: "Explore opportunities", + description: "Browse opportunities, compare rewards, and earn tokens", + }, + }, +}; + +export default en; diff --git a/src/I18n/index.ts b/src/I18n/index.ts new file mode 100644 index 00000000..a2236b25 --- /dev/null +++ b/src/I18n/index.ts @@ -0,0 +1,34 @@ +import en from "./en"; + +type LanguageFiles = typeof en; + +/** + * I18n class to handle translations + */ +export class I18n { + public static readonly trad: I18n = new I18n(en); + + private primary: LanguageFiles; + // private secondary: LanguageFiles; + + public get: LanguageFiles; + + constructor(primary: LanguageFiles) { + this.primary = primary; + // this.secondary = secondary; + this.get = this.createProxy(this.primary); + } + + // proxy to handle typing autocompletion + private createProxy(obj: T): T { + return new Proxy(obj, { + get: (target, prop) => { + const value = target[prop as keyof T]; + if (typeof value === "object" && value !== null) { + return this.createProxy(value as object); + } + return value; + }, + }); + } +} diff --git a/src/components/element/functions/SearchBar.tsx b/src/components/element/functions/SearchBar.tsx index 83d8afe8..52ec0dc5 100644 --- a/src/components/element/functions/SearchBar.tsx +++ b/src/components/element/functions/SearchBar.tsx @@ -20,7 +20,9 @@ function OpportunityResult({ opportunity }: { opportunity: Opportunity }) { return ( <> diff --git a/src/components/element/opportunity/OpportunityTableRow.tsx b/src/components/element/opportunity/OpportunityTableRow.tsx index 238684ba..5e1a9ad3 100644 --- a/src/components/element/opportunity/OpportunityTableRow.tsx +++ b/src/components/element/opportunity/OpportunityTableRow.tsx @@ -3,6 +3,7 @@ import { Link } from "@remix-run/react"; import type { BoxProps } from "dappkit"; import { Dropdown, Group, Icon, Icons, PrimitiveTag, Text, Title, Value } from "dappkit"; import { mergeClass } from "dappkit"; +import config from "merkl.config"; import { useOverflowingRef } from "packages/dappkit/src/hooks/events/useOverflowing"; import { useMemo } from "react"; import type { OpportunityNavigationMode } from "src/config/opportunity"; @@ -115,7 +116,11 @@ export default function OpportunityTableRow({ "text-nowrap whitespace-nowrap text-ellipsis min-w-0 inline-block overflow-hidden", overflowing && "hover:overflow-visible hover:animate-textScroll hover:text-clip", )}> - {opportunity.name} + + {config.opprtunityPercentage + ? opportunity.name + : opportunity.name.replace(/\s*\d+(\.\d+)?%$/, "").trim()} + diff --git a/src/components/element/tvl/TvlSection.tsx b/src/components/element/tvl/TvlSection.tsx index 458ce09d..58a01609 100644 --- a/src/components/element/tvl/TvlSection.tsx +++ b/src/components/element/tvl/TvlSection.tsx @@ -100,7 +100,7 @@ export default function TvlSection({ opportunity }: TvlSectionProps) { )} - + {breakdown.value} diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 333bfc5e..2048d36d 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -44,7 +44,7 @@ export default function Header() { return Object.assign( { home }, { - claims: { + dashboard: { icon: "RiDashboardFill", route: user ? `/users/${user}` : "/users", key: uuidv4(), @@ -97,7 +97,7 @@ export default function Header() { })} - + {config.header.searchbar.enabled && } diff --git a/src/config/type.ts b/src/config/type.ts index 0f1d189f..f323b8e2 100644 --- a/src/config/type.ts +++ b/src/config/type.ts @@ -25,11 +25,17 @@ export type MerklConfig = { deposit?: boolean; opportunityNavigationMode?: OpportunityNavigationMode; rewardsNavigationMode?: RewardsNavigationMode; + opprtunityPercentage: boolean; modes: Mode[]; wagmi: Parameters["0"]; appName: string; fonts?: { title: string[]; text: string[]; mono: string[] }; routes: routesType; + header: { + searchbar: { + enabled: boolean; + }; + }; images: { [name: string]: string; }; diff --git a/src/hooks/resources/useOpportunity.tsx b/src/hooks/resources/useOpportunity.tsx index ad30a75f..91d46848 100644 --- a/src/hooks/resources/useOpportunity.tsx +++ b/src/hooks/resources/useOpportunity.tsx @@ -1,6 +1,7 @@ import type { Opportunity } from "@merkl/api"; import type { Token } from "@merkl/api"; import { Icon, Value } from "dappkit"; +import config from "merkl.config"; import { useMemo } from "react"; import type { TagType } from "src/components/element/Tag"; import { v4 as uuidv4 } from "uuid"; @@ -134,7 +135,10 @@ export default function useOpportunity(opportunity: Opportunity) { rewardIcons, description, rewardsBreakdown, - ...opportunity, + opportunity: { + ...opportunity, + name: config.opprtunityPercentage ? opportunity.name : opportunity.name.replace(/\s*\d+(\.\d+)?%$/, "").trim(), + }, tags, herosData, }; diff --git a/src/routes/_merkl.(home).tsx b/src/routes/_merkl.(home).tsx index 93edf215..746921af 100644 --- a/src/routes/_merkl.(home).tsx +++ b/src/routes/_merkl.(home).tsx @@ -1,5 +1,6 @@ import type { MetaFunction } from "@remix-run/node"; import { Outlet } from "@remix-run/react"; +import { I18n } from "src/I18n"; import Hero from "src/components/composite/Hero"; export const meta: MetaFunction = () => { @@ -11,8 +12,8 @@ export default function Index() { + title={I18n.trad.get.pages.opportunities.title} + description={I18n.trad.get.pages.opportunities.description}> ); diff --git a/src/routes/_merkl.opportunities.$chain.$type.$id.tsx b/src/routes/_merkl.opportunities.$chain.$type.$id.tsx index f56ed0f6..41c452ec 100644 --- a/src/routes/_merkl.opportunities.$chain.$type.$id.tsx +++ b/src/routes/_merkl.opportunities.$chain.$type.$id.tsx @@ -1,6 +1,7 @@ import type { Chain } from "@merkl/api"; import { type LoaderFunctionArgs, type MetaFunction, json } from "@remix-run/node"; import { Meta, Outlet, useLoaderData } from "@remix-run/react"; +import config from "merkl.config"; import { useMemo } from "react"; import { Cache } from "src/api/services/cache.service"; import { ChainService } from "src/api/services/chain.service"; @@ -17,20 +18,23 @@ export async function loader({ params: { id, type, chain: chainId } }: LoaderFun const chain = await ChainService.get({ search: chainId }); - const opportunity = await OpportunityService.getCampaignsByParams({ + const rawOpportunity = await OpportunityService.getCampaignsByParams({ chainId: chain.id, type: type, identifier: id, }); - - return json({ opportunity, chain }); + return json({ rawOpportunity, chain }); } export const clientLoader = Cache.wrap("opportunity", 300); export const meta: MetaFunction = ({ data, error }) => { if (error) return [{ title: error }]; - return [{ title: `${data?.opportunity.name} on Merkl` }]; + return [ + { + title: `${data?.rawOpportunity.name} on Merkl`, + }, + ]; }; export type OutletContextOpportunity = { @@ -39,11 +43,11 @@ export type OutletContextOpportunity = { }; export default function Index() { - const { opportunity, chain } = useLoaderData(); - const { tags, description, link, herosData } = useOpportunity(opportunity); + const { rawOpportunity, chain } = useLoaderData(); + const { tags, description, link, herosData, opportunity } = useOpportunity(rawOpportunity); const styleName = useMemo(() => { - const spaced = opportunity?.name.split(" "); + const spaced = opportunity.name.split(" "); return spaced .map(str => { @@ -75,8 +79,11 @@ export default function Index() { ({ src: t.icon }))} breadcrumbs={[ - { link: "/", name: "Opportunities" }, - { link: "/", name: opportunity.name }, + { link: config.routes.opportunities?.route ?? "/", name: "Opportunities" }, + { + link: "/", + name: opportunity.name, + }, ]} title={styleName} description={description}