Skip to content

Commit ac5b2cf

Browse files
Pass referrer on native (with an opt out) (bluesky-social#6648)
* Pass referer on native * Add ChainLink3 * Add an opt out for sending utm * Remove noreferrer on links We do have <meta name="referrer" content="origin-when-cross-origin"> in HTML, should be sufficient. * Narrow down the condition slightly --------- Co-authored-by: Eric Bailey <[email protected]>
1 parent fee2f5d commit ac5b2cf

File tree

9 files changed

+125
-16
lines changed

9 files changed

+125
-16
lines changed
Loading

src/components/Link.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ export function Link({
223223
{...web({
224224
hrefAttrs: {
225225
target: download ? undefined : isExternal ? 'blank' : undefined,
226-
rel: isExternal ? 'noopener noreferrer' : undefined,
226+
rel: isExternal ? 'noopener' : undefined,
227227
download,
228228
},
229229
dataSet: {
@@ -307,7 +307,7 @@ export function InlineLinkText({
307307
{...web({
308308
hrefAttrs: {
309309
target: download ? undefined : isExternal ? 'blank' : undefined,
310-
rel: isExternal ? 'noopener noreferrer' : undefined,
310+
rel: isExternal ? 'noopener' : undefined,
311311
download,
312312
},
313313
dataSet: {

src/components/icons/ChainLink.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {createSinglePathSVG} from './TEMPLATE'
2+
3+
export const ChainLink3_Stroke2_Corner0_Rounded = createSinglePathSVG({
4+
path: 'M18.535 5.465a5.003 5.003 0 0 0-7.076 0l-.005.005-.752.742a1 1 0 1 1-1.404-1.424l.749-.74a7.003 7.003 0 0 1 9.904 9.905l-.002.003-.737.746a1 1 0 1 1-1.424-1.404l.747-.757a5.003 5.003 0 0 0 0-7.076ZM6.202 9.288a1 1 0 0 1 .01 1.414l-.747.757a5.003 5.003 0 1 0 7.076 7.076l.005-.005.752-.742a1 1 0 1 1 1.404 1.424l-.746.737-.003.002a7.003 7.003 0 0 1-9.904-9.904l.74-.75a1 1 0 0 1 1.413-.009Zm8.505.005a1 1 0 0 1 0 1.414l-4 4a1 1 0 0 1-1.414-1.414l4-4a1 1 0 0 1 1.414 0Z',
5+
})

src/lib/hooks/useOpenLink.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import * as WebBrowser from 'expo-web-browser'
44

55
import {
66
createBskyAppAbsoluteUrl,
7+
isBskyAppUrl,
78
isBskyRSSUrl,
89
isRelativeUrl,
910
} from '#/lib/strings/url-helpers'
1011
import {isNative} from '#/platform/detection'
1112
import {useModalControls} from '#/state/modals'
1213
import {useInAppBrowser} from '#/state/preferences/in-app-browser'
14+
import {useOptOutOfUtm} from '#/state/preferences/opt-out-of-utm'
1315
import {useTheme} from '#/alf'
1416
import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper'
1517

@@ -18,6 +20,7 @@ export function useOpenLink() {
1820
const enabled = useInAppBrowser()
1921
const t = useTheme()
2022
const sheetWrapper = useSheetWrapper()
23+
const optOutOfUtm = useOptOutOfUtm()
2124

2225
const openLink = useCallback(
2326
async (url: string, override?: boolean) => {
@@ -26,6 +29,9 @@ export function useOpenLink() {
2629
}
2730

2831
if (isNative && !url.startsWith('mailto:')) {
32+
if (!optOutOfUtm && !isBskyAppUrl(url) && url.startsWith('http')) {
33+
url = addUtmSource(url)
34+
}
2935
if (override === undefined && enabled === undefined) {
3036
openModal({
3137
name: 'in-app-browser-consent',
@@ -47,8 +53,24 @@ export function useOpenLink() {
4753
}
4854
Linking.openURL(url)
4955
},
50-
[enabled, openModal, t, sheetWrapper],
56+
[enabled, openModal, t, sheetWrapper, optOutOfUtm],
5157
)
5258

5359
return openLink
5460
}
61+
62+
function addUtmSource(url: string): string {
63+
let parsedUrl
64+
try {
65+
parsedUrl = new URL(url)
66+
} catch (e) {
67+
return url
68+
}
69+
if (!parsedUrl.searchParams.has('utm_source')) {
70+
parsedUrl.searchParams.set('utm_source', 'bluesky')
71+
if (!parsedUrl.searchParams.has('utm_medium')) {
72+
parsedUrl.searchParams.set('utm_medium', 'social')
73+
}
74+
}
75+
return parsedUrl.toString()
76+
}

src/screens/Settings/ContentAndMediaSettings.tsx

+45-11
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,16 @@ import {
99
useInAppBrowser,
1010
useSetInAppBrowser,
1111
} from '#/state/preferences/in-app-browser'
12+
import {
13+
useOptOutOfUtm,
14+
useSetOptOutOfUtm,
15+
} from '#/state/preferences/opt-out-of-utm'
1216
import * as SettingsList from '#/screens/Settings/components/SettingsList'
17+
import {atoms as a} from '#/alf'
18+
import {Admonition} from '#/components/Admonition'
1319
import * as Toggle from '#/components/forms/Toggle'
1420
import {Bubbles_Stroke2_Corner2_Rounded as BubblesIcon} from '#/components/icons/Bubble'
21+
import {ChainLink3_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink'
1522
import {Hashtag_Stroke2_Corner0_Rounded as HashtagIcon} from '#/components/icons/Hashtag'
1623
import {Home_Stroke2_Corner2_Rounded as HomeIcon} from '#/components/icons/Home'
1724
import {Macintosh_Stroke2_Corner2_Rounded as MacintoshIcon} from '#/components/icons/Macintosh'
@@ -29,6 +36,8 @@ export function ContentAndMediaSettingsScreen({}: Props) {
2936
const setAutoplayDisabledPref = useSetAutoplayDisabled()
3037
const inAppBrowserPref = useInAppBrowser()
3138
const setUseInAppBrowser = useSetInAppBrowser()
39+
const optOutOfUtm = useOptOutOfUtm()
40+
const setOptOutOfUtm = useSetOptOutOfUtm()
3241

3342
return (
3443
<Layout.Screen>
@@ -68,6 +77,19 @@ export function ContentAndMediaSettingsScreen({}: Props) {
6877
</SettingsList.ItemText>
6978
</SettingsList.LinkItem>
7079
<SettingsList.Divider />
80+
<Toggle.Item
81+
name="disable_autoplay"
82+
label={_(msg`Autoplay videos and GIFs`)}
83+
value={!autoplayDisabledPref}
84+
onChange={value => setAutoplayDisabledPref(!value)}>
85+
<SettingsList.Item>
86+
<SettingsList.ItemIcon icon={PlayIcon} />
87+
<SettingsList.ItemText>
88+
<Trans>Autoplay videos and GIFs</Trans>
89+
</SettingsList.ItemText>
90+
<Toggle.Platform />
91+
</SettingsList.Item>
92+
</Toggle.Item>
7193
{isNative && (
7294
<Toggle.Item
7395
name="use_in_app_browser"
@@ -83,19 +105,31 @@ export function ContentAndMediaSettingsScreen({}: Props) {
83105
</SettingsList.Item>
84106
</Toggle.Item>
85107
)}
86-
<Toggle.Item
87-
name="disable_autoplay"
88-
label={_(msg`Autoplay videos and GIFs`)}
89-
value={!autoplayDisabledPref}
90-
onChange={value => setAutoplayDisabledPref(!value)}>
108+
{isNative && <SettingsList.Divider />}
109+
{isNative && (
110+
<Toggle.Item
111+
name="allow_utm"
112+
label={_(msg`Specify Bluesky as a referer`)}
113+
value={!(optOutOfUtm ?? false)}
114+
onChange={value => setOptOutOfUtm(!value)}>
115+
<SettingsList.Item>
116+
<SettingsList.ItemIcon icon={ChainLinkIcon} />
117+
<SettingsList.ItemText>
118+
<Trans>Send Bluesky referrer</Trans>
119+
</SettingsList.ItemText>
120+
<Toggle.Platform />
121+
</SettingsList.Item>
122+
</Toggle.Item>
123+
)}
124+
{isNative && (
91125
<SettingsList.Item>
92-
<SettingsList.ItemIcon icon={PlayIcon} />
93-
<SettingsList.ItemText>
94-
<Trans>Autoplay videos and GIFs</Trans>
95-
</SettingsList.ItemText>
96-
<Toggle.Platform />
126+
<Admonition type="info" style={[a.flex_1]}>
127+
<Trans>
128+
Helps external sites estimate traffic from Bluesky.
129+
</Trans>
130+
</Admonition>
97131
</SettingsList.Item>
98-
</Toggle.Item>
132+
)}
99133
</SettingsList.Container>
100134
</Layout.Content>
101135
</Layout.Screen>

src/state/persisted/schema.ts

+2
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ const schema = z.object({
124124
subtitlesEnabled: z.boolean().optional(),
125125
/** @deprecated */
126126
mutedThreads: z.array(z.string()),
127+
optOutOfUtm: z.boolean().optional(),
127128
})
128129
export type Schema = z.infer<typeof schema>
129130

@@ -169,6 +170,7 @@ export const defaults: Schema = {
169170
kawaii: false,
170171
hasCheckedForStarterPack: false,
171172
subtitlesEnabled: true,
173+
optOutOfUtm: false,
172174
}
173175

174176
export function tryParse(rawData: string): Schema | undefined {

src/state/preferences/index.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {Provider as InAppBrowserProvider} from './in-app-browser'
99
import {Provider as KawaiiProvider} from './kawaii'
1010
import {Provider as LanguagesProvider} from './languages'
1111
import {Provider as LargeAltBadgeProvider} from './large-alt-badge'
12+
import {Provider as OutOutOfUtmProvider} from './opt-out-of-utm'
1213
import {Provider as SubtitlesProvider} from './subtitles'
1314
import {Provider as UsedStarterPacksProvider} from './used-starter-packs'
1415

@@ -39,7 +40,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
3940
<AutoplayProvider>
4041
<UsedStarterPacksProvider>
4142
<SubtitlesProvider>
42-
<KawaiiProvider>{children}</KawaiiProvider>
43+
<OutOutOfUtmProvider>
44+
<KawaiiProvider>{children}</KawaiiProvider>
45+
</OutOutOfUtmProvider>
4346
</SubtitlesProvider>
4447
</UsedStarterPacksProvider>
4548
</AutoplayProvider>
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react'
2+
3+
import * as persisted from '#/state/persisted'
4+
5+
type StateContext = boolean
6+
type SetContext = (v: boolean) => void
7+
8+
const stateContext = React.createContext<StateContext>(
9+
Boolean(persisted.defaults.optOutOfUtm),
10+
)
11+
const setContext = React.createContext<SetContext>((_: boolean) => {})
12+
13+
export function Provider({children}: {children: React.ReactNode}) {
14+
const [state, setState] = React.useState(
15+
Boolean(persisted.get('optOutOfUtm')),
16+
)
17+
18+
const setStateWrapped = React.useCallback(
19+
(optOutOfUtm: persisted.Schema['optOutOfUtm']) => {
20+
setState(Boolean(optOutOfUtm))
21+
persisted.write('optOutOfUtm', optOutOfUtm)
22+
},
23+
[setState],
24+
)
25+
26+
React.useEffect(() => {
27+
return persisted.onUpdate('optOutOfUtm', nextOptOutOfUtm => {
28+
setState(Boolean(nextOptOutOfUtm))
29+
})
30+
}, [setStateWrapped])
31+
32+
return (
33+
<stateContext.Provider value={state}>
34+
<setContext.Provider value={setStateWrapped}>
35+
{children}
36+
</setContext.Provider>
37+
</stateContext.Provider>
38+
)
39+
}
40+
41+
export const useOptOutOfUtm = () => React.useContext(stateContext)
42+
export const useSetOptOutOfUtm = () => React.useContext(setContext)

src/view/com/util/Link.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ export const TextLink = memo(function TextLink({
256256
if (isExternal) {
257257
return {
258258
target: '_blank',
259-
// rel: 'noopener noreferrer',
259+
// rel: 'noopener',
260260
}
261261
}
262262
return {}

0 commit comments

Comments
 (0)