From 9ab9880c0f9912234d90a0715b020957be83b618 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Tue, 10 Dec 2024 20:08:38 +0300 Subject: [PATCH] add files from official template --- apps/web/app/_assets/globals.css | 3 + apps/web/app/_assets/ton.svg | 1 + apps/web/app/error.tsx | 5 + apps/web/app/favicon.ico | Bin 0 -> 25931 bytes apps/web/app/init-data/page.tsx | 121 ++++ apps/web/app/launch-params/page.tsx | 33 + apps/web/app/layout.tsx | 30 +- apps/web/app/page.test.tsx | 8 - apps/web/app/page.tsx | 38 +- apps/web/app/theme-params/page.tsx | 30 + apps/web/app/ton-connect/page.tsx | 105 +++ apps/web/app/ton-connect/styles.css | 16 + .../components/DisplayData/DisplayData.tsx | 59 ++ apps/web/components/DisplayData/styles.css | 15 + apps/web/components/ErrorBoundary.tsx | 39 ++ apps/web/components/ErrorPage.tsx | 26 + apps/web/components/Link/Link.tsx | 48 ++ apps/web/components/Link/styles.css | 4 + .../LocaleSwitcher/LocaleSwitcher.tsx | 26 + apps/web/components/Page.tsx | 31 + apps/web/components/RGB/RGB.tsx | 15 + apps/web/components/RGB/styles.css | 12 + apps/web/components/Root/Root.tsx | 63 ++ apps/web/components/Root/styles.css | 10 + apps/web/core/i18n/config.ts | 10 + apps/web/core/i18n/i18n.ts | 18 + apps/web/core/i18n/locale.ts | 21 + apps/web/core/i18n/provider.tsx | 18 + apps/web/core/i18n/types.ts | 5 + apps/web/core/init.ts | 41 ++ apps/web/hooks/useClientOnce.ts | 9 + apps/web/hooks/useDidMount.ts | 14 + apps/web/hooks/useTelegramMock.ts | 75 +++ apps/web/next.config.js | 9 +- apps/web/package.json | 4 + apps/web/public/locales/en.json | 6 + apps/web/public/locales/ru.json | 6 + apps/web/tsconfig.json | 6 +- pnpm-lock.yaml | 617 ++++++++++++++++++ 39 files changed, 1562 insertions(+), 35 deletions(-) create mode 100644 apps/web/app/_assets/globals.css create mode 100644 apps/web/app/_assets/ton.svg create mode 100644 apps/web/app/error.tsx create mode 100644 apps/web/app/favicon.ico create mode 100644 apps/web/app/init-data/page.tsx create mode 100644 apps/web/app/launch-params/page.tsx delete mode 100644 apps/web/app/page.test.tsx create mode 100644 apps/web/app/theme-params/page.tsx create mode 100644 apps/web/app/ton-connect/page.tsx create mode 100644 apps/web/app/ton-connect/styles.css create mode 100644 apps/web/components/DisplayData/DisplayData.tsx create mode 100644 apps/web/components/DisplayData/styles.css create mode 100644 apps/web/components/ErrorBoundary.tsx create mode 100644 apps/web/components/ErrorPage.tsx create mode 100644 apps/web/components/Link/Link.tsx create mode 100644 apps/web/components/Link/styles.css create mode 100644 apps/web/components/LocaleSwitcher/LocaleSwitcher.tsx create mode 100644 apps/web/components/Page.tsx create mode 100644 apps/web/components/RGB/RGB.tsx create mode 100644 apps/web/components/RGB/styles.css create mode 100644 apps/web/components/Root/Root.tsx create mode 100644 apps/web/components/Root/styles.css create mode 100644 apps/web/core/i18n/config.ts create mode 100644 apps/web/core/i18n/i18n.ts create mode 100644 apps/web/core/i18n/locale.ts create mode 100644 apps/web/core/i18n/provider.tsx create mode 100644 apps/web/core/i18n/types.ts create mode 100644 apps/web/core/init.ts create mode 100644 apps/web/hooks/useClientOnce.ts create mode 100644 apps/web/hooks/useDidMount.ts create mode 100644 apps/web/hooks/useTelegramMock.ts create mode 100644 apps/web/public/locales/en.json create mode 100644 apps/web/public/locales/ru.json diff --git a/apps/web/app/_assets/globals.css b/apps/web/app/_assets/globals.css new file mode 100644 index 0000000..5d83f12 --- /dev/null +++ b/apps/web/app/_assets/globals.css @@ -0,0 +1,3 @@ +body { + background: var(--tg-theme-secondary-bg-color, white); +} diff --git a/apps/web/app/_assets/ton.svg b/apps/web/app/_assets/ton.svg new file mode 100644 index 0000000..1983df4 --- /dev/null +++ b/apps/web/app/_assets/ton.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/app/error.tsx b/apps/web/app/error.tsx new file mode 100644 index 0000000..ad511b4 --- /dev/null +++ b/apps/web/app/error.tsx @@ -0,0 +1,5 @@ +'use client'; + +import { ErrorPage } from '@/components/ErrorPage'; + +export default ErrorPage; diff --git a/apps/web/app/favicon.ico b/apps/web/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/apps/web/app/init-data/page.tsx b/apps/web/app/init-data/page.tsx new file mode 100644 index 0000000..c04b22e --- /dev/null +++ b/apps/web/app/init-data/page.tsx @@ -0,0 +1,121 @@ +'use client'; + +import { useMemo } from 'react'; +import { useSignal, initData, type User } from '@telegram-apps/sdk-react'; +import { List, Placeholder } from '@telegram-apps/telegram-ui'; + +import { + DisplayData, + type DisplayDataRow, +} from '@/components/DisplayData/DisplayData'; +import { Page } from '@/components/Page'; + +function getUserRows(user: User): DisplayDataRow[] { + return [ + { title: 'id', value: user.id.toString() }, + { title: 'username', value: user.username }, + { title: 'photo_url', value: user.photoUrl }, + { title: 'last_name', value: user.lastName }, + { title: 'first_name', value: user.firstName }, + { title: 'is_bot', value: user.isBot }, + { title: 'is_premium', value: user.isPremium }, + { title: 'language_code', value: user.languageCode }, + { title: 'allows_to_write_to_pm', value: user.allowsWriteToPm }, + { title: 'added_to_attachment_menu', value: user.addedToAttachmentMenu }, + ]; +} + +export default function InitDataPage() { + const initDataRaw = useSignal(initData.raw); + const initDataState = useSignal(initData.state); + + const initDataRows = useMemo(() => { + if (!initDataState || !initDataRaw) { + return; + } + const { + authDate, + hash, + queryId, + chatType, + chatInstance, + canSendAfter, + startParam, + } = initDataState; + return [ + { title: 'raw', value: initDataRaw }, + { title: 'auth_date', value: authDate.toLocaleString() }, + { title: 'auth_date (raw)', value: authDate.getTime() / 1000 }, + { title: 'hash', value: hash }, + { + title: 'can_send_after', + value: initData.canSendAfterDate()?.toISOString(), + }, + { title: 'can_send_after (raw)', value: canSendAfter }, + { title: 'query_id', value: queryId }, + { title: 'start_param', value: startParam }, + { title: 'chat_type', value: chatType }, + { title: 'chat_instance', value: chatInstance }, + ]; + }, [initDataState, initDataRaw]); + + const userRows = useMemo(() => { + return initDataState && initDataState.user + ? getUserRows(initDataState.user) + : undefined; + }, [initDataState]); + + const receiverRows = useMemo(() => { + return initDataState && initDataState.receiver + ? getUserRows(initDataState.receiver) + : undefined; + }, [initDataState]); + + const chatRows = useMemo(() => { + if (!initDataState?.chat) { + return; + } + const { + id, + title, + type, + username, + photoUrl, + } = initDataState.chat; + + return [ + { title: 'id', value: id.toString() }, + { title: 'title', value: title }, + { title: 'type', value: type }, + { title: 'username', value: username }, + { title: 'photo_url', value: photoUrl }, + ]; + }, [initData]); + + if (!initDataRows) { + return ( + + + Telegram sticker + + + ); + } + return ( + + + + {userRows && } + {receiverRows && } + {chatRows && } + + + ); +}; diff --git a/apps/web/app/launch-params/page.tsx b/apps/web/app/launch-params/page.tsx new file mode 100644 index 0000000..861a2f8 --- /dev/null +++ b/apps/web/app/launch-params/page.tsx @@ -0,0 +1,33 @@ +'use client'; + +import { useLaunchParams } from '@telegram-apps/sdk-react'; +import { List } from '@telegram-apps/telegram-ui'; + +import { DisplayData } from '@/components/DisplayData/DisplayData'; +import { Page } from '@/components/Page'; + +export default function LaunchParamsPage() { + const lp = useLaunchParams(); + + return ( + + + + + + ); +}; diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index e305402..e2145a6 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,22 +1,26 @@ -import '@repo/ui/globals.css'; +import { Root } from '@/components/Root/Root'; +import { I18nProvider } from '@/core/i18n/provider'; import { type Metadata } from 'next'; -import { Inter } from 'next/font/google'; - -const inter = Inter({ subsets: ['latin'] }); +import { getLocale } from 'next-intl/server'; +import { type PropsWithChildren } from 'react'; +import '@telegram-apps/telegram-ui/dist/styles.css'; +import './_assets/globals.css'; export const metadata: Metadata = { - description: 'Generated by create turbo', - title: 'Docs', + description: 'Your application description goes here', + title: 'Your Application Title Goes Here', }; -export default function RootLayout({ - children, -}: { - readonly children: React.ReactNode; -}): JSX.Element { +export default async function RootLayout({ children }: PropsWithChildren) { + const locale = await getLocale(); + return ( - - {children} + + + + {children} + + ); } diff --git a/apps/web/app/page.test.tsx b/apps/web/app/page.test.tsx deleted file mode 100644 index 756448a..0000000 --- a/apps/web/app/page.test.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import Page from './page'; -import { render, screen } from '@testing-library/react'; -import { expect, test } from 'vitest'; - -test('Page', () => { - render(); - expect(screen.getByRole('button', { name: 'Click me' })).toBeDefined(); -}); diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 9903988..cadfbb9 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,17 +1,35 @@ 'use client'; -import { Button } from '@repo/ui/components/ui/button'; -import { useState } from 'react'; -export default function Page() { - const [clicked, setClicked] = useState(false); +import { Link } from '@/components/Link/Link'; +import { LocaleSwitcher } from '@/components/LocaleSwitcher/LocaleSwitcher'; +import { Page } from '@/components/Page'; +import { Cell, List, Section } from '@telegram-apps/telegram-ui'; +import { useTranslations } from 'next-intl'; - function handleClick() { - setClicked(!clicked); - } +export default function Home() { + const t = useTranslations('i18n'); return ( -
- -
+ + +
+ + Init Data + + + Launch Parameters + + + Theme Parameters + +
+
+ +
+
+
); } diff --git a/apps/web/app/theme-params/page.tsx b/apps/web/app/theme-params/page.tsx new file mode 100644 index 0000000..7a68c9a --- /dev/null +++ b/apps/web/app/theme-params/page.tsx @@ -0,0 +1,30 @@ +'use client'; + +import { useSignal, themeParams } from '@telegram-apps/sdk-react'; +import { List } from '@telegram-apps/telegram-ui'; + +import { DisplayData } from '@/components/DisplayData/DisplayData'; +import { Page } from '@/components/Page'; + +export default function ThemeParamsPage() { + const tp = useSignal(themeParams.state); + + return ( + + + ({ + title: title + .replace(/[A-Z]/g, (m) => `_${m.toLowerCase()}`) + .replace(/background/, 'bg'), + value, + })) + } + /> + + + ); +}; diff --git a/apps/web/app/ton-connect/page.tsx b/apps/web/app/ton-connect/page.tsx new file mode 100644 index 0000000..a2c83c1 --- /dev/null +++ b/apps/web/app/ton-connect/page.tsx @@ -0,0 +1,105 @@ +'use client'; + +import { openLink } from '@telegram-apps/sdk-react'; +import { Page } from '@/components/Page'; +import { TonConnectButton, useTonWallet } from '@tonconnect/ui-react'; +import { + Avatar, + Cell, + List, + Navigation, + Placeholder, + Section, + Text, + Title, +} from '@telegram-apps/telegram-ui'; + +import { DisplayData } from '@/components/DisplayData/DisplayData'; + +import './styles.css'; + +export default function TONConnectPage() { + const wallet = useTonWallet(); + if (!wallet) { + return ( + + + + To display the data related to the TON Connect, it is required + to connect your wallet + + + + } + /> + + ); + } + + const { + account: { chain, publicKey, address }, + device: { + appName, + appVersion, + maxProtocolVersion, + platform, + features, + }, + } = wallet; + + return ( + + + {'imageUrl' in wallet && ( + <> +
+ + } + after={About wallet} + subtitle={wallet.appName} + onClick={(e) => { + e.preventDefault(); + openLink(wallet.aboutUrl); + }} + > + {wallet.name} + +
+ + + )} + + typeof f === 'object' ? f.name : undefined) + .filter(v => v) + .join(', '), + }, + ]} + /> +
+
+ ); +}; diff --git a/apps/web/app/ton-connect/styles.css b/apps/web/app/ton-connect/styles.css new file mode 100644 index 0000000..42eac0b --- /dev/null +++ b/apps/web/app/ton-connect/styles.css @@ -0,0 +1,16 @@ +.ton-connect-page__placeholder { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + box-sizing: border-box; +} + +.ton-connect-page__button { + margin: 16px auto 0; +} + +.ton-connect-page__button-connected { + margin: 16px 24px 16px auto; +} diff --git a/apps/web/components/DisplayData/DisplayData.tsx b/apps/web/components/DisplayData/DisplayData.tsx new file mode 100644 index 0000000..882c1c0 --- /dev/null +++ b/apps/web/components/DisplayData/DisplayData.tsx @@ -0,0 +1,59 @@ +import { isRGB } from '@telegram-apps/sdk-react'; +import { Cell, Checkbox, Section } from '@telegram-apps/telegram-ui'; +import type { FC, ReactNode } from 'react'; + +import { RGB } from '@/components/RGB/RGB'; +import { Link } from '@/components/Link/Link'; + +import './styles.css'; + +export type DisplayDataRow = + & { title: string } + & ( + | { type: 'link'; value?: string } + | { value: ReactNode } + ) + +export interface DisplayDataProps { + header?: ReactNode; + footer?: ReactNode; + rows: DisplayDataRow[]; +} + +export const DisplayData: FC = ({ header, rows }) => ( +
+ {rows.map((item, idx) => { + let valueNode: ReactNode; + + if (item.value === undefined) { + valueNode = empty; + } else { + if ('type' in item) { + valueNode = Open; + } else if (typeof item.value === 'string') { + valueNode = isRGB(item.value) + ? + : item.value; + } else if (typeof item.value === 'boolean') { + valueNode = ; + } else { + valueNode = item.value; + } + } + + return ( + + + {valueNode} + + + ); + })} +
+); diff --git a/apps/web/components/DisplayData/styles.css b/apps/web/components/DisplayData/styles.css new file mode 100644 index 0000000..e30b7be --- /dev/null +++ b/apps/web/components/DisplayData/styles.css @@ -0,0 +1,15 @@ +.display-data__header { + font-weight: 400; +} + +.display-data__line { + padding: 16px 24px; +} + +.display-data__line-title { + color: var(--tg-theme-subtitle-text-color); +} + +.display-data__line-value { + word-break: break-word; +} diff --git a/apps/web/components/ErrorBoundary.tsx b/apps/web/components/ErrorBoundary.tsx new file mode 100644 index 0000000..a9dfed1 --- /dev/null +++ b/apps/web/components/ErrorBoundary.tsx @@ -0,0 +1,39 @@ +import { + Component, + type ComponentType, + type GetDerivedStateFromError, + type PropsWithChildren, +} from 'react'; + +export interface ErrorBoundaryProps extends PropsWithChildren { + fallback: ComponentType<{ error: Error }>; +} + +interface ErrorBoundaryState { + error?: Error; +} + +export class ErrorBoundary extends Component { + state: ErrorBoundaryState = {}; + + // eslint-disable-next-line max-len + static getDerivedStateFromError: GetDerivedStateFromError = (error) => ({ error }); + + componentDidCatch(error: Error) { + this.setState({ error }); + } + + render() { + const { + state: { + error, + }, + props: { + fallback: Fallback, + children, + }, + } = this; + + return error ? : children; + } +} diff --git a/apps/web/components/ErrorPage.tsx b/apps/web/components/ErrorPage.tsx new file mode 100644 index 0000000..1ae3956 --- /dev/null +++ b/apps/web/components/ErrorPage.tsx @@ -0,0 +1,26 @@ +import { useEffect } from 'react'; + +export function ErrorPage({ + error, + reset, +}: { + error: Error & { digest?: string } + reset?: () => void +}) { + useEffect(() => { + // Log the error to an error reporting service + console.error(error); + }, [error]); + + return ( +
+

An unhandled error occurred!

+
+ + {error.message} + +
+ {reset && } +
+ ); +} \ No newline at end of file diff --git a/apps/web/components/Link/Link.tsx b/apps/web/components/Link/Link.tsx new file mode 100644 index 0000000..702a204 --- /dev/null +++ b/apps/web/components/Link/Link.tsx @@ -0,0 +1,48 @@ +import { openLink, classNames } from '@telegram-apps/sdk-react'; +import { type FC, type MouseEventHandler, type JSX, useCallback } from 'react'; +import { type LinkProps as NextLinkProps, default as NextLink } from 'next/link'; + +import './styles.css'; + +export interface LinkProps extends NextLinkProps, Omit { +} + +export const Link: FC = ({ + className, + onClick: propsOnClick, + href, + ...rest +}) => { + const onClick = useCallback>((e) => { + propsOnClick?.(e); + + // Compute if target path is external. In this case we would like to open link using + // TMA method. + let path: string; + if (typeof href === 'string') { + path = href; + } else { + const { search = '', pathname = '', hash = '' } = href; + path = `${pathname}?${search}#${hash}`; + } + + const targetUrl = new URL(path, window.location.toString()); + const currentUrl = new URL(window.location.toString()); + const isExternal = targetUrl.protocol !== currentUrl.protocol + || targetUrl.host !== currentUrl.host; + + if (isExternal) { + e.preventDefault(); + openLink(targetUrl.toString()); + } + }, [href, propsOnClick]); + + return ( + + ); +}; diff --git a/apps/web/components/Link/styles.css b/apps/web/components/Link/styles.css new file mode 100644 index 0000000..7251d50 --- /dev/null +++ b/apps/web/components/Link/styles.css @@ -0,0 +1,4 @@ +.link { + text-decoration: none; + color: var(--tg-theme-link-color); +} \ No newline at end of file diff --git a/apps/web/components/LocaleSwitcher/LocaleSwitcher.tsx b/apps/web/components/LocaleSwitcher/LocaleSwitcher.tsx new file mode 100644 index 0000000..f695d5e --- /dev/null +++ b/apps/web/components/LocaleSwitcher/LocaleSwitcher.tsx @@ -0,0 +1,26 @@ +'use client'; + +import { Select } from '@telegram-apps/telegram-ui'; +import { useLocale } from 'next-intl'; +import { FC } from 'react'; + +import { localesMap } from '@/core/i18n/config'; +import { setLocale } from '@/core/i18n/locale'; +import { Locale } from '@/core/i18n/types'; + +export const LocaleSwitcher: FC = () => { + const locale = useLocale(); + + const onChange = (value: string) => { + const locale = value as Locale; + setLocale(locale); + }; + + return ( + + ); +}; diff --git a/apps/web/components/Page.tsx b/apps/web/components/Page.tsx new file mode 100644 index 0000000..481aa28 --- /dev/null +++ b/apps/web/components/Page.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { backButton } from '@telegram-apps/sdk-react'; +import { PropsWithChildren, useEffect } from 'react'; +import { useRouter } from 'next/navigation'; + +export function Page({ children, back = true }: PropsWithChildren<{ + /** + * True if it is allowed to go back from this page. + * @default true + */ + back?: boolean +}>) { + const router = useRouter(); + + useEffect(() => { + if (back) { + backButton.show(); + } else { + backButton.hide(); + } + }, [back]); + + useEffect(() => { + return backButton.onClick(() => { + router.back(); + }); + }, [router]); + + return <>{children}; +} \ No newline at end of file diff --git a/apps/web/components/RGB/RGB.tsx b/apps/web/components/RGB/RGB.tsx new file mode 100644 index 0000000..5719bba --- /dev/null +++ b/apps/web/components/RGB/RGB.tsx @@ -0,0 +1,15 @@ +import { classNames, type RGB as RGBType } from '@telegram-apps/sdk-react'; +import type { FC } from 'react'; + +import './styles.css'; + +export type RGBProps = JSX.IntrinsicElements['div'] & { + color: RGBType; +}; + +export const RGB: FC = ({ color, className, ...rest }) => ( + + + {color} + +); diff --git a/apps/web/components/RGB/styles.css b/apps/web/components/RGB/styles.css new file mode 100644 index 0000000..fec0799 --- /dev/null +++ b/apps/web/components/RGB/styles.css @@ -0,0 +1,12 @@ +.rgb { + display: inline-flex; + align-items: center; + gap: 5px; +} + +.rgb__icon { + width: 18px; + aspect-ratio: 1; + border: 1px solid #555; + border-radius: 50%; +} \ No newline at end of file diff --git a/apps/web/components/Root/Root.tsx b/apps/web/components/Root/Root.tsx new file mode 100644 index 0000000..5c90b9c --- /dev/null +++ b/apps/web/components/Root/Root.tsx @@ -0,0 +1,63 @@ +'use client'; + +import { ErrorBoundary } from '@/components/ErrorBoundary'; +import { ErrorPage } from '@/components/ErrorPage'; +import { setLocale } from '@/core/i18n/locale'; +import { init } from '@/core/init'; +import { useClientOnce } from '@/hooks/useClientOnce'; +import { useDidMount } from '@/hooks/useDidMount'; +import { useTelegramMock } from '@/hooks/useTelegramMock'; +import { initData, miniApp, useLaunchParams, useSignal } from '@telegram-apps/sdk-react'; +import { AppRoot } from '@telegram-apps/telegram-ui'; +import { type PropsWithChildren, useEffect } from 'react'; +import './styles.css'; + +export function Root(props: PropsWithChildren) { + // Unfortunately, Telegram Mini Apps does not allow us to use all features of + // the Server Side Rendering. That's why we are showing loader on the server + // side. + const didMount = useDidMount(); + + return didMount ? ( + + + + ) : ( +
Loading
+ ); +} + +function RootInner({ children }: PropsWithChildren) { + const isDevelopment = process.env.NODE_ENV === 'development'; + + // Mock Telegram environment in development mode if needed. + if (isDevelopment) { + // eslint-disable-next-line react-hooks/rules-of-hooks + useTelegramMock(); + } + + const lp = useLaunchParams(); + const debug = isDevelopment || lp.startParam === 'debug'; + + // Initialize the library. + useClientOnce(() => { + init(debug); + }); + + const isDark = useSignal(miniApp.isDark); + const initDataUser = useSignal(initData.user); + + // Set the user locale. + useEffect(() => { + initDataUser && setLocale(initDataUser.languageCode); + }, [initDataUser]); + + return ( + + {children} + + ); +} diff --git a/apps/web/components/Root/styles.css b/apps/web/components/Root/styles.css new file mode 100644 index 0000000..40c6bbc --- /dev/null +++ b/apps/web/components/Root/styles.css @@ -0,0 +1,10 @@ +.root__loading { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} \ No newline at end of file diff --git a/apps/web/core/i18n/config.ts b/apps/web/core/i18n/config.ts new file mode 100644 index 0000000..915d153 --- /dev/null +++ b/apps/web/core/i18n/config.ts @@ -0,0 +1,10 @@ +export const defaultLocale = 'en'; + +export const timeZone = 'Europe/Amsterdam'; + +export const locales = [defaultLocale, 'ru'] as const; + +export const localesMap = [ + { key: 'en', title: 'English' }, + { key: 'ru', title: 'Русский' }, +]; diff --git a/apps/web/core/i18n/i18n.ts b/apps/web/core/i18n/i18n.ts new file mode 100644 index 0000000..c0ead13 --- /dev/null +++ b/apps/web/core/i18n/i18n.ts @@ -0,0 +1,18 @@ +import { defaultLocale, locales } from './config'; +import { getLocale } from './locale'; +import { type Locale } from './types'; +import { getRequestConfig } from 'next-intl/server'; + +const i18nRequestConfig = getRequestConfig(async () => { + const locale = (await getLocale()) as Locale; + + return { + locale, + messages: + locale === defaultLocale || !locales.includes(locale) + ? (await import(`@/public/locales/${defaultLocale}.json`)).default + : (await import(`@/public/locales/${locale}.json`)).default, + }; +}); + +export default i18nRequestConfig; diff --git a/apps/web/core/i18n/locale.ts b/apps/web/core/i18n/locale.ts new file mode 100644 index 0000000..83246a4 --- /dev/null +++ b/apps/web/core/i18n/locale.ts @@ -0,0 +1,21 @@ +//use server is required +"use server"; + +import { cookies } from "next/headers"; + +import { defaultLocale } from "./config"; +import type { Locale } from "./types"; + +// In this example the locale is read from a cookie. You could alternatively +// also read it from a database, backend service, or any other source. +const COOKIE_NAME = "NEXT_LOCALE"; + +const getLocale = async () => { + return cookies().get(COOKIE_NAME)?.value || defaultLocale; +}; + +const setLocale = async (locale?: string) => { + cookies().set(COOKIE_NAME, locale as Locale || defaultLocale); +}; + +export { getLocale, setLocale }; diff --git a/apps/web/core/i18n/provider.tsx b/apps/web/core/i18n/provider.tsx new file mode 100644 index 0000000..7570aa7 --- /dev/null +++ b/apps/web/core/i18n/provider.tsx @@ -0,0 +1,18 @@ +import { NextIntlClientProvider } from "next-intl"; +import { getMessages } from "next-intl/server"; +import React from "react"; + +import { timeZone } from "./config"; + +const I18nProvider: React.FC = async ({ + children, +}) => { + const messages = await getMessages(); + return ( + + {children} + + ); +}; + +export { I18nProvider }; diff --git a/apps/web/core/i18n/types.ts b/apps/web/core/i18n/types.ts new file mode 100644 index 0000000..bcd244f --- /dev/null +++ b/apps/web/core/i18n/types.ts @@ -0,0 +1,5 @@ +import type { locales } from "./config"; + +type Locale = (typeof locales)[number]; + +export type { Locale }; diff --git a/apps/web/core/init.ts b/apps/web/core/init.ts new file mode 100644 index 0000000..e24aa84 --- /dev/null +++ b/apps/web/core/init.ts @@ -0,0 +1,41 @@ +import { + $debug, + backButton, + initData, + init as initSDK, + miniApp, + themeParams, + viewport, +} from '@telegram-apps/sdk-react'; + +/** + * Initializes the application and configures its dependencies. + */ +export function init(debug: boolean): void { + // Set @telegram-apps/sdk-react debug mode. + $debug.set(debug); + + // Initialize special event handlers for Telegram Desktop, Android, iOS, etc. + // Also, configure the package. + initSDK(); + + // Mount all components used in the project. + if (backButton.isSupported()) backButton.mount(); + miniApp.mount(); + themeParams.mount(); + initData.restore(); + void viewport + .mount() + .then(() => { + // Define components-related CSS variables. + viewport.bindCssVars(); + miniApp.bindCssVars(); + themeParams.bindCssVars(); + }) + .catch((error) => { + console.error('Something went wrong mounting the viewport', error); + }); + + // Add Eruda if needed. + debug && import('eruda').then((library) => library.default.init()).catch(console.error); +} diff --git a/apps/web/hooks/useClientOnce.ts b/apps/web/hooks/useClientOnce.ts new file mode 100644 index 0000000..7963599 --- /dev/null +++ b/apps/web/hooks/useClientOnce.ts @@ -0,0 +1,9 @@ +import { useRef } from 'react'; + +export function useClientOnce(fn: () => void): void { + const canCall = useRef(true); + if (typeof window !== 'undefined' && canCall.current) { + canCall.current = false; + fn(); + } +} \ No newline at end of file diff --git a/apps/web/hooks/useDidMount.ts b/apps/web/hooks/useDidMount.ts new file mode 100644 index 0000000..e77f5ae --- /dev/null +++ b/apps/web/hooks/useDidMount.ts @@ -0,0 +1,14 @@ +import { useEffect, useState } from 'react'; + +/** + * @return True, if component was mounted. + */ +export function useDidMount(): boolean { + const [didMount, setDidMount] = useState(false); + + useEffect(() => { + setDidMount(true); + }, []); + + return didMount; +} \ No newline at end of file diff --git a/apps/web/hooks/useTelegramMock.ts b/apps/web/hooks/useTelegramMock.ts new file mode 100644 index 0000000..c0ca8f1 --- /dev/null +++ b/apps/web/hooks/useTelegramMock.ts @@ -0,0 +1,75 @@ +import { useClientOnce } from '@/hooks/useClientOnce'; +import { + isTMA, + type LaunchParams, + mockTelegramEnv, + parseInitData, + retrieveLaunchParams, +} from '@telegram-apps/sdk-react'; + +/** + * Mocks Telegram environment in development mode. + */ +export function useTelegramMock(): void { + useClientOnce(() => { + if (!sessionStorage.getItem('env-mocked') && isTMA('simple')) { + return; + } + + // Determine which launch params should be applied. We could already + // apply them previously, or they may be specified on purpose using the + // default launch parameters transmission method. + let lp: LaunchParams | undefined; + try { + lp = retrieveLaunchParams(); + } catch { + const initDataRaw = new URLSearchParams([ + [ + 'user', + JSON.stringify({ + allows_write_to_pm: true, + first_name: 'Andrew', + id: 99_281_932, + is_premium: true, + language_code: 'en', + last_name: 'Rogue', + username: 'rogue', + }), + ], + ['hash', '89d6079ad6762351f38c6dbbc41bb53048019256a9443988af7a48bcad16ba31'], + ['auth_date', '1716922846'], + ['start_param', 'debug'], + ['chat_type', 'sender'], + ['chat_instance', '8428209589180549439'], + ]).toString(); + + lp = { + initData: parseInitData(initDataRaw), + initDataRaw, + platform: 'tdesktop', + themeParams: { + accentTextColor: '#6ab2f2', + bgColor: '#17212b', + buttonColor: '#5288c1', + buttonTextColor: '#ffffff', + destructiveTextColor: '#ec3942', + headerBgColor: '#17212b', + hintColor: '#708499', + linkColor: '#6ab3f3', + secondaryBgColor: '#232e3c', + sectionBgColor: '#17212b', + sectionHeaderTextColor: '#6ab3f3', + subtitleTextColor: '#708499', + textColor: '#f5f5f5', + }, + version: '8', + }; + } + + sessionStorage.setItem('env-mocked', '1'); + mockTelegramEnv(lp); + console.warn( + '⚠️ As long as the current environment was not considered as the Telegram-based one, it was mocked. Take a note, that you should not do it in production and current behavior is only specific to the development process. Environment mocking is also applied only in development mode. So, after building the application, you will not see this behavior and related warning, leading to crashing the application outside Telegram.', + ); + }); +} diff --git a/apps/web/next.config.js b/apps/web/next.config.js index 0f656ae..f183c05 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -1,7 +1,10 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { +import createNextIntlPlugin from 'next-intl/plugin'; + +const withNextIntl = createNextIntlPlugin('./core/i18n/i18n.ts'); + +const nextConfig = withNextIntl({ reactStrictMode: true, transpilePackages: ['@repo/ui'], -}; +}); export default nextConfig; diff --git a/apps/web/package.json b/apps/web/package.json index 9e81b07..b60222d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,8 +17,11 @@ "dependencies": { "@apollo/client": "catalog:", "@repo/ui": "workspace:*", + "@telegram-apps/sdk-react": "^2.0.19", + "@telegram-apps/telegram-ui": "^2.1.8", "graphql": "catalog:", "next": "catalog:", + "next-intl": "^3.26.0", "react": "catalog:", "react-dom": "catalog:" }, @@ -38,6 +41,7 @@ "@types/react-dom": "catalog:", "@vitejs/plugin-react": "catalog:", "autoprefixer": "catalog:", + "eruda": "^3.4.1", "jsdom": "catalog:", "lint-staged": "catalog:", "postcss": "catalog:", diff --git a/apps/web/public/locales/en.json b/apps/web/public/locales/en.json new file mode 100644 index 0000000..1d633ec --- /dev/null +++ b/apps/web/public/locales/en.json @@ -0,0 +1,6 @@ +{ + "i18n": { + "header": "Application supports i18n", + "footer": "You can select a different language from the dropdown menu." + } +} \ No newline at end of file diff --git a/apps/web/public/locales/ru.json b/apps/web/public/locales/ru.json new file mode 100644 index 0000000..fbaf2c3 --- /dev/null +++ b/apps/web/public/locales/ru.json @@ -0,0 +1,6 @@ +{ + "i18n": { + "header": "Поддержка i18n", + "footer": "Вы можете выбрать другой язык в выпадающем меню." + } +} \ No newline at end of file diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 24e7548..4f8e45b 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -5,7 +5,11 @@ { "name": "next" } - ] + ], + "baseUrl": ".", + "paths": { + "@/*": ["./*"] + } }, "include": [ "next-env.d.ts", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 881c079..241d7d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,12 +143,21 @@ importers: '@repo/ui': specifier: workspace:* version: link:../../packages/ui + '@telegram-apps/sdk-react': + specifier: ^2.0.19 + version: 2.0.19(@types/react@19.0.1)(react@19.0.0) + '@telegram-apps/telegram-ui': + specifier: ^2.1.8 + version: 2.1.8(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) graphql: specifier: 'catalog:' version: 16.9.0 next: specifier: 'catalog:' version: 15.0.4(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + next-intl: + specifier: ^3.26.0 + version: 3.26.0(next@15.0.4(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) react: specifier: 'catalog:' version: 19.0.0 @@ -201,6 +210,9 @@ importers: autoprefixer: specifier: 'catalog:' version: 10.4.20(postcss@8.4.49) + eruda: + specifier: ^3.4.1 + version: 3.4.1 jsdom: specifier: 'catalog:' version: 25.0.1 @@ -1123,6 +1135,36 @@ packages: resolution: {integrity: sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@floating-ui/core@1.6.8': + resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} + + '@floating-ui/dom@1.6.12': + resolution: {integrity: sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==} + + '@floating-ui/react-dom@2.1.2': + resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.8': + resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} + + '@formatjs/ecma402-abstract@2.3.1': + resolution: {integrity: sha512-Ip9uV+/MpLXWRk03U/GzeJMuPeOXpJBSB5V1tjA6kJhvqssye5J5LoYLc7Z5IAHb7nR62sRoguzrFiVCP/hnzw==} + + '@formatjs/fast-memoize@2.2.5': + resolution: {integrity: sha512-6PoewUMrrcqxSoBXAOJDiW1m+AmkrAj0RiXnOMD59GRaswjXhm3MDhgepXPBgonc09oSirAJTsAggzAGQf6A6g==} + + '@formatjs/icu-messageformat-parser@2.9.7': + resolution: {integrity: sha512-cuEHyRM5VqLQobANOjtjlgU7+qmk9Q3fDQuBiRRJ3+Wp3ZoZhpUPtUfuimZXsir6SaI2TaAJ+SLo9vLnV5QcbA==} + + '@formatjs/icu-skeleton-parser@1.8.11': + resolution: {integrity: sha512-8LlHHE/yL/zVJZHAX3pbKaCjZKmBIO6aJY1mkVh4RMSEu/2WRZ4Ysvv3kKXJ9M8RJLBHdnk1/dUQFdod1Dt7Dw==} + + '@formatjs/intl-localematcher@0.5.9': + resolution: {integrity: sha512-8zkGu/sv5euxbjfZ/xmklqLyDGQSxsLqg8XOq88JW3cmJtzhCP8EtSJXlaKZnVO4beEaoiT9wj4eIoCQ9smwxA==} + '@graphql-codegen/add@5.0.3': resolution: {integrity: sha512-SxXPmramkth8XtBlAHu4H4jYcYXM/o3p01+psU+0NADQowA8jtYkK6MW5rV6T+CxkEaNZItfSmZRPgIuypcqnA==} peerDependencies: @@ -1681,6 +1723,9 @@ packages: engines: {node: '>=18'} hasBin: true + '@radix-ui/primitive@1.1.0': + resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} + '@radix-ui/react-compose-refs@1.1.0': resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} peerDependencies: @@ -1690,6 +1735,111 @@ packages: '@types/react': optional: true + '@radix-ui/react-context@1.1.1': + resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.2': + resolution: {integrity: sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.1': + resolution: {integrity: sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.1': + resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.0': + resolution: {integrity: sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.0': + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-portal@1.1.2': + resolution: {integrity: sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.1': + resolution: {integrity: sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.0.0': + resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.1.0': resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} peerDependencies: @@ -1699,6 +1849,42 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.0': + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@repeaterjs/repeater@3.0.4': resolution: {integrity: sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==} @@ -1812,6 +1998,43 @@ packages: '@swc/helpers@0.5.13': resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} + '@telegram-apps/bridge@1.7.1': + resolution: {integrity: sha512-oRbznpIC4UibMVygQ+tcS0ZSKx7DaI07MXQF42VETQ/VOCKeaWZeQFUifo4A+CzT6XMGo2hyse/CQP9ziX0H7g==} + + '@telegram-apps/navigation@1.0.9': + resolution: {integrity: sha512-Ur24luu+fizrKCDQAoJWQzMj+IwNiqtQlrITz42DORKSohj5yvf9kD5AJO2r9sHC+iC2pLXHCn1dV34o6tbeaQ==} + + '@telegram-apps/sdk-react@2.0.19': + resolution: {integrity: sha512-mQfBspQd+KbNW9gp26b2QN+T+SFiYfjZV1X91mM68hIPnChPjwCJRTZrNtx+nvT49B/NURMMtTT/eF84IX1ltA==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@telegram-apps/sdk@2.9.1': + resolution: {integrity: sha512-fW5e0B7yt4jIlEJfXzZwrY2cYufyXQw6xIu2eRfKl1l6N/ETIwZZRr6jOOsRCzzpWUfqePvwItqKIuRf8mGcog==} + + '@telegram-apps/signals@1.1.0': + resolution: {integrity: sha512-5qN7cU8t3l7n0cKcnzc/1TYKJTwAggUinfwbLHL1SYmB47pBHjCvfsRiYliFohk6lb635SBmNuVZL6LHFmGZaw==} + + '@telegram-apps/telegram-ui@2.1.8': + resolution: {integrity: sha512-Y4mIRzBJDerFvF6yzsfwHpu2tKEMHnnnQgdnLTwIdXNVPq/sJTaY66Qxbh9eeO5+tct01VcAx4E70/1fMvnp8A==} + engines: {node: '>=18.0.0', npm: '>=7.0.0'} + peerDependencies: + react: ^18.2.0 + react-dom: ^18.2.0 + + '@telegram-apps/toolkit@1.0.0': + resolution: {integrity: sha512-fSVoveLuMzwRKWeXEufMSXxH+HvjsFKb1DeT3pG5qLpnb2rdtejnNcwAt6WEPtiZ3a4YntYaFuR3KYgVv0ZxeQ==} + + '@telegram-apps/transformers@1.2.0': + resolution: {integrity: sha512-RufLdD044RPaAJdh+Mp/98JI+Wkp5mhX3WYCg6IZYFMRwu3QTu2FBwYmU9FdRmBF9utbcymSFrY1cqxh+Vtkfg==} + + '@telegram-apps/types@1.2.0': + resolution: {integrity: sha512-HhvnSCsqlJpes5ZGsZP/qbDNq8eLLnjgZKaF5NRsDqAKUPvaIIFT1HdyDII/8EioUgoI4FHsP8MylK2Gzm2efg==} + '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} @@ -1831,6 +2054,9 @@ packages: '@types/react-dom': optional: true + '@twa-dev/types@7.10.0': + resolution: {integrity: sha512-BXHyNLXy+cPbOZ2qQq5ArPKcP4kUfuX3YBSFT0XUxtMNWAzvyMuYAiq59UTfQ8OnELXlfChrAoIT3c2pujBtcQ==} + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -2079,6 +2305,12 @@ packages: resolution: {integrity: sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==} engines: {node: '>=8'} + '@xelene/vaul-with-scroll-fix@0.1.4': + resolution: {integrity: sha512-R9J7y92rzZKIQLtFHKtzRsb4x8G26cvwGIDSf7OQZCdROFt7xic4Yz1BlFYjm8oiQsYV4SFSQfOzatZjvxz5XQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2145,6 +2377,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.4: + resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} + engines: {node: '>=10'} + aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -2618,6 +2854,9 @@ packages: resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -2684,6 +2923,9 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + eruda@3.4.1: + resolution: {integrity: sha512-RmaO5yD97URY/9Q0lye3cmmNPoXNKreeePIw7c/zllbscR92CjGFZFuQ70+0fLIvLcKW3Xha8DS8NFhmeNbEBQ==} + es-abstract@1.23.5: resolution: {integrity: sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==} engines: {node: '>= 0.4'} @@ -3216,6 +3458,10 @@ packages: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + get-set-props@0.1.0: resolution: {integrity: sha512-7oKuKzAGKj0ag+eWZwcGw2fjiZ78tXnXQoBgY0aU7ZOxTu4bB7hSuQSDgtKy978EDH062P5FmD2EWiDpQS9K9Q==} engines: {node: '>=0.10.0'} @@ -3447,6 +3693,9 @@ packages: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} + intl-messageformat@10.7.10: + resolution: {integrity: sha512-hp7iejCBiJdW3zmOe18FdlJu8U/JsADSDiBPQhfdSeI8B9POtvPRvPh3nMlvhYayGMKLv6maldhR7y3Pf1vkpw==} + invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} @@ -3935,6 +4184,16 @@ packages: resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} engines: {node: '>=18'} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + next-intl@3.26.0: + resolution: {integrity: sha512-gkamnHIANQzeW8xpTGRxd0xiOCztQhY8GDp79fgdlw0GioqrjTEfSWLhHkgaAtvHRbuh/ByJdwiEY5eNK9bUSQ==} + peerDependencies: + next: ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + next@15.0.4: resolution: {integrity: sha512-nuy8FH6M1FG0lktGotamQDCXhh5hZ19Vo0ht1AOIQWrYJLP598TIUagKtvJrfJ5AGwB/WmDqkKaKhMpVifvGPA==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} @@ -4338,6 +4597,36 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.6: + resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.6.0: + resolution: {integrity: sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.1: + resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + react@19.0.0: resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} @@ -5042,6 +5331,31 @@ packages: urlpattern-polyfill@8.0.2: resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==} + use-callback-ref@1.3.2: + resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-intl@3.26.0: + resolution: {integrity: sha512-HGXmpjGlbEv1uFZPfm557LK8p/hv0pKF9UwnrJeHUTxQx6bUGzMgpmPRLCVY3zkr7hfjy4LPwQJfk4Fhnn+dIg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + + use-sidecar@1.1.2: + resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -6262,6 +6576,49 @@ snapshots: dependencies: levn: 0.4.1 + '@floating-ui/core@1.6.8': + dependencies: + '@floating-ui/utils': 0.2.8 + + '@floating-ui/dom@1.6.12': + dependencies: + '@floating-ui/core': 1.6.8 + '@floating-ui/utils': 0.2.8 + + '@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@floating-ui/dom': 1.6.12 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + '@floating-ui/utils@0.2.8': {} + + '@formatjs/ecma402-abstract@2.3.1': + dependencies: + '@formatjs/fast-memoize': 2.2.5 + '@formatjs/intl-localematcher': 0.5.9 + decimal.js: 10.4.3 + tslib: 2.8.1 + + '@formatjs/fast-memoize@2.2.5': + dependencies: + tslib: 2.8.1 + + '@formatjs/icu-messageformat-parser@2.9.7': + dependencies: + '@formatjs/ecma402-abstract': 2.3.1 + '@formatjs/icu-skeleton-parser': 1.8.11 + tslib: 2.8.1 + + '@formatjs/icu-skeleton-parser@1.8.11': + dependencies: + '@formatjs/ecma402-abstract': 2.3.1 + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.5.9': + dependencies: + tslib: 2.8.1 + '@graphql-codegen/add@5.0.3(graphql@16.9.0)': dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.9.0) @@ -7082,12 +7439,108 @@ snapshots: dependencies: playwright: 1.49.1 + '@radix-ui/primitive@1.1.0': {} + '@radix-ui/react-compose-refs@1.1.0(@types/react@19.0.1)(react@19.0.0)': dependencies: react: 19.0.0 optionalDependencies: '@types/react': 19.0.1 + '@radix-ui/react-context@1.1.1(@types/react@19.0.1)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.1 + + '@radix-ui/react-dialog@1.1.2(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.0.1)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.1)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.1)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.0(@types/react@19.0.1)(react@19.0.0) + '@radix-ui/react-portal': 1.1.2(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.1(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.1.0(@types/react@19.0.1)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.1)(react@19.0.0) + aria-hidden: 1.2.4 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-remove-scroll: 2.6.0(@types/react@19.0.1)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.1 + '@types/react-dom': 19.0.1 + + '@radix-ui/react-dismissable-layer@1.1.1(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.0.1)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.1)(react@19.0.0) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.1)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.1 + '@types/react-dom': 19.0.1 + + '@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.1)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.1 + + '@radix-ui/react-focus-scope@1.1.0(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.0.1)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.1)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.1 + '@types/react-dom': 19.0.1 + + '@radix-ui/react-id@1.1.0(@types/react@19.0.1)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.1)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.1 + + '@radix-ui/react-portal@1.1.2(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.1)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.1 + '@types/react-dom': 19.0.1 + + '@radix-ui/react-presence@1.1.1(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.0.1)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.1)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.1 + '@types/react-dom': 19.0.1 + + '@radix-ui/react-primitive@2.0.0(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-slot': 1.1.0(@types/react@19.0.1)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.1 + '@types/react-dom': 19.0.1 + '@radix-ui/react-slot@1.1.0(@types/react@19.0.1)(react@19.0.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.0.1)(react@19.0.0) @@ -7095,6 +7548,32 @@ snapshots: optionalDependencies: '@types/react': 19.0.1 + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.1)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.1 + + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.1)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.1)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.1 + + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.1)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.1)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.1 + + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.1)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.1 + '@repeaterjs/repeater@3.0.4': {} '@repeaterjs/repeater@3.0.6': {} @@ -7174,6 +7653,57 @@ snapshots: dependencies: tslib: 2.8.1 + '@telegram-apps/bridge@1.7.1': + dependencies: + '@telegram-apps/signals': 1.1.0 + '@telegram-apps/toolkit': 1.0.0 + '@telegram-apps/transformers': 1.2.0 + '@telegram-apps/types': 1.2.0 + + '@telegram-apps/navigation@1.0.9': + dependencies: + '@telegram-apps/bridge': 1.7.1 + '@telegram-apps/signals': 1.1.0 + '@telegram-apps/toolkit': 1.0.0 + + '@telegram-apps/sdk-react@2.0.19(@types/react@19.0.1)(react@19.0.0)': + dependencies: + '@telegram-apps/sdk': 2.9.1 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.1 + + '@telegram-apps/sdk@2.9.1': + dependencies: + '@telegram-apps/bridge': 1.7.1 + '@telegram-apps/navigation': 1.0.9 + '@telegram-apps/signals': 1.1.0 + '@telegram-apps/toolkit': 1.0.0 + '@telegram-apps/transformers': 1.2.0 + + '@telegram-apps/signals@1.1.0': {} + + '@telegram-apps/telegram-ui@2.1.8(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@swc/helpers': 0.5.13 + '@twa-dev/types': 7.10.0 + '@xelene/vaul-with-scroll-fix': 0.1.4(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + '@telegram-apps/toolkit@1.0.0': {} + + '@telegram-apps/transformers@1.2.0': + dependencies: + '@telegram-apps/toolkit': 1.0.0 + '@telegram-apps/types': 1.2.0 + + '@telegram-apps/types@1.2.0': {} + '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.26.2 @@ -7195,6 +7725,8 @@ snapshots: '@types/react': 19.0.1 '@types/react-dom': 19.0.1 + '@twa-dev/types@7.10.0': {} + '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5': @@ -7541,6 +8073,15 @@ snapshots: dependencies: tslib: 2.8.1 + '@xelene/vaul-with-scroll-fix@0.1.4(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-dialog': 1.1.2(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: acorn: 8.14.0 @@ -7594,6 +8135,10 @@ snapshots: argparse@2.0.1: {} + aria-hidden@1.2.4: + dependencies: + tslib: 2.8.1 + aria-query@5.3.0: dependencies: dequal: 2.0.3 @@ -8122,6 +8667,8 @@ snapshots: detect-libc@2.0.3: optional: true + detect-node-es@1.1.0: {} + didyoumean@1.2.2: {} dir-glob@3.0.1: @@ -8176,6 +8723,8 @@ snapshots: dependencies: is-arrayish: 0.2.1 + eruda@3.4.1: {} + es-abstract@1.23.5: dependencies: array-buffer-byte-length: 1.0.1 @@ -8993,6 +9542,8 @@ snapshots: has-symbols: 1.0.3 hasown: 2.0.2 + get-nonce@1.0.1: {} + get-set-props@0.1.0: {} get-stream@8.0.1: {} @@ -9246,6 +9797,13 @@ snapshots: hasown: 2.0.2 side-channel: 1.0.6 + intl-messageformat@10.7.10: + dependencies: + '@formatjs/ecma402-abstract': 2.3.1 + '@formatjs/fast-memoize': 2.2.5 + '@formatjs/icu-messageformat-parser': 2.9.7 + tslib: 2.8.1 + invariant@2.2.4: dependencies: loose-envify: 1.4.0 @@ -9717,6 +10275,16 @@ snapshots: natural-orderby@5.0.0: {} + negotiator@1.0.0: {} + + next-intl@3.26.0(next@15.0.4(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0): + dependencies: + '@formatjs/intl-localematcher': 0.5.9 + negotiator: 1.0.0 + next: 15.0.4(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + use-intl: 3.26.0(react@19.0.0) + next@15.0.4(@babel/core@7.26.0)(@playwright/test@1.49.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: '@next/env': 15.0.4 @@ -10085,6 +10653,34 @@ snapshots: react-refresh@0.14.2: {} + react-remove-scroll-bar@2.3.6(@types/react@19.0.1)(react@19.0.0): + dependencies: + react: 19.0.0 + react-style-singleton: 2.2.1(@types/react@19.0.1)(react@19.0.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.1 + + react-remove-scroll@2.6.0(@types/react@19.0.1)(react@19.0.0): + dependencies: + react: 19.0.0 + react-remove-scroll-bar: 2.3.6(@types/react@19.0.1)(react@19.0.0) + react-style-singleton: 2.2.1(@types/react@19.0.1)(react@19.0.0) + tslib: 2.8.1 + use-callback-ref: 1.3.2(@types/react@19.0.1)(react@19.0.0) + use-sidecar: 1.1.2(@types/react@19.0.1)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.1 + + react-style-singleton@2.2.1(@types/react@19.0.1)(react@19.0.0): + dependencies: + get-nonce: 1.0.1 + invariant: 2.2.4 + react: 19.0.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.1 + react@19.0.0: {} read-cache@1.0.0: @@ -10846,6 +11442,27 @@ snapshots: urlpattern-polyfill@8.0.2: {} + use-callback-ref@1.3.2(@types/react@19.0.1)(react@19.0.0): + dependencies: + react: 19.0.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.1 + + use-intl@3.26.0(react@19.0.0): + dependencies: + '@formatjs/fast-memoize': 2.2.5 + intl-messageformat: 10.7.10 + react: 19.0.0 + + use-sidecar@1.1.2(@types/react@19.0.1)(react@19.0.0): + dependencies: + detect-node-es: 1.1.0 + react: 19.0.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.0.1 + util-deprecate@1.0.2: {} validate-npm-package-license@3.0.4: