Skip to content

Commit cb97146

Browse files
authored
Merge pull request #3 from software-mansion-labs/update-readme-and-types
feat: added prop control over clipboard; updated readme and types in package
2 parents ecdbcc9 + d66a677 commit cb97146

File tree

8 files changed

+152
-55
lines changed

8 files changed

+152
-55
lines changed

README.md

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,98 @@ sdk for handling deferred links
44

55
## Installation
66

7-
87
```sh
98
npm install detour-react-native
109
```
1110

11+
#### You need to install additional dependencies
12+
13+
```sh
14+
npm install expo-localization react-native-device-info expo-clipboard @react-native-async-storage/async-storage
15+
```
1216

1317
## Usage
1418

19+
#### Initialize provider in root of your app
1520

1621
```js
17-
import { multiply } from 'detour-react-native';
22+
import { DetourProvider, type Config } from 'detour-react-native';
23+
24+
export default function App() {
25+
const config: Config = {
26+
API_KEY: 'ssss-ssss-ssss',
27+
appID: 'app-id-from-dashboard',
28+
shouldUseClipboard: true,
29+
};
30+
31+
return(
32+
<DetourProvider config={config}>
33+
// rest of app content
34+
</DetourProvider>)
35+
}
36+
```
37+
38+
#### Use values from context
39+
40+
```js
41+
import { useDetourContext } from 'detour-react-native';
42+
43+
// inside component
44+
const { deferredLink, deferredLinkProcessed, route } = useDetourContext();
45+
```
46+
47+
## Types
48+
49+
The package exposes several types to help you with type-checking in your own codebase.
1850

19-
// ...
51+
**Config**
2052

21-
const result = await multiply(3, 7);
53+
This type is used to define the configuration object you pass to the DetourProvider.
54+
55+
```js
56+
export type Config = {
57+
/**
58+
* Your application ID from the Detour dashboard.
59+
*/
60+
appID: string;
61+
62+
/**
63+
* Your API key from the Detour dashboard.
64+
*/
65+
API_KEY: string;
66+
67+
/**
68+
* Optional: A flag to determine if the provider should check the clipboard for a deferred link.
69+
* When true, it displays permission alert to user.
70+
* Defaults to true if not provided.
71+
*/
72+
shouldUseClipboard?: boolean;
73+
};
2274
```
2375

76+
**DeferredLinkContext**
77+
78+
This type represents the object returned by the useDetourContext hook, containing the deferred link and its processing status.
79+
80+
```js
81+
export type DeferredLinkContext = {
82+
/**
83+
* Boolean indicating if the deferred link has been processed.
84+
* This is useful for conditionally rendering UI components.
85+
*/
86+
deferredLinkProcessed: boolean;
87+
88+
/**
89+
* The deferred link value. This can be a string or a URL object, or null if no link was found.
90+
*/
91+
deferredLink: string | URL | null;
92+
93+
/**
94+
* The detected route based on the deferred link, or null if no route was detected.
95+
*/
96+
route: string | null;
97+
};
98+
```
2499

25100
## Contributing
26101

example/src/App.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import { DetourProvider } from 'detour-react-native';
1+
import { DetourProvider, type Config } from 'detour-react-native';
22

33
import { Screen } from './Screen';
44

55
export default function App() {
6-
return (
7-
<DetourProvider appID="example-id" API_KEY="sssss-sssss-sssss">
8-
{<Screen />}
9-
</DetourProvider>
10-
);
6+
const config: Config = {
7+
API_KEY: 'ssss-ssss-ssss',
8+
appID: 'app-id-from-dashboard',
9+
shouldUseClipboard: true,
10+
};
11+
12+
return <DetourProvider config={config}>{<Screen />}</DetourProvider>;
1113
}

src/DetourContext.tsx

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1-
import { createContext, useContext, type ReactNode } from 'react';
1+
import { createContext, useContext, type PropsWithChildren } from 'react';
22
import { useDeferredLink } from './hooks/useDeferredLink';
3+
import type { Config, DeferredLinkContext } from './types';
34

4-
type DetourContextType = ReturnType<typeof useDeferredLink>;
5+
type Props = PropsWithChildren & { config: Config };
6+
7+
type DetourContextType = DeferredLinkContext;
58

69
const DetourContext = createContext<DetourContextType | undefined>(undefined);
710

8-
export const DetourProvider = ({
9-
appID,
10-
API_KEY,
11-
children,
12-
}: {
13-
appID: string;
14-
API_KEY: string;
15-
children: ReactNode;
16-
}) => {
17-
const value = useDeferredLink({ API_KEY, appID });
11+
export const DetourProvider = ({ config, children }: Props) => {
12+
const { API_KEY, appID, shouldUseClipboard = true } = config;
13+
const value = useDeferredLink({ API_KEY, appID, shouldUseClipboard });
1814

1915
return (
2016
<DetourContext.Provider value={value}>{children}</DetourContext.Provider>

src/api/getDeferredLink.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
1+
import type { RequiredConfig } from '../types';
12
import { getProbabilisticFingerprint } from '../utils/fingerprint';
23

34
const API_URL = 'https://godetour.dev/api/link/match-link';
45

5-
export const getDeferredLink = async (apiKey: string, appId: string) => {
6-
const probabilisticFingerprint = await getProbabilisticFingerprint();
6+
export const getDeferredLink = async ({
7+
API_KEY,
8+
appID,
9+
shouldUseClipboard,
10+
}: RequiredConfig) => {
11+
const probabilisticFingerprint =
12+
await getProbabilisticFingerprint(shouldUseClipboard);
713

814
try {
915
const response = await fetch(API_URL, {
1016
method: 'POST',
1117
headers: {
1218
'Content-Type': 'application/json',
13-
'Authorization': `Bearer ${apiKey}`,
14-
'X-App-ID': appId,
19+
'Authorization': `Bearer ${API_KEY}`,
20+
'X-App-ID': appID,
1521
},
1622
body: JSON.stringify(probabilisticFingerprint),
1723
});

src/hooks/useDeferredLink.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
import { useEffect, useState } from 'react';
2-
import { checkIsFirstEntrance, markFirstEntrance } from '../utils/appEntrance';
32
import { getDeferredLink } from '../api/getDeferredLink';
3+
import type { DeferredLinkContext, RequiredConfig } from '../types';
4+
import { checkIsFirstEntrance, markFirstEntrance } from '../utils/appEntrance';
45

56
let deferredSessionHandled = false;
67

8+
type ReturnType = DeferredLinkContext;
9+
710
export const useDeferredLink = ({
811
API_KEY,
912
appID,
10-
}: {
11-
API_KEY: string;
12-
appID: string;
13-
}): {
14-
deferredLinkProcessed: boolean;
15-
deferredLink: string | URL | null;
16-
route: string | null;
17-
} => {
13+
shouldUseClipboard,
14+
}: RequiredConfig): ReturnType => {
1815
const [deferredLinkProcessed, setDeferredProcessed] = useState(false);
1916
const [matchedLink, setMatchedLink] = useState<string | URL | null>(null);
2017
const [route, setRoute] = useState<string | null>(null);
@@ -37,7 +34,11 @@ export const useDeferredLink = ({
3734
await markFirstEntrance();
3835
}
3936

40-
const link = await getDeferredLink(API_KEY, appID);
37+
const link = await getDeferredLink({
38+
API_KEY,
39+
appID,
40+
shouldUseClipboard,
41+
});
4142
if (!link) {
4243
console.log('No deferred link found');
4344
setDeferredProcessed(true);
@@ -65,7 +66,7 @@ export const useDeferredLink = ({
6566
setDeferredProcessed(true);
6667
}
6768
})();
68-
}, [API_KEY, appID]);
69+
}, [API_KEY, appID, shouldUseClipboard]);
6970

7071
return {
7172
deferredLinkProcessed,

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export { DetourProvider, useDetourContext } from './DetourContext';
2+
export type { Config, DeferredLinkContext } from './types/index';

src/types/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export type Config = {
2+
appID: string;
3+
API_KEY: string;
4+
shouldUseClipboard?: boolean;
5+
};
6+
7+
export type RequiredConfig = Required<Config>;
8+
9+
export type DeferredLinkContext = {
10+
deferredLinkProcessed: boolean;
11+
deferredLink: string | URL | null;
12+
route: string | null;
13+
};

src/utils/fingerprint.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,26 @@ type ProbabilisticFingerprint = {
1919
pastedLink?: string;
2020
};
2121

22-
export const getProbabilisticFingerprint =
23-
async (): Promise<ProbabilisticFingerprint> => {
24-
const { width, height } = Dimensions.get('window');
22+
export const getProbabilisticFingerprint = async (
23+
shouldUseClipboard: boolean
24+
): Promise<ProbabilisticFingerprint> => {
25+
const { width, height } = Dimensions.get('window');
2526

26-
return {
27-
platform: Platform.OS,
28-
model: DeviceInfo.getModel(),
29-
manufacturer: await DeviceInfo.getManufacturer(),
30-
systemVersion: DeviceInfo.getSystemVersion(),
31-
screenWidth: width,
32-
screenHeight: height,
33-
scale: PixelRatio.get(),
34-
locale: Localization.getLocales(),
35-
timezone: Localization.getCalendars()[0]?.timeZone,
36-
userAgent: await DeviceInfo.getUserAgent(),
37-
appVersion: DeviceInfo.getVersion(),
38-
timestamp: Date.now(),
39-
pastedLink: await Clipboard.getStringAsync(),
40-
};
27+
return {
28+
platform: Platform.OS,
29+
model: DeviceInfo.getModel(),
30+
manufacturer: await DeviceInfo.getManufacturer(),
31+
systemVersion: DeviceInfo.getSystemVersion(),
32+
screenWidth: width,
33+
screenHeight: height,
34+
scale: PixelRatio.get(),
35+
locale: Localization.getLocales(),
36+
timezone: Localization.getCalendars()[0]?.timeZone,
37+
userAgent: await DeviceInfo.getUserAgent(),
38+
appVersion: DeviceInfo.getVersion(),
39+
timestamp: Date.now(),
40+
pastedLink: shouldUseClipboard
41+
? await Clipboard.getStringAsync()
42+
: undefined,
4143
};
44+
};

0 commit comments

Comments
 (0)