{"id":10470,"date":"2025-06-28T08:40:48","date_gmt":"2025-06-28T02:40:48","guid":{"rendered":"http:\/\/hobbi.st\/?page_id=10470"},"modified":"2025-07-17T11:20:57","modified_gmt":"2025-07-17T05:20:57","slug":"miniapp","status":"publish","type":"page","link":"https:\/\/hobbi.st\/ru\/miniapp\/","title":{"rendered":"miniapp"},"content":{"rendered":"<div data-elementor-type=\"wp-page\" data-elementor-id=\"10470\" class=\"elementor elementor-10470\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4e98c57 e-con-full e-grid e-con e-parent\" data-id=\"4e98c57\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2ecdde2 elementor-widget-mobile__width-inherit elementor-widget elementor-widget-ucaddon_hobbist_mini_app\" data-id=\"2ecdde2\" data-element_type=\"widget\" data-widget_type=\"ucaddon_hobbist_mini_app.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\n<!-- start hobbist mini app -->\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>WordPress Navigation Buttons<\/title>\n    <!-- Tailwind CSS CDN for styling, providing utility classes for rapid UI development -->\n    <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n    <!-- Font Awesome for icons, providing a wide range of vector icons -->\n    <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.0.0-beta3\/css\/all.min.css\" crossorigin=\"anonymous\" referrerpolicy=\"no-referrer\" \/>\n    <style>\n        \/* Import Inter font from Google Fonts for a modern and clean typography *\/\n        @import url('https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;600;700&display=swap');\n\n        body {\n            font-family: 'Inter', sans-serif;\n            margin: 0;\n            display: flex;\n            justify-content: center; \/* Center content horizontally *\/\n            align-items: center; \/* Center content vertically *\/\n            min-height: 100vh; \/* Ensure body takes at least the full viewport height *\/\n            background-color: #1f2937; \/* Darker charcoal background *\/\n            overflow: hidden; \/* Hide any overflow *\/\n        }\n\n        \/* Footer Tabs Styling - adapted to be a central button container *\/\n        .footer-tabs-container {\n            position: relative; \/* Changed from fixed to relative for Elementor widget placement *\/\n            width: 100%; \/* Full width within its container *\/\n            max-width: 768px; \/* Max width for desktop viewing *\/\n            z-index: 50;\n            background-color: rgba(26, 32, 44, 0.85);\n            border-radius: 0.75rem; \/* Rounded corners for the container *\/\n            height: 4.5rem; \/* Fixed height for the button bar *\/\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);\n            backdrop-filter: blur(8px);\n            -webkit-backdrop-filter: blur(8px);\n            padding: 0 0.5rem; \/* Added horizontal padding *\/\n            box-sizing: border-box;\n        }\n        .tab-buttons {\n            display: flex;\n            width: 100%;\n            height: 100%;\n            justify-content: space-evenly;\n            align-items: center;\n        }\n        .tab-button {\n            flex: 1;\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            padding: 0;\n            border-radius: 0.75rem; \/* Rounded buttons *\/\n            font-size: 0.85rem;\n            font-weight: 600;\n            cursor: pointer;\n            transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);\n            color: #a0aec0;\n            background-color: transparent;\n            border: none;\n            text-decoration: none;\n            position: relative;\n            height: 100%;\n            box-shadow: none;\n        }\n\n        \/* Specific styling for the 'Earn' tab to make it elevated and colored *\/\n        #tab-Earn {\n            position: relative;\n            width: 60px;\n            height: 60px;\n            padding: 0;\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            background-color: transparent;\n            background-image: none;\n            border-radius: 0;\n            clip-path: none;\n            box-shadow: none;\n            transform: translateY(-20px); \/* Lift the button higher *\/\n            z-index: 51;\n            color: #ffffff;\n            transition: all 0.3s cubic-bezier(0.25, 1, 0.25, 1);\n            flex-grow: 0;\n            flex-shrink: 0;\n            margin-left: auto;\n            margin-right: auto;\n            overflow: visible;\n        }\n\n        \/* Pseudo-element to create the hexagon background for the Earn button *\/\n        #tab-Earn::before {\n            content: '';\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background-color: #a67c00; \/* Gold color for the hexagon background *\/\n            clip-path: polygon(50% 0%, 93.3% 25%, 93.3% 75%, 50% 100%, 6.7% 75%, 6.7% 25%); \/* Hexagon shape *\/\n            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.4);\n            z-index: 0;\n            transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);\n            transform: rotate(0deg);\n        }\n\n        \/* Class for rotating the hexagon and the plus\/minus icon *\/\n        #tab-Earn.rotated-earn-button {\n            transform: translateY(-20px);\n        }\n        #tab-Earn.rotated-earn-button::before {\n            transform: rotate(90deg);\n        }\n\n        \/* Active state for Earn tab *\/\n        #tab-Earn.active {\n            transform: translateY(-22px);\n        }\n        #tab-Earn.active::before {\n            box-shadow: 0 8px 20px rgba(0, 0, 0, 0.6);\n        }\n        \/* Hover state for Earn tab *\/\n        #tab-Earn:hover {\n            transform: translateY(-22px);\n        }\n        #tab-Earn:hover::before {\n            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.7);\n        }\n\n        \/* Hide text on Earn tab, only icons are visible *\/\n        #tab-Earn span:last-child {\n            display: none;\n        }\n\n        \/* Style for the earn primary icon (coins) inside the uplifted button *\/\n        #tab-Earn .earn-main-icon {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%);\n            font-size: 1.8rem;\n            z-index: 1;\n            color: white;\n            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);\n        }\n\n        \/* Style for the plus\/times sign on the Earn button *\/\n        .earn-toggle-icon {\n            position: absolute;\n            top: 50%;\n            left: 50%;\n            transform: translate(-50%, -50%) translateY(-15px);\n            font-weight: 900;\n            color: #4ade80;\n            z-index: 3;\n            pointer-events: none;\n            transition: transform 0.25s ease-out;\n        }\n        \/* Rotate the plus\/minus icon with the hexagon *\/\n        #tab-Earn.rotated-earn-button .earn-toggle-icon {\n            transform: translate(-50%, -50%) translateY(-15px) rotate(90deg);\n        }\n\n        .earn-toggle-icon i.fas {\n            font-size: 2.2rem;\n            transition: transform 0.25s ease-out;\n            pointer-events: auto;\n            cursor: pointer;\n        }\n        .earn-toggle-icon.rotate-to-x i.fas {\n            transform: rotate(45deg);\n        }\n\n        \/* Standard tab button icon and text styling *\/\n        .tab-button .tab-icon {\n            font-size: 1.2rem;\n            margin-bottom: 0.25rem;\n            transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);\n        }\n        .tab-button:hover:not(.active) {\n            background-color: rgba(255, 255, 255, 0.2);\n            transform: translateY(-2px);\n            color: #e2e8f0;\n        }\n        .tab-button.active {\n            background-image: linear-gradient(to right, #3b82f6, #60a5fa);\n            color: #ffffff;\n            box-shadow: 0 5px 15px rgba(59, 130, 246, 0.5);\n            transform: translateY(-2px);\n            font-size: 0.95rem;\n        }\n        .tab-button.active .tab-icon {\n            color: #ffffff;\n        }\n        .tab-button.active:active {\n            transform: translateY(0);\n            box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);\n        }\n\n        \/* Radial Action Buttons for Earn Menu *\/\n        .radial-buttons-container {\n            position: absolute;\n            bottom: 100px; \/* Adjusted to be clearly above the lifted hexagon button *\/\n            left: 50%;\n            transform: translateX(-50%);\n            display: flex;\n            justify-content: center;\n            align-items: flex-end;\n            padding-bottom: 1rem;\n            pointer-events: none;\n            opacity: 0;\n            transition: opacity 0.3s ease-out;\n            z-index: 52;\n            min-width: 150px;\n        }\n        .radial-buttons-container.active {\n            opacity: 1;\n            pointer-events: auto;\n        }\n\n        .radial-action-button {\n            position: absolute;\n            background-color: #a67c00;\n            color: #000000;\n            width: 45px;\n            height: 45px;\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 1.1rem;\n            cursor: pointer;\n            box-shadow: 0 3px 8px rgba(0,0,0,0.3);\n            transition: all 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);\n            opacity: 0;\n            transform: translate(0, 0) scale(0);\n            z-index: 1;\n        }\n        .radial-action-button:hover {\n            background-color: #6b7280;\n            transform: scale(1.1);\n        }\n        .radial-action-button.show {\n            opacity: 1;\n            transform: translate(var(--x), var(--y)) scale(1);\n        }\n        \/* Specific positions for 3 radial buttons *\/\n        .radial-action-button:nth-child(1) {\n            --x: -55px;\n            --y: -15px;\n            transition-delay: 0.05s;\n        }\n        .radial-action-button:nth-child(2) {\n            --x: 0px;\n            --y: -55px;\n            transition-delay: 0.1s;\n        }\n        .radial-action-button:nth-child(3) {\n            --x: 55px;\n            --y: -15px;\n            transition-delay: 0.15s;\n        }\n    <\/style>\n<\/head>\n<body>\n    <!-- Footer container for navigation tabs -->\n    <div id=\"footerTabsContainer\" class=\"footer-tabs-container\">\n        <div class=\"tab-buttons\" id=\"tabButtonsContainer\">\n            <!-- Tab buttons will be inserted here dynamically by JavaScript -->\n        <\/div>\n    <\/div>\n\n    <script>\n        \/\/ Keep track of the currently active tab\n        let currentActiveTab = null;\n        \/\/ State variable for Earn menu expansion\n        let isEarnMenuOpen = false;\n\n        document.addEventListener('DOMContentLoaded', () => {\n            const tabButtonsContainer = document.getElementById('tabButtonsContainer');\n\n            const baseWordPressDomain = 'https:\/\/hobbi.st';\n            const wordpressPagePaths = {\n                'Home': '', \/\/ Corresponds to base URL\n                'Learn': 'courses\/',\n                'Earn': 'earn\/', \/\/ This will be handled differently (radial menu)\n                'Activities': 'activities\/',\n                'Profile \/ Settings': 'profile-settings\/',\n                \/\/ New paths for radial menu items\n                'Mine': 'home-page-2\/',\n                'Boost': 'jobsa\/',\n                'Gift': 'courses\/%D1%81%D0%B5%D0%BA%D1%80%D0%B5%D1%82-%D1%81%D0%B8%D0%BD%D0%B4%D0%B8%D0%BA%D0%B0%D1%82-%D1%88%D0%B8%D1%84%D1%80%D0%BE%D0%B2%D0%B0%D0%BB%D1%8C%D1%89%D0%B8%D0%BA%D0%BE%D0%B2-hobbist\/'\n            };\n\n            const tabIcons = {\n                'Home': 'fas fa-home',\n                'Learn': 'fas fa-graduation-cap',\n                'Earn': 'fas fa-coins', \/\/ Main icon for the Earn button\n                'Activities': 'fas fa-calendar-alt',\n                'Profile \/ Settings': 'fas fa-user-circle'\n            };\n\n            \/\/ Define the radial action buttons with their icons and associated actions\n            const radialActionButtons = [\n                { name: 'Mine', icon: '<img decoding=\"async\" src=\"https:\/\/hobbi.st\/wp-content\/uploads\/2023\/08\/fireheart.svg\" alt=\"Mine\" class=\"w-full h-full object-contain\" onerror=\"this.onerror=null;this.src=\\'https:\/\/placehold.co\/32x32\/cccccc\/000000?text=Mine\\'\">' },\n                { name: 'Boost', icon: 'fas fa-bolt' },\n                { name: 'Gift', icon: 'fas fa-gift' }\n            ];\n\n            \/**\n             * Toggles the expansion\/collapse of the \"Earn\" radial menu.\n             * Also changes the '+' icon to 'X' and vice-versa.\n             *\/\n            function toggleEarnMenu() {\n                const earnButton = document.getElementById('tab-Earn');\n                const toggleIcon = earnButton.querySelector('.earn-toggle-icon i'); \/\/ Select the <i> tag within toggle-icon\n                const radialContainer = earnButton.querySelector('.radial-buttons-container');\n                const radialButtons = radialContainer.querySelectorAll('.radial-action-button');\n\n                isEarnMenuOpen = !isEarnMenuOpen; \/\/ Toggle the state\n\n                if (isEarnMenuOpen) {\n                    \/\/ Open the menu\n                    earnButton.classList.add('rotated-earn-button'); \/\/ Add rotation class\n\n                    toggleIcon.className = 'fas fa-times'; \/\/ Change to 'X'\n                    radialContainer.classList.add('active');\n                    radialButtons.forEach(button => button.classList.add('show'));\n\n                    \/\/ Optionally, deactivate other main tabs when Earn menu is open\n                    document.querySelectorAll('.tab-button').forEach(btn => {\n                        if (btn.id !== 'tab-Earn') {\n                            btn.classList.remove('active');\n                        }\n                    });\n\n                } else {\n                    \/\/ Close the menu\n                    toggleIcon.className = 'fas fa-plus'; \/\/ Change back to '+'\n                    radialContainer.classList.remove('active');\n                    radialButtons.forEach(button => button.classList.remove('show'));\n                    earnButton.classList.remove('rotated-earn-button'); \/\/ Remove rotation class\n\n                    \/\/ Reactivate the 'Earn' tab visually, as it's the anchor for this menu\n                    earnButton.classList.add('active');\n                }\n            }\n\n            \/**\n             * Loads content into the browser based on the selected tab name or radial action name.\n             * @param {string} contentKey The name of the tab or radial action to load (e.g., 'Home', 'Mine').\n             *\/\n            function loadContent(contentKey) {\n                const relativePath = wordpressPagePaths[contentKey];\n                if (relativePath === undefined) {\n                    console.error('Relative path not found for content key:', contentKey);\n                    \/\/ In a real Elementor widget, you might display an error message\n                    \/\/ or log it to the console for debugging.\n                    return;\n                }\n\n                let finalUrl = baseWordPressDomain;\n                \/\/ Assuming default language or no language prefix for Elementor usage\n                \/\/ If language is needed, it should be configured in Elementor directly or hardcoded here.\n                \/\/ For simplicity, we'll assume no language prefix for this Elementor widget context.\n\n                if (relativePath) { finalUrl += `\/${relativePath}`; }\n\n                \/\/ Clean up any double slashes (except in protocol) and ensure trailing slash for directories\n                finalUrl = finalUrl.replace(\/([^:]\\\/)\\\/+\/g, \"$1\");\n                if (!finalUrl.endsWith('\/') && !finalUrl.includes('.')) { finalUrl += '\/'; }\n\n                console.log(`Navigating to URL: ${finalUrl}`);\n                window.location.href = finalUrl; \/\/ Navigate to the URL in the current tab\n            }\n\n            \/**\n             * Handles main tab clicks, manages Earn menu interaction, and loads content.\n             * @param {string} tabName The name of the tab to load.\n             *\/\n            function loadTab(tabName) {\n                \/\/ If the Earn menu is open and another tab is clicked, close the Earn menu first\n                if (isEarnMenuOpen && tabName !== 'Earn') {\n                    toggleEarnMenu(); \/\/ This will close the menu\n                    \/\/ Wait a short moment for the animation to start before loading new content\n                    setTimeout(() => {\n                        loadContent(tabName);\n                    }, 200); \/\/ Small delay to allow menu to start collapsing\n                } else if (tabName === 'Earn') {\n                    \/\/ If Earn tab is clicked, toggle its radial menu\n                    toggleEarnMenu();\n                } else {\n                    \/\/ For other tabs, proceed directly\n                    loadContent(tabName);\n                }\n\n                \/\/ Update active tab styles for all tabs, ensuring only the current one is active\n                document.querySelectorAll('.tab-button').forEach(button => { button.classList.remove('active'); });\n                const targetButton = document.getElementById(`tab-${tabName.replace(\/\\s|\\\/\/g, '-')}`);\n                if (targetButton) {\n                    targetButton.classList.add('active');\n                } else {\n                    console.warn(`Tab button for ${tabName} not found.`);\n                }\n                currentActiveTab = tabName;\n            }\n\n            \/\/ Dynamically create tab buttons based on wordpressPagePaths\n            let firstContentTabName = null;\n            Object.keys(wordpressPagePaths).forEach((tabName, index) => {\n                \/\/ Set the first content tab name (excluding 'Earn' if it's the first)\n                \/\/ Filter out 'Mine', 'Boost', 'Gift' as they are radial menu items, not main tabs\n                if (firstContentTabName === null && !['Earn', 'Mine', 'Boost', 'Gift'].includes(tabName)) {\n                    firstContentTabName = tabName;\n                } else if (firstContentTabName === null && tabName === 'Earn' && Object.keys(wordpressPagePaths).length === 4) {\n                    firstContentTabName = tabName;\n                }\n\n                \/\/ Skip creating main tab buttons for radial menu items\n                if (['Mine', 'Boost', 'Gift'].includes(tabName)) {\n                    return;\n                }\n\n                const button = document.createElement('button');\n                button.id = `tab-${tabName.replace(\/\\s|\\\/\/g, '-')}`;\n\n                \/\/ Only add .tab-button class to non-Earn tabs for specific styling\n                if (tabName !== 'Earn') {\n                    button.classList.add('tab-button');\n                }\n\n                if (tabName === 'Earn') {\n                    \/\/ Main coins icon\n                    const mainIcon = document.createElement('i');\n                    mainIcon.className = `${tabIcons[tabName]} earn-main-icon`; \/\/ Specific class for coins icon\n                    button.appendChild(mainIcon);\n\n                    \/\/ Plus\/Times toggle icon container\n                    const toggleIconContainer = document.createElement('div');\n                    toggleIconContainer.classList.add('earn-toggle-icon');\n                    const toggleIcon = document.createElement('i');\n                    toggleIcon.className = 'fas fa-plus'; \/\/ Default to plus\n                    toggleIconContainer.appendChild(toggleIcon);\n                    button.appendChild(toggleIconContainer);\n\n                    \/\/ Radial buttons container (hidden by default)\n                    const radialContainer = document.createElement('div');\n                    radialContainer.classList.add('radial-buttons-container');\n                    radialActionButtons.forEach(radialBtnData => {\n                        const radialBtn = document.createElement('button');\n                        radialBtn.classList.add('radial-action-button');\n\n                        \/\/ Check if the icon is an image URL or a Font Awesome class\n                        if (radialBtnData.icon.includes('<img')) {\n                            radialBtn.innerHTML = radialBtnData.icon; \/\/ Directly set the image HTML\n                        } else {\n                            const radialIcon = document.createElement('i');\n                            radialIcon.className = radialBtnData.icon;\n                            radialBtn.appendChild(radialIcon);\n                        }\n\n                        radialBtn.onclick = (e) => {\n                            e.stopPropagation(); \/\/ Prevent main tab click from firing\n                            loadContent(radialBtnData.name); \/\/ Load content for the radial action\n\n                            toggleEarnMenu(); \/\/ Collapse the menu after action\n                            \/\/ Ensure 'Earn' tab stays active after clicking its submenu\n                            document.querySelectorAll('.tab-button').forEach(button => { button.classList.remove('active'); });\n                            document.getElementById('tab-Earn').classList.add('active');\n                            currentActiveTab = 'Earn';\n                        };\n                        radialContainer.appendChild(radialBtn);\n                    });\n                    button.appendChild(radialContainer);\n\n                } else {\n                    \/\/ Standard tab button structure: icon and text\n                    const icon = document.createElement('i');\n                    icon.className = `${tabIcons[tabName]} tab-icon`;\n                    const textSpan = document.createElement('span');\n                    textSpan.textContent = tabName;\n                    button.appendChild(icon);\n                    button.appendChild(textSpan);\n                }\n\n                button.addEventListener('click', () => loadTab(tabName));\n                tabButtonsContainer.appendChild(button);\n            });\n\n            \/\/ Set the initial active tab and load its content\n            if (firstContentTabName) {\n                \/\/ Do not load content automatically on page load for Elementor widget\n                \/\/ The user will click a button to navigate.\n                \/\/ Just set the initial active visual state.\n                const initialButton = document.getElementById(`tab-${firstContentTabName.replace(\/\\s|\\\/\/g, '-')}`);\n                if (initialButton) {\n                    initialButton.classList.add('active');\n                    currentActiveTab = firstContentTabName;\n                }\n            }\n        });\n    <\/script>\n<\/body>\n<\/html>\n<!-- end hobbist mini app -->\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>Telegram Mini App Tabs Loading content&#8230; Sign Up \/ Log In with Telegram This app would like to use your Telegram account information (ID, Name) to seamlessly log you in or register you. Accept &#038; Continue Decline<\/p>","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"give_campaign_id":0,"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"pmpro_default_level":"","footnotes":""},"class_list":["post-10470","page","type-page","status-publish","hentry","pmpro-has-access"],"campaignId":"","_links":{"self":[{"href":"https:\/\/hobbi.st\/ru\/wp-json\/wp\/v2\/pages\/10470","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hobbi.st\/ru\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/hobbi.st\/ru\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/hobbi.st\/ru\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/hobbi.st\/ru\/wp-json\/wp\/v2\/comments?post=10470"}],"version-history":[{"count":0,"href":"https:\/\/hobbi.st\/ru\/wp-json\/wp\/v2\/pages\/10470\/revisions"}],"wp:attachment":[{"href":"https:\/\/hobbi.st\/ru\/wp-json\/wp\/v2\/media?parent=10470"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}