UNPKG

@deeplinknow/react-native

Version:

React Native + Expo SDK for the Deeplink Now deferred deep linking platform

289 lines (275 loc) 9.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _reactNative = require("react-native"); var _Clipboard = require("./services/Clipboard.js"); // We need to get these from NativeModules since they're platform-specific const { Locale, TimeZone } = _reactNative.NativeModules; // Export all types that consumers might need class DeepLinkNow { apiKey = null; config = { enableLogs: false }; installTime = new Date().toISOString(); validDomains = new Set([]); log(...args) { if (this.config.enableLogs) { console.log("[DeepLinkNow]", ...args); } } warn(...args) { console.warn("[DeepLinkNow]", ...args); } async initialize(apiKey, config) { if (!apiKey || typeof apiKey !== "string") { this.warn("Invalid API key provided"); return; } this.apiKey = apiKey; this.config = { ...this.config, ...config }; // Hit the /v1/sdk/init endpoint const response = await this.makeRequest("init", { method: "POST", body: JSON.stringify({ api_key: apiKey }) }); if (response) { const appName = response.app.alias; // Set up the base domains this.validDomains.add(`${appName}.deeplinknow.com`); this.validDomains.add(`${appName}.deeplink.now`); // Cache valid domains response?.app?.custom_domains?.filter(domain => domain.domain && domain.verified)?.forEach(domain => { if (domain.domain) this.validDomains.add(domain.domain); }); this.log("Init response:", response); this.log("Successfully initialized with config:", this.config); } } isValidDomain(domain) { return this.validDomains.has(domain); } async hasDeepLinkToken() { try { const content = await _Clipboard.Clipboard.getString(); return content?.startsWith("dln://") ?? false; } catch (e) { this.warn("Failed to check clipboard:", e); return false; } } async checkClipboard() { if (!this.apiKey) { this.warn("SDK not initialized. Call initialize() first"); return null; } try { const content = await _Clipboard.Clipboard.getString(); // example content = https://test-app.deeplinknow.com/params?1234n4 const domain = content?.split("://")?.[1]?.split("/")?.[0]; if (domain && (domain?.includes("deeplinknow.com") || domain?.includes("deeplink.now") || this.validDomains.has(domain))) { this.log("Found deep link token in clipboard"); return content; } } catch (e) { this.warn("Failed to read clipboard:", e); } return null; } parseDeepLink(url) { try { const urlObj = new URL(url); if (!this.isValidDomain(urlObj.hostname)) { return null; } const parameters = {}; urlObj.searchParams.forEach((value, key) => { parameters[key] = value; }); return { path: urlObj.pathname, parameters }; } catch { return null; } } async getFingerprint() { // Get device model using simple platform detection const platform = _reactNative.Platform.OS; const deviceModel = platform === "ios" ? "iPhone" // In React Native, we can assume iPhone as default iOS device : platform === "android" ? "Android" : "unknown"; // Get OS version directly from Platform.Version const osVersion = String(_reactNative.Platform.Version); // Get language and locale using platform-specific reliable methods let language = "en"; try { if (Locale && typeof Locale.getDefault === "function") { const localeResult = Locale.getDefault(); if (typeof localeResult === "string") { language = localeResult; } } else if (_reactNative.Platform.OS === "ios" && _reactNative.NativeModules.SettingsManager?.settings) { language = _reactNative.NativeModules.SettingsManager.settings.AppleLocale || _reactNative.NativeModules.SettingsManager.settings.AppleLanguages?.[0] || "en"; } else if (_reactNative.Platform.OS === "android" && _reactNative.NativeModules.I18nManager) { language = _reactNative.NativeModules.I18nManager.localeIdentifier || "en"; } else { language = Intl?.DateTimeFormat()?.resolvedOptions()?.locale || "en"; } } catch (e) { this.warn("Failed to get language:", e); language = "en"; // Fallback to English } // Get timezone using platform-specific reliable methods let timezone = "UTC"; try { if (TimeZone && typeof TimeZone.getDefault === "function") { const timezoneResult = TimeZone.getDefault(); if (typeof timezoneResult === "string") { timezone = timezoneResult; } } else if (_reactNative.Platform.OS === "ios" && _reactNative.NativeModules.SettingsManager?.settings) { timezone = _reactNative.NativeModules.SettingsManager.settings.AppleTimeZone || "UTC"; } else { timezone = Intl?.DateTimeFormat()?.resolvedOptions()?.timeZone || "UTC"; } } catch (e) { this.warn("Failed to get timezone:", e); timezone = "UTC"; // Fallback to UTC } // Get Android ID if on Android platform let deviceId = null; if (_reactNative.Platform.OS === "android") { try { if (_reactNative.NativeModules.DeviceInfo?.getAndroidId) { deviceId = await _reactNative.NativeModules.DeviceInfo.getAndroidId(); } } catch (e) { this.warn("Failed to get Android ID:", e); } } // Get screen dimensions and pixel ratio let screenWidth; let screenHeight; let pixelRatio; try { const { Dimensions } = require("react-native"); const window = Dimensions.get("window"); screenWidth = window.width; screenHeight = window.height; pixelRatio = Dimensions.get("screen").scale; } catch (e) { this.warn("Failed to get screen dimensions:", e); } // Generate user agent string that matches web format const userAgent = _reactNative.Platform.select({ ios: `Mozilla/5.0 (${deviceModel}; CPU ${_reactNative.Platform.OS} ${osVersion} like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/${osVersion} Mobile/15E148 Safari/604.1`, android: `Mozilla/5.0 (Linux; Android ${osVersion}; ${deviceModel}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36` }) || `DeepLinkNow-ReactNative/${_reactNative.Platform.OS}`; // Generate hardware fingerprint const hardwareFingerprint = this.generateHardwareFingerprint(_reactNative.Platform.OS, screenWidth, screenHeight, pixelRatio, language, timezone); return { user_agent: userAgent, platform: _reactNative.Platform.OS === "ios" ? "ios" : "android", os_version: osVersion, device_model: deviceModel, language, timezone, installed_at: this.installTime, last_opened_at: new Date().toISOString(), device_id: deviceId, advertising_id: null, vendor_id: null, hardware_fingerprint: hardwareFingerprint, screen_width: screenWidth, screen_height: screenHeight, pixel_ratio: pixelRatio }; } simpleHash(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = (hash << 5) - hash + char; hash = hash & hash; // Convert to 32bit integer } return hash.toString(16); } generateHardwareFingerprint(platform, screenWidth, screenHeight, pixelRatio, language, timezone) { const components = [platform, String(_reactNative.Platform.Version), String(screenWidth), String(screenHeight), String(pixelRatio), language, timezone]; // Create a deterministic string from components const fingerprintString = components.join("|"); return this.simpleHash(fingerprintString); } async makeRequest(endpoint, options = {}) { if (!this.apiKey) { this.warn("SDK not initialized. Call initialize() first"); return null; } try { const response = await fetch(`https://deeplinknow.com/api/v1/sdk/${endpoint}`, { ...options, headers: { ...options.headers, "Content-Type": "application/json", "x-api-key": this.apiKey } }); const data = await response.json(); if (!response.ok) { this.warn(`API request failed: ${response.status}`, data); return null; } return data; } catch (error) { this.warn("API request failed:", error); return null; } } async findDeferredUser() { this.log("Finding deferred user..."); const fingerprint = await this.getFingerprint(); const matchRequest = { user_agent: fingerprint.user_agent, platform: fingerprint.platform, os_version: fingerprint.os_version, device_model: fingerprint.device_model, language: fingerprint.language, timezone: fingerprint.timezone, installed_at: fingerprint.installed_at, last_opened_at: fingerprint.last_opened_at, device_id: fingerprint.device_id, advertising_id: fingerprint.advertising_id, vendor_id: fingerprint.vendor_id, hardware_fingerprint: fingerprint.hardware_fingerprint, pixel_ratio: fingerprint.pixel_ratio, screen_height: fingerprint.screen_height, screen_width: fingerprint.screen_width }; this.log("Sending match request:", matchRequest); const response = await this.makeRequest("match", { method: "POST", body: JSON.stringify({ fingerprint: matchRequest }) }); if (response) { this.log("Match response:", response); } return response; } } var _default = exports.default = new DeepLinkNow(); //# sourceMappingURL=index.js.map