Compare commits
1 Commits
master
...
tgFullscre
| Author | SHA1 | Date |
|---|---|---|
|
|
9dcc6860f4 |
|
|
@ -10,13 +10,13 @@
|
|||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@sentry/react": "^9.1.0",
|
||||
"@telegram-apps/sdk": "^3.5.1",
|
||||
"@ton/core": "^0.59.1",
|
||||
"@tonconnect/ui-react": "^2.0.2",
|
||||
"@vkruglikov/react-telegram-web-app": "^2.1.9",
|
||||
"axios": "^1.6.7",
|
||||
"buffer": "^6.0.3",
|
||||
"clsx": "^2.1.0",
|
||||
"jssha": "^3.3.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.51.0",
|
||||
|
|
@ -1404,6 +1404,113 @@
|
|||
"react": "^16.14.0 || 17.x || 18.x || 19.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@telegram-apps/bridge": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/bridge/-/bridge-2.4.0.tgz",
|
||||
"integrity": "sha512-Lp/vhspF7okK8zXvSWWirunKXOPE6Gr11o9VBne4VmKG/yHRhEW7Pf07ncPtXLLzI6wW8+VYc3khsHPABJymEw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@telegram-apps/signals": "^1.1.1",
|
||||
"@telegram-apps/toolkit": "^2.0.0",
|
||||
"@telegram-apps/transformers": "^2.2.0",
|
||||
"@telegram-apps/types": "^2.0.0",
|
||||
"better-promises": "^0.4.0",
|
||||
"error-kid": "^0.0.4",
|
||||
"mitt": "^3.0.1",
|
||||
"valibot": "1.0.0-beta.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@telegram-apps/navigation": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/navigation/-/navigation-1.0.13.tgz",
|
||||
"integrity": "sha512-TsUueB5LQp77GQHoMa93nq26Uw7GJjrFCPbyseMVU7aBBxAc+8CV2IYytRwcVp5sv/q7ThK5X4JaKn2V1yBHDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@telegram-apps/bridge": "^1.9.2",
|
||||
"@telegram-apps/signals": "^1.1.1",
|
||||
"@telegram-apps/toolkit": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@telegram-apps/navigation/node_modules/@telegram-apps/bridge": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/bridge/-/bridge-1.9.2.tgz",
|
||||
"integrity": "sha512-SJLcNWLXhbbZr9MiqFH/g2ceuitSJKMxUIZysK4zUNyTUNuonrQG80Q/yrO+XiNbKUj8WdDNM86NBARhuyyinQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@telegram-apps/signals": "^1.1.1",
|
||||
"@telegram-apps/toolkit": "^1.1.1",
|
||||
"@telegram-apps/transformers": "^1.2.2",
|
||||
"@telegram-apps/types": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@telegram-apps/navigation/node_modules/@telegram-apps/toolkit": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/toolkit/-/toolkit-1.1.1.tgz",
|
||||
"integrity": "sha512-+vhKx6ngfvjyTE6Xagl3z1TPVbfx5s7xAkcYzCdHYUo6T60jLIqLgyZMcI1UPoIAMuMu1pHoO+p8QNCj/+tFmw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@telegram-apps/navigation/node_modules/@telegram-apps/transformers": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/transformers/-/transformers-1.2.2.tgz",
|
||||
"integrity": "sha512-vvMwXckd1D7Ozc0h66PSUwF5QLrRV9HlGJFFeBuUex8QEk5mSPtsJkLiqB8aBbwuFDa91+TUSM/CxqPZO/e9YQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@telegram-apps/toolkit": "^1.1.1",
|
||||
"@telegram-apps/types": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@telegram-apps/navigation/node_modules/@telegram-apps/types": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/types/-/types-1.2.1.tgz",
|
||||
"integrity": "sha512-so4HLh7clur0YyMthi9KVIgWoGpZdXlFOuQjk3+Q5NAvJZ11nAheBSwPlGw/Ko92+zwvrSBE/lQyN2+p17RP+w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@telegram-apps/sdk": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/sdk/-/sdk-3.5.1.tgz",
|
||||
"integrity": "sha512-m/ynpSozXsqq6Kfb6M9fm8SD6x/+jqvTFT59FuuOBvC+G8eMd0F+fvZdr6Pj0I6IL9M67nrfm92JpTu/aVNEjw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@telegram-apps/bridge": "^2.4.0",
|
||||
"@telegram-apps/navigation": "^1.0.13",
|
||||
"@telegram-apps/signals": "^1.1.1",
|
||||
"@telegram-apps/toolkit": "^2.0.0",
|
||||
"@telegram-apps/transformers": "^2.2.0",
|
||||
"@telegram-apps/types": "^2.0.0",
|
||||
"better-promises": "^0.4.0",
|
||||
"error-kid": "^0.0.4",
|
||||
"valibot": "1.0.0-beta.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@telegram-apps/signals": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/signals/-/signals-1.1.1.tgz",
|
||||
"integrity": "sha512-vz37r8lemGpPzDiBRfqpXYBynzmy3SFnY6zfHsTZABTYYt0b0WQZyU5mFDqqqugGhka78Gy11xmr9csgy4YgGA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@telegram-apps/toolkit": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/toolkit/-/toolkit-2.0.0.tgz",
|
||||
"integrity": "sha512-1GKTLBNme1Phu/gFvgS9NWPq+LhPfzSIfnwhcF9I/6tCdufrLRcVaSMRiK9R4VDYD6iZUyj+a2l250qWAxxjQQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@telegram-apps/transformers": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/transformers/-/transformers-2.2.0.tgz",
|
||||
"integrity": "sha512-wqXXOukhEjZKhzdq5vG1LkxWL11DApbmUKzk+3nA/ki3TLyD3awVTOXbpoNdOwFl2xliIooYcsUOEl4WCyyLGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@telegram-apps/toolkit": "^2.0.0",
|
||||
"@telegram-apps/types": "^2.0.0",
|
||||
"valibot": "1.0.0-beta.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@telegram-apps/types": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@telegram-apps/types/-/types-2.0.0.tgz",
|
||||
"integrity": "sha512-yF499FJK82a2IDNDAQdrmVH3sgFZl/QFNdVZKgWpgtunIVJ1fres5wi9+4aUBRVIdQwZOZZqB/AOvYYuYXsq3Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@ton/core": {
|
||||
"version": "0.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@ton/core/-/core-0.59.1.tgz",
|
||||
|
|
@ -2189,6 +2296,15 @@
|
|||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/better-promises": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/better-promises/-/better-promises-0.4.0.tgz",
|
||||
"integrity": "sha512-AcKkTUSd4o1vMf41eBbHW1NkY7vrXeNI6etitGdQE54WFXsF2wkfonrKA06Za7lViRNyT/cMvj5z+DScqhYW8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"error-kid": "^0.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/big-integer": {
|
||||
"version": "1.6.52",
|
||||
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
|
||||
|
|
@ -2640,6 +2756,12 @@
|
|||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/error-kid": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/error-kid/-/error-kid-0.0.4.tgz",
|
||||
"integrity": "sha512-x+yQhY56SorLMnX6kOf+z3JCv2QBurcWEDcIjgxYtVr4fGeCfAtOdZOCyWttkHHDFPtL2PqnaRUmphbmALJd9w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.22.5",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz",
|
||||
|
|
@ -4222,15 +4344,6 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jssha": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz",
|
||||
"integrity": "sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
|
|
@ -4413,6 +4526,12 @@
|
|||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/mitt": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
|
||||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
|
|
@ -6153,7 +6272,7 @@
|
|||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
@ -6271,6 +6390,20 @@
|
|||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/valibot": {
|
||||
"version": "1.0.0-beta.14",
|
||||
"resolved": "https://registry.npmjs.org/valibot/-/valibot-1.0.0-beta.14.tgz",
|
||||
"integrity": "sha512-tLyV2rE5QL6U29MFy3xt4AqMrn+/HErcp2ZThASnQvPMwfSozjV1uBGKIGiegtZIGjinJqn0SlBdannf18wENA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.3.4",
|
||||
"@sentry/react": "^9.1.0",
|
||||
"@telegram-apps/sdk": "^3.5.1",
|
||||
"@ton/core": "^0.59.1",
|
||||
"@tonconnect/ui-react": "^2.0.2",
|
||||
"@vkruglikov/react-telegram-web-app": "^2.1.9",
|
||||
|
|
|
|||
|
|
@ -1,27 +1,30 @@
|
|||
import "~/app/styles/globals.css";
|
||||
import '~/app/styles/globals.css';
|
||||
import '~/shared/libs/buffer';
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useExpand, useWebApp } from "@vkruglikov/react-telegram-web-app";
|
||||
|
||||
import { Providers } from "~/app/providers";
|
||||
import { AppRouter } from "~/app/router";
|
||||
import { useEffect } from 'react';
|
||||
import { useExpand, useWebApp } from '@vkruglikov/react-telegram-web-app';
|
||||
|
||||
import { Providers } from '~/app/providers';
|
||||
import { AppRouter } from '~/app/router';
|
||||
import { Notification } from '~/shared/ui/notification';
|
||||
import { init } from '@telegram-apps/sdk';
|
||||
export const App = () => {
|
||||
const WebApp = useWebApp();
|
||||
const [, expand] = useExpand();
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
WebApp.enableClosingConfirmation();
|
||||
expand();
|
||||
|
||||
WebApp.setHeaderColor("#1d1d1b");
|
||||
WebApp.setBackgroundColor("#1d1d1b");
|
||||
WebApp.setHeaderColor('#1d1d1b');
|
||||
WebApp.setBackgroundColor('#1d1d1b');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Providers>
|
||||
<AppRouter />
|
||||
<Notification />
|
||||
</Providers>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import ReactPlayer from 'react-player/lazy';
|
||||
import { useTonConnectUI } from '@tonconnect/ui-react';
|
||||
import { useWebApp } from '@vkruglikov/react-telegram-web-app';
|
||||
|
||||
import { Button } from '~/shared/ui/button';
|
||||
import { usePurchaseContent, useViewContent } from '~/shared/services/content';
|
||||
import { fromNanoTON } from '~/shared/utils';
|
||||
|
|
@ -10,7 +9,8 @@ import { AudioPlayer } from '~/shared/ui/audio-player';
|
|||
import { useAuth } from '~/shared/services/auth';
|
||||
import { CongratsModal } from './components/congrats-modal';
|
||||
import { ErrorModal } from './components/error-modal';
|
||||
|
||||
import { useRootStore } from '~/shared/stores/root';
|
||||
import { useTgFullscreen } from '~/shared/hooks/use-tgfullscreen';
|
||||
type InvoiceStatus = 'paid' | 'failed' | 'cancelled' | 'pending';
|
||||
|
||||
// Add type for invoice event
|
||||
|
|
@ -21,7 +21,7 @@ interface InvoiceEvent {
|
|||
|
||||
export const ViewContentPage = () => {
|
||||
const WebApp = useWebApp();
|
||||
|
||||
const { addNotification } = useRootStore();
|
||||
const { data: content, refetch: refetchContent } = useViewContent(
|
||||
WebApp.initDataUnsafe?.start_param
|
||||
);
|
||||
|
|
@ -30,6 +30,8 @@ export const ViewContentPage = () => {
|
|||
|
||||
const [tonConnectUI] = useTonConnectUI();
|
||||
|
||||
const { isFullscreen, toggleFullscreen } = useTgFullscreen();
|
||||
|
||||
const auth = useAuth();
|
||||
const [isCongratsModal, setIsCongratsModal] = useState(false);
|
||||
const [isErrorModal, setIsErrorModal] = useState(false);
|
||||
|
|
@ -42,11 +44,12 @@ export const ViewContentPage = () => {
|
|||
|
||||
while (Date.now() - startTime < timeoutMs) {
|
||||
if (tonConnectUI.connected) {
|
||||
addNotification('Кошелек подключен', 'success');
|
||||
return true;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
||||
}
|
||||
|
||||
addNotification('Время ожидания подключения кошелька истекло', 'warning');
|
||||
return false; // Timed out
|
||||
};
|
||||
|
||||
|
|
@ -185,6 +188,31 @@ export const ViewContentPage = () => {
|
|||
<main className={'min-h-screen flex w-full flex-col gap-[50px] px-4 '}>
|
||||
{isCongratsModal && <CongratsModal onConfirm={handleConfirmCongrats} />}
|
||||
{isErrorModal && <ErrorModal onConfirm={handleErrorModal} />}
|
||||
<div
|
||||
className={`${isFullscreen ? 'left-[50%] translate-x-[-50%] fixed top-11 lg:top-2' : 'left-4 absolute top-2'} z-50`}
|
||||
>
|
||||
<button
|
||||
onClick={toggleFullscreen}
|
||||
className="bg-primary bg-opacity-80 w-8 h-8 flex items-center justify-center rounded-full text-white"
|
||||
>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2 6V2H6M8 2H12V6M12 8V12H8M6 12H2V8"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-[50px]">
|
||||
{content?.data?.content_type.startsWith('audio') &&
|
||||
content?.data?.display_options?.metadata?.image && (
|
||||
<div className={'mt-[30px] h-[314px] w-full'}>
|
||||
|
|
@ -195,28 +223,33 @@ export const ViewContentPage = () => {
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{content?.data?.content_type.startsWith('audio') ? (
|
||||
<AudioPlayer src={content?.data?.display_options?.content_url} />
|
||||
) : (
|
||||
<div className="w-full video-container">
|
||||
<ReactPlayer
|
||||
playsinline={true}
|
||||
controls={true}
|
||||
width="100%"
|
||||
height="auto"
|
||||
className="react-player"
|
||||
allowFullScreen={true}
|
||||
url={content?.data?.display_options?.content_url}
|
||||
config={{
|
||||
file: {
|
||||
attributes: {
|
||||
playsInline: true,
|
||||
autoPlay: true,
|
||||
poster:
|
||||
content?.data?.display_options?.metadata?.image || undefined,
|
||||
content?.data?.display_options?.metadata?.image ||
|
||||
undefined,
|
||||
},
|
||||
},
|
||||
}}
|
||||
url={content?.data?.display_options?.content_url}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
<section className={'flex flex-col'}>
|
||||
<h1 className={'text-[20px] font-bold'}>
|
||||
{content?.data?.display_options?.metadata?.name}
|
||||
|
|
@ -227,7 +260,6 @@ export const ViewContentPage = () => {
|
|||
{content?.data?.display_options?.metadata?.description}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div className="mt-auto pb-2">
|
||||
{content?.data?.downloadable && (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { useWebApp } from '@vkruglikov/react-telegram-web-app';
|
||||
import { viewport } from '@telegram-apps/sdk';
|
||||
|
||||
/**
|
||||
* Hook for managing Telegram WebApp fullscreen state
|
||||
* @returns {Object} Object containing isFullscreen state and toggleFullscreen function
|
||||
*/
|
||||
export const useTgFullscreen = () => {
|
||||
const WebApp = useWebApp();
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
|
||||
// Initialize viewport once on mount
|
||||
useEffect(() => {
|
||||
// Mount viewport if available
|
||||
if (viewport.mount.isAvailable() && !viewport.isMounted()) {
|
||||
viewport.mount();
|
||||
}
|
||||
|
||||
// Check current fullscreen state
|
||||
if (viewport.isFullscreen && typeof viewport.isFullscreen === 'function') {
|
||||
setIsFullscreen(viewport.isFullscreen());
|
||||
}
|
||||
|
||||
// Use the WebApp event system for tracking changes
|
||||
const handleViewportChanged = () => {
|
||||
if (WebApp.isExpanded) {
|
||||
setIsFullscreen(true);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
WebApp.onEvent('viewportChanged', handleViewportChanged);
|
||||
} catch (error) {
|
||||
console.warn('Could not add viewportChanged event listener:', error);
|
||||
}
|
||||
|
||||
return () => {
|
||||
try {
|
||||
WebApp.offEvent('viewportChanged', handleViewportChanged);
|
||||
} catch (error) {
|
||||
console.warn('Could not remove viewportChanged event listener:', error);
|
||||
}
|
||||
};
|
||||
}, [WebApp]);
|
||||
|
||||
const toggleFullscreen = useCallback(async () => {
|
||||
try {
|
||||
if (!isFullscreen) {
|
||||
// Try to enter fullscreen
|
||||
if (viewport.requestFullscreen && viewport.requestFullscreen.isAvailable()) {
|
||||
await viewport.requestFullscreen();
|
||||
setIsFullscreen(true);
|
||||
} else if (viewport.expand && viewport.expand.isAvailable()) {
|
||||
viewport.expand();
|
||||
setIsFullscreen(true);
|
||||
} else {
|
||||
WebApp.expand();
|
||||
setIsFullscreen(true);
|
||||
}
|
||||
} else {
|
||||
// Try to exit fullscreen
|
||||
if (viewport.exitFullscreen && viewport.exitFullscreen.isAvailable()) {
|
||||
await viewport.exitFullscreen();
|
||||
setIsFullscreen(false);
|
||||
} else {
|
||||
setIsFullscreen(false);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error toggling fullscreen:', error);
|
||||
|
||||
if (!isFullscreen) {
|
||||
try {
|
||||
WebApp.expand();
|
||||
setIsFullscreen(true);
|
||||
} catch {
|
||||
console.error('All fullscreen methods failed');
|
||||
}
|
||||
} else {
|
||||
setIsFullscreen(false);
|
||||
}
|
||||
}
|
||||
}, [WebApp, isFullscreen]);
|
||||
|
||||
return { isFullscreen, toggleFullscreen };
|
||||
};
|
||||
|
|
@ -1,10 +1,19 @@
|
|||
import { create } from "zustand";
|
||||
import { create } from 'zustand';
|
||||
|
||||
export type Royalty = {
|
||||
address: string;
|
||||
value: number;
|
||||
};
|
||||
|
||||
type NotificationType = 'success' | 'danger' | 'warning' | 'info';
|
||||
|
||||
interface Notification {
|
||||
id: string;
|
||||
message: string;
|
||||
type: NotificationType;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
type RootStore = {
|
||||
name: string;
|
||||
setName: (name: string) => void;
|
||||
|
|
@ -50,22 +59,26 @@ type RootStore = {
|
|||
|
||||
hashtags: string[];
|
||||
setHashtags: (hashtags: string[]) => void;
|
||||
|
||||
notifications: Notification[];
|
||||
addNotification: (message: string, type: NotificationType) => void;
|
||||
removeNotification: (id: string) => void;
|
||||
};
|
||||
|
||||
export const useRootStore = create<RootStore>((set) => ({
|
||||
name: "",
|
||||
name: '',
|
||||
setName: (name) => set({ name }),
|
||||
|
||||
author: "",
|
||||
author: '',
|
||||
setAuthor: (author) => set({ author }),
|
||||
|
||||
file: null,
|
||||
setFile: (file) => set({ file }),
|
||||
|
||||
fileType: "",
|
||||
fileType: '',
|
||||
setFileType: (fileType) => set({ fileType }),
|
||||
|
||||
fileSrc: "",
|
||||
fileSrc: '',
|
||||
setFileSrc: (fileSrc) => set({ fileSrc }),
|
||||
|
||||
allowCover: false,
|
||||
|
|
@ -97,4 +110,26 @@ export const useRootStore = create<RootStore>((set) => ({
|
|||
|
||||
hashtags: [],
|
||||
setHashtags: (hashtags: string[]) => set({ hashtags }),
|
||||
|
||||
notifications: [],
|
||||
|
||||
addNotification: (message: string, type: NotificationType = 'info') => {
|
||||
const id = Date.now().toString();
|
||||
const notification = {
|
||||
id,
|
||||
message,
|
||||
type,
|
||||
createdAt: Date.now(),
|
||||
};
|
||||
|
||||
set((state) => ({
|
||||
notifications: [...state.notifications, notification],
|
||||
}));
|
||||
},
|
||||
|
||||
removeNotification: (id: string) => {
|
||||
set((state) => ({
|
||||
notifications: state.notifications.filter((notification) => notification.id !== id),
|
||||
}));
|
||||
},
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useRootStore } from '~/shared/stores/root';
|
||||
|
||||
interface AnimatedNotification {
|
||||
id: string;
|
||||
message: string;
|
||||
type: 'success' | 'danger' | 'warning' | 'info';
|
||||
isExiting: boolean;
|
||||
}
|
||||
|
||||
export const Notification = () => {
|
||||
const { notifications, removeNotification } = useRootStore();
|
||||
const [animatedNotifications, setAnimatedNotifications] = useState<AnimatedNotification[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentIds = animatedNotifications.map((n) => n.id);
|
||||
const newNotifications = notifications
|
||||
.filter((n) => !currentIds.includes(n.id))
|
||||
.map((n) => ({ ...n, isExiting: false }));
|
||||
|
||||
if (newNotifications.length > 0) {
|
||||
newNotifications.forEach((notification) => {
|
||||
setTimeout(() => {
|
||||
handleRemove(notification.id);
|
||||
}, 4000);
|
||||
});
|
||||
|
||||
setAnimatedNotifications((prev) => [...prev, ...newNotifications]);
|
||||
}
|
||||
}, [notifications]);
|
||||
|
||||
// Функция для начала анимации удаления
|
||||
const handleRemove = (id: string) => {
|
||||
setAnimatedNotifications((prev) =>
|
||||
prev.map((n) => (n.id === id ? { ...n, isExiting: true } : n))
|
||||
);
|
||||
setTimeout(() => {
|
||||
setAnimatedNotifications((prev) => prev.filter((n) => n.id !== id));
|
||||
removeNotification(id);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-x-0 top-2 z-50 flex flex-col items-center gap-2 pointer-events-none">
|
||||
{animatedNotifications.map((notification) => (
|
||||
<div
|
||||
key={notification.id}
|
||||
onClick={() => handleRemove(notification.id)}
|
||||
className={`
|
||||
p-2 rounded shadow-md relative transition-all duration-300 max-w-sm
|
||||
transform origin-top pointer-events-auto cursor-pointer
|
||||
${notification.isExiting ? 'animate-fade-out' : 'animate-slide-in-top'}
|
||||
${getNotificationClass(notification.type)}
|
||||
`}
|
||||
>
|
||||
<div className="text-center">{notification.message}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getNotificationClass = (type: 'success' | 'danger' | 'warning' | 'info'): string => {
|
||||
const baseStyle = 'border border-white bg-opacity-70 backdrop-blur-sm';
|
||||
|
||||
switch (type) {
|
||||
case 'success':
|
||||
return `${baseStyle} bg-primary text-white`;
|
||||
case 'danger':
|
||||
return `${baseStyle} bg-primary text-white`;
|
||||
case 'warning':
|
||||
return `${baseStyle} bg-primary text-white`;
|
||||
case 'info':
|
||||
default:
|
||||
return `${baseStyle} bg-primary text-white`;
|
||||
}
|
||||
};
|
||||
|
|
@ -1,11 +1,31 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
gray: "#1d1d1b",
|
||||
primary: "#e40615",
|
||||
gray: '#1d1d1b',
|
||||
primary: '#e40615',
|
||||
},
|
||||
keyframes: {
|
||||
'slide-in-top': {
|
||||
'0%': {
|
||||
transform: 'translateY(-1rem)',
|
||||
opacity: '0',
|
||||
},
|
||||
'100%': {
|
||||
transform: 'translateY(0)',
|
||||
opacity: '1',
|
||||
},
|
||||
},
|
||||
'fade-out': {
|
||||
'0%': { opacity: '1' },
|
||||
'100%': { opacity: '0' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'slide-in-top': 'slide-in-top 0.3s ease-out forwards',
|
||||
'fade-out': 'fade-out 0.3s ease-out forwards',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
119
yarn.lock
119
yarn.lock
|
|
@ -441,6 +441,96 @@
|
|||
"@sentry/core" "9.1.0"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
|
||||
"@telegram-apps/bridge@^1.9.2":
|
||||
version "1.9.2"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/bridge/-/bridge-1.9.2.tgz"
|
||||
integrity sha512-SJLcNWLXhbbZr9MiqFH/g2ceuitSJKMxUIZysK4zUNyTUNuonrQG80Q/yrO+XiNbKUj8WdDNM86NBARhuyyinQ==
|
||||
dependencies:
|
||||
"@telegram-apps/signals" "^1.1.1"
|
||||
"@telegram-apps/toolkit" "^1.1.1"
|
||||
"@telegram-apps/transformers" "^1.2.2"
|
||||
"@telegram-apps/types" "^1.2.1"
|
||||
|
||||
"@telegram-apps/bridge@^2.4.0":
|
||||
version "2.4.0"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/bridge/-/bridge-2.4.0.tgz"
|
||||
integrity sha512-Lp/vhspF7okK8zXvSWWirunKXOPE6Gr11o9VBne4VmKG/yHRhEW7Pf07ncPtXLLzI6wW8+VYc3khsHPABJymEw==
|
||||
dependencies:
|
||||
"@telegram-apps/signals" "^1.1.1"
|
||||
"@telegram-apps/toolkit" "^2.0.0"
|
||||
"@telegram-apps/transformers" "^2.2.0"
|
||||
"@telegram-apps/types" "^2.0.0"
|
||||
better-promises "^0.4.0"
|
||||
error-kid "^0.0.4"
|
||||
mitt "^3.0.1"
|
||||
valibot "1.0.0-beta.14"
|
||||
|
||||
"@telegram-apps/navigation@^1.0.13":
|
||||
version "1.0.13"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/navigation/-/navigation-1.0.13.tgz"
|
||||
integrity sha512-TsUueB5LQp77GQHoMa93nq26Uw7GJjrFCPbyseMVU7aBBxAc+8CV2IYytRwcVp5sv/q7ThK5X4JaKn2V1yBHDQ==
|
||||
dependencies:
|
||||
"@telegram-apps/bridge" "^1.9.2"
|
||||
"@telegram-apps/signals" "^1.1.1"
|
||||
"@telegram-apps/toolkit" "^1.1.1"
|
||||
|
||||
"@telegram-apps/sdk@^3.5.1":
|
||||
version "3.5.1"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/sdk/-/sdk-3.5.1.tgz"
|
||||
integrity sha512-m/ynpSozXsqq6Kfb6M9fm8SD6x/+jqvTFT59FuuOBvC+G8eMd0F+fvZdr6Pj0I6IL9M67nrfm92JpTu/aVNEjw==
|
||||
dependencies:
|
||||
"@telegram-apps/bridge" "^2.4.0"
|
||||
"@telegram-apps/navigation" "^1.0.13"
|
||||
"@telegram-apps/signals" "^1.1.1"
|
||||
"@telegram-apps/toolkit" "^2.0.0"
|
||||
"@telegram-apps/transformers" "^2.2.0"
|
||||
"@telegram-apps/types" "^2.0.0"
|
||||
better-promises "^0.4.0"
|
||||
error-kid "^0.0.4"
|
||||
valibot "1.0.0-beta.14"
|
||||
|
||||
"@telegram-apps/signals@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/signals/-/signals-1.1.1.tgz"
|
||||
integrity sha512-vz37r8lemGpPzDiBRfqpXYBynzmy3SFnY6zfHsTZABTYYt0b0WQZyU5mFDqqqugGhka78Gy11xmr9csgy4YgGA==
|
||||
|
||||
"@telegram-apps/toolkit@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/toolkit/-/toolkit-1.1.1.tgz"
|
||||
integrity sha512-+vhKx6ngfvjyTE6Xagl3z1TPVbfx5s7xAkcYzCdHYUo6T60jLIqLgyZMcI1UPoIAMuMu1pHoO+p8QNCj/+tFmw==
|
||||
|
||||
"@telegram-apps/toolkit@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/toolkit/-/toolkit-2.0.0.tgz"
|
||||
integrity sha512-1GKTLBNme1Phu/gFvgS9NWPq+LhPfzSIfnwhcF9I/6tCdufrLRcVaSMRiK9R4VDYD6iZUyj+a2l250qWAxxjQQ==
|
||||
|
||||
"@telegram-apps/transformers@^1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/transformers/-/transformers-1.2.2.tgz"
|
||||
integrity sha512-vvMwXckd1D7Ozc0h66PSUwF5QLrRV9HlGJFFeBuUex8QEk5mSPtsJkLiqB8aBbwuFDa91+TUSM/CxqPZO/e9YQ==
|
||||
dependencies:
|
||||
"@telegram-apps/toolkit" "^1.1.1"
|
||||
"@telegram-apps/types" "^1.2.1"
|
||||
|
||||
"@telegram-apps/transformers@^2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/transformers/-/transformers-2.2.0.tgz"
|
||||
integrity sha512-wqXXOukhEjZKhzdq5vG1LkxWL11DApbmUKzk+3nA/ki3TLyD3awVTOXbpoNdOwFl2xliIooYcsUOEl4WCyyLGw==
|
||||
dependencies:
|
||||
"@telegram-apps/toolkit" "^2.0.0"
|
||||
"@telegram-apps/types" "^2.0.0"
|
||||
valibot "1.0.0-beta.14"
|
||||
|
||||
"@telegram-apps/types@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/types/-/types-1.2.1.tgz"
|
||||
integrity sha512-so4HLh7clur0YyMthi9KVIgWoGpZdXlFOuQjk3+Q5NAvJZ11nAheBSwPlGw/Ko92+zwvrSBE/lQyN2+p17RP+w==
|
||||
|
||||
"@telegram-apps/types@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/@telegram-apps/types/-/types-2.0.0.tgz"
|
||||
integrity sha512-yF499FJK82a2IDNDAQdrmVH3sgFZl/QFNdVZKgWpgtunIVJ1fres5wi9+4aUBRVIdQwZOZZqB/AOvYYuYXsq3Q==
|
||||
|
||||
"@ton/core@^0.59.1":
|
||||
version "0.59.1"
|
||||
resolved "https://registry.npmjs.org/@ton/core/-/core-0.59.1.tgz"
|
||||
|
|
@ -920,6 +1010,13 @@ base64-js@^1.3.1:
|
|||
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
better-promises@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmjs.org/better-promises/-/better-promises-0.4.0.tgz"
|
||||
integrity sha512-AcKkTUSd4o1vMf41eBbHW1NkY7vrXeNI6etitGdQE54WFXsF2wkfonrKA06Za7lViRNyT/cMvj5z+DScqhYW8A==
|
||||
dependencies:
|
||||
error-kid "^0.0.4"
|
||||
|
||||
big-integer@^1.6.16:
|
||||
version "1.6.52"
|
||||
resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz"
|
||||
|
|
@ -1234,6 +1331,11 @@ emoji-regex@^9.2.2:
|
|||
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz"
|
||||
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
|
||||
|
||||
error-kid@^0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.npmjs.org/error-kid/-/error-kid-0.0.4.tgz"
|
||||
integrity sha512-x+yQhY56SorLMnX6kOf+z3JCv2QBurcWEDcIjgxYtVr4fGeCfAtOdZOCyWttkHHDFPtL2PqnaRUmphbmALJd9w==
|
||||
|
||||
es-abstract@^1.22.1, es-abstract@^1.22.3:
|
||||
version "1.22.5"
|
||||
resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz"
|
||||
|
|
@ -2076,11 +2178,6 @@ json5@^2.2.3:
|
|||
resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
|
||||
jssha@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz"
|
||||
integrity sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==
|
||||
|
||||
jssha@3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz"
|
||||
|
|
@ -2245,6 +2342,11 @@ minimist@^1.2.0, minimist@^1.2.6:
|
|||
resolved "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz"
|
||||
integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==
|
||||
|
||||
mitt@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz"
|
||||
integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==
|
||||
|
||||
ms@^2.1.1, ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
||||
|
|
@ -3146,7 +3248,7 @@ typed-array-length@^1.0.5:
|
|||
is-typed-array "^1.1.13"
|
||||
possible-typed-array-names "^1.0.0"
|
||||
|
||||
typescript@^5.0.0, typescript@^5.2.2, typescript@>=4.2.0:
|
||||
typescript@^5.0.0, typescript@^5.2.2, typescript@>=4.2.0, typescript@>=5:
|
||||
version "5.3.3"
|
||||
resolved "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz"
|
||||
integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
|
||||
|
|
@ -3204,6 +3306,11 @@ util-deprecate@^1.0.2:
|
|||
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
valibot@1.0.0-beta.14:
|
||||
version "1.0.0-beta.14"
|
||||
resolved "https://registry.npmjs.org/valibot/-/valibot-1.0.0-beta.14.tgz"
|
||||
integrity sha512-tLyV2rE5QL6U29MFy3xt4AqMrn+/HErcp2ZThASnQvPMwfSozjV1uBGKIGiegtZIGjinJqn0SlBdannf18wENA==
|
||||
|
||||
vite-tsconfig-paths@^4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.1.tgz"
|
||||
|
|
|
|||
Loading…
Reference in New Issue