Module: LinkIO::SmartRedirect
- Defined in:
- lib/linkio/smart_redirect.rb
Overview
Generates the HTML page that attempts to open the native app via its custom
scheme and falls back to the app store after a timeout. This is a faithful
port of the Node.js backend's generateSmartRedirectPage.
Class Method Summary collapse
-
.page(app_scheme:, params:, store_url:, platform:, package_name:, timeout:) ⇒ String
A complete HTML document.
Class Method Details
.page(app_scheme:, params:, store_url:, platform:, package_name:, timeout:) ⇒ String
Returns a complete HTML document.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/linkio/smart_redirect.rb', line 17 def page(app_scheme:, params:, store_url:, platform:, package_name:, timeout:) query_string = Utils.encode_query(params) app_uri = "#{app_scheme}://link#{"?#{query_string}" unless query_string.empty?}" android_intent = if platform == Platform::ANDROID "intent://link#{"?#{query_string}" unless query_string.empty?}" \ "#Intent;scheme=#{app_scheme};package=#{package_name};end" else "" end is_android = platform == Platform::ANDROID <<~HTML <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Opening App...</title> <style> body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; text-align: center; } .container { padding: 20px; } .spinner { width: 50px; height: 50px; border: 3px solid rgba(255,255,255,0.3); border-radius: 50%; border-top-color: white; animation: spin 1s linear infinite; margin: 0 auto 20px; } @keyframes spin { to { transform: rotate(360deg); } } h1 { font-size: 24px; margin-bottom: 10px; } p { opacity: 0.9; margin-bottom: 20px; } .btn { display: inline-block; padding: 12px 24px; background: white; color: #667eea; text-decoration: none; border-radius: 8px; font-weight: 600; margin: 5px; } </style> </head> <body> <div class="container"> <div class="spinner"></div> <h1>Opening App...</h1> <p>If the app doesn't open, tap below to download</p> <a href="#{store_url}" class="btn">Download App</a> </div> <script> (function() { var appUri = "#{app_uri}"; var storeUrl = "#{store_url}"; var timeout = #{timeout}; var androidIntent = "#{android_intent}"; var isAndroid = #{is_android}; var startTime = Date.now(); var hasFocus = true; // Track if user leaves the page (app opened) window.addEventListener('blur', function() { hasFocus = false; }); window.addEventListener('pagehide', function() { hasFocus = false; }); document.addEventListener('visibilitychange', function() { if (document.hidden) hasFocus = false; }); // Try to open app var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = appUri; document.body.appendChild(iframe); // Also try direct location for some browsers setTimeout(function() { if (hasFocus) { window.location.href = appUri; } }, 100); // Android: try intent scheme as fallback if (isAndroid && androidIntent) { setTimeout(function() { if (hasFocus && Date.now() - startTime < timeout) { window.location.href = androidIntent; } }, 500); } // Fallback to store after timeout setTimeout(function() { if (hasFocus && Date.now() - startTime >= timeout - 100) { window.location.href = storeUrl; } }, timeout); })(); </script> </body> </html> HTML end |