Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions examples/next-app-router/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";
import TMAWrapper from "@/components/TMAWrapper";
import { ErrorBoundary } from "@/components/ErrorBoundary";
import { EnvUnsupported } from "@/components/EnvUnsupported";

const geistSans = localFont({
src: "./fonts/GeistVF.woff",
Expand All @@ -27,8 +30,12 @@ export default function RootLayout({
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
>
<ErrorBoundary fallback={<EnvUnsupported />}>
<TMAWrapper>
{children}
</TMAWrapper>
</ErrorBoundary>
</body>
</html>
);
Expand Down
10 changes: 0 additions & 10 deletions examples/next-app-router/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,6 @@ export default function Home() {
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
app/page.tsx
</code>
.
</li>
<li>Save and see your changes instantly.</li>
</ol>
<ReadAccount />
</main>
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
Expand Down
39 changes: 39 additions & 0 deletions examples/next-app-router/components/EnvUnsupported.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use client'
import { Placeholder, AppRoot } from '@telegram-apps/telegram-ui';
import { retrieveLaunchParams, isColorDark, isRGB } from '@telegram-apps/sdk-react';
import { useMemo } from 'react';

export function EnvUnsupported() {
const [platform, isDark] = useMemo(() => {
let platform = 'base';
let isDark = false;
try {
const lp = retrieveLaunchParams();
const { bgColor } = lp.themeParams;
platform = lp.platform;
isDark = bgColor && isRGB(bgColor) ? isColorDark(bgColor) : false;
} catch {
/* empty */
}

return [platform, isDark];
}, []);

return (
<AppRoot
appearance={isDark ? 'dark' : 'light'}
platform={['macos', 'ios'].includes(platform) ? 'ios' : 'base'}
>
<Placeholder
header="Oops"
description="You are using too old Telegram client to run this application"
>
<img
alt="Telegram sticker"
src="https://xelene.me/telegram.gif"
style={{ display: 'block', width: '144px', height: '144px' }}
/>
</Placeholder>
</AppRoot>
);
}
45 changes: 45 additions & 0 deletions examples/next-app-router/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use client'
import {
Component,
type ComponentType,
type GetDerivedStateFromError,
type PropsWithChildren,
type ReactNode,
} from 'react';

export interface ErrorBoundaryProps extends PropsWithChildren {
fallback?: ReactNode | ComponentType<{ error: unknown }>;
}

interface ErrorBoundaryState {
error?: unknown;
}

export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
state: ErrorBoundaryState = {};

// eslint-disable-next-line max-len
static getDerivedStateFromError: GetDerivedStateFromError<ErrorBoundaryProps, ErrorBoundaryState> = (error) => ({ error });

componentDidCatch(error: Error) {
this.setState({ error });
}

render() {
const {
state: {
error,
},
props: {
fallback: Fallback,
children,
},
} = this;

return 'error' in this.state
? typeof Fallback === 'function'
? <Fallback error={error} />
: Fallback
: children;
}
}
97 changes: 70 additions & 27 deletions examples/next-app-router/components/ReadAccount.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,89 @@
'use client'

import React from 'react'
import React, { useState } from 'react'
import Image from "next/image";
import useUXUYSDK from '@/hooks/useUXUYSDK';
import { useLaunchParams } from '@telegram-apps/sdk-react';
import { Spoiler } from '@telegram-apps/telegram-ui';

type ReadAccountProps = { }

const ReadAccount: React.FC<ReadAccountProps> = ({ }) => {

const lp = useLaunchParams()

const sdk = useUXUYSDK()

const [address, setAddress] = useState()

const handleEVMClick = () => {
console.log(sdk, sdk?.ethereum)
sdk?.ethereum?.enable?.()
sdk?.ethereum?.enable?.().then(res => {
sdk?.ethereum
?.request({ method: "eth_accounts" })
.then((accounts) => {
const address = accounts[0]
setAddress(address)
})
.catch(err => {
console.log(err)
})
})
}

const handleSOLClick = () => {
sdk?.solana?.connect?.({}, false)
}

return (
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
onClick={handleEVMClick}
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
<>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
NAME:
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold w-screen">
{lp.initData?.user?.firstName} {lp.initData?.user?.lastName}
</code>
</li>
<li>
PLATEFORM:
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold w-screen">
{lp.platform}
</code>
</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
onClick={handleEVMClick}
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Connect EVM Account - {address}
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
onClick={handleSOLClick}
rel="noopener noreferrer"
>
Connect SOL Account
</a>
</div>
<Spoiler>
<div
style={{
background: "yellowgreen",
height: 200,
width: 200,
}}
/>
Get EVM Account
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
)
</Spoiler>
</>
);
}

export default ReadAccount
69 changes: 69 additions & 0 deletions examples/next-app-router/components/TMAWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use client'
import React, { useLayoutEffect, useState } from 'react'
import {
backButton,
settingsButton,
viewport,
themeParams,
miniApp,
initData,
$debug,
init as initSDK,
closingBehavior,
mainButton,
} from '@telegram-apps/sdk-react';
import '@telegram-apps/telegram-ui/dist/styles.css';

type TMAWrapperProps = { }

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();

// Check if all required components are supported.
if (!backButton.isSupported() || !miniApp.isSupported()) {
throw new Error('ERR_NOT_SUPPORTED');
}

// Mount all components used in the project.
miniApp.mount();
themeParams.mount();
backButton.mount();
mainButton.mount()
settingsButton.mount();
closingBehavior.mount()
initData.restore();

void viewport
.mount()
.catch(e => {
console.error('Something went wrong mounting the viewport', e);
})
.then(() => {
viewport.bindCssVars();
});

// Define components-related CSS variables.
miniApp.bindCssVars();
themeParams.bindCssVars();
}

const TMAWrapper: React.FC<React.PropsWithChildren<TMAWrapperProps>> = ({ children }) => {

const [initialized, setInitialized] = useState(false)

useLayoutEffect(() => {
init(true)
setInitialized(true)
}, [])

if (!initialized) return null

return children
}

export default TMAWrapper
5 changes: 4 additions & 1 deletion examples/next-app-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"dev": "next dev -H 0.0.0.0",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@telegram-apps/sdk": "^2.6.2",
"@telegram-apps/sdk-react": "^2.0.13",
"@telegram-apps/telegram-ui": "^2.1.8",
"@uxuycom/web3-tg-sdk": "^0.1.3",
"next": "15.0.3",
"react": "19.0.0-rc-66855b96-20241106",
Expand Down
Loading