@deeplinknow/react-native
Version:
React Native + Expo SDK for the Deeplink Now deferred deep linking platform
289 lines (275 loc) • 9.86 kB
JavaScript
;
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