diff --git a/src/background/index.ts b/src/background/index.ts index cacdbbd..8c3908d 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -1,8 +1,6 @@ -import { rollbar, initAnalytics, logUrlChange } from '../utils'; +import { rollbar } from '../utils'; import { ChromeUninstallUrlUpdate, ChromeUrlUpdate } from '../types/Chrome'; -initAnalytics(); - export const isUninstallMsg = ( msg: unknown | ChromeUninstallUrlUpdate ): msg is ChromeUninstallUrlUpdate => { @@ -14,8 +12,6 @@ export const isUninstallMsg = ( chrome.tabs.onUpdated.addListener((tabId, { url }) => { if (url) { - logUrlChange(url); - const newUrl: ChromeUrlUpdate = { event: 'URL_UPDATE', url }; chrome.tabs.sendMessage(tabId, newUrl); diff --git a/src/content/hooks/useUrlChangeChrome.ts b/src/content/hooks/useUrlChangeChrome.ts index 04577b2..983b89e 100644 --- a/src/content/hooks/useUrlChangeChrome.ts +++ b/src/content/hooks/useUrlChangeChrome.ts @@ -1,7 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { useEffect } from 'react'; import { ChromeUrlUpdate } from '../../types/Chrome'; -import { tryIdentifyUserFromUrlParam } from '../../utils/analytics'; +import { + logUrlChange, + tryIdentifyUserFromUrlParam, +} from '../../utils/analytics'; /** * Invoke the `onUrlChange` param when Chrome detects that @@ -16,20 +19,24 @@ export const useUrlChangeChrome = ( runOnInit = true ): void => { useEffect(() => { + const initialUrl = window.location.href; + + tryIdentifyUserFromUrlParam(initialUrl); + logUrlChange(initialUrl); + + if (runOnInit) { + onUrlChange(initialUrl); + } + const onUrlChangeWrapper = (request: ChromeUrlUpdate) => { if (request.event === 'URL_UPDATE') { + logUrlChange(request.url); onUrlChange(request.url); } }; chrome.runtime.onMessage.addListener(onUrlChangeWrapper); - if (runOnInit) { - onUrlChange(window.location.href); - } - - tryIdentifyUserFromUrlParam(window.location.href); - return () => { chrome.runtime.onMessage.removeListener(onUrlChangeWrapper); }; diff --git a/src/content/utils/__tests__/airbnb.spec.ts b/src/content/utils/__tests__/airbnb.spec.ts index c6875a3..bf25113 100644 --- a/src/content/utils/__tests__/airbnb.spec.ts +++ b/src/content/utils/__tests__/airbnb.spec.ts @@ -5,48 +5,49 @@ jest.mock('mixpanel-browser'); describe('airbnb.ts', () => { describe('getSearchLocation()', () => { it('returns a `country` key when one location is provided', () => { - const locations = airbnb.getSearchLocation('United States'); + const locations = airbnb.getSearchLocation( + '/s/United States/homes' + ); expect(locations.country).toEqual('United States'); }); it('returns a `state` key when two locations are provided', () => { - const locations = airbnb.getSearchLocation('HI, United States'); + const locations = airbnb.getSearchLocation( + '/s/HI--United-States/homes' + ); expect(locations.state).toEqual('HI'); }); it('returns a `city` key when three locations are provided', () => { const locations = airbnb.getSearchLocation( - 'Honolulu, HI, United States' + '/s/Honolulu--HI--United-States/homes' ); expect(locations.city).toEqual('Honolulu'); }); it('returns a `neighborhood` key when four locations are provided', () => { const locations = airbnb.getSearchLocation( - 'Waikīkī, Honolulu, HI, United States' + '/s/Waikīkī--Honolulu--HI--United-States/homes' ); expect(locations.neighborhood).toEqual('Waikīkī'); }); it('returns a `poi` key when five locations are provided', () => { - const locationStr = 'Oahu, Waikīkī, Honolulu, HI, United States'; - const locations = airbnb.getSearchLocation(locationStr); + const locations = airbnb.getSearchLocation( + '/s/Oahu--Waikīkī--Honolulu--HI--United-States/homes' + ); expect(locations.poi).toEqual('Oahu'); }); it('returns a `unknown` key with the full string if more than five locations are provided', () => { const locationStr = - 'Test, Oahu, Waikīkī, Honolulu, HI, United States'; + '/s/Test--Oahu--Waikīkī--Honolulu--HI--United-States/homes'; const locations = airbnb.getSearchLocation(locationStr); expect(locations.unknown).toEqual(locationStr); }); - - it('returns an empty object if the param is undefined', () => { - expect(airbnb.getSearchLocation(undefined)).toEqual({}); - }); }); describe('getFilterString()', () => { diff --git a/src/content/utils/airbnb.ts b/src/content/utils/airbnb.ts index abc2fe1..a369751 100644 --- a/src/content/utils/airbnb.ts +++ b/src/content/utils/airbnb.ts @@ -173,22 +173,20 @@ export const filterKeyToStringMap: { }; /** - * Parse our the individual location types from the `query` key - * in `search` portion of a URL. + * Parse our the individual location types from the pathname of + * a search. */ -export const getSearchLocation = ( - query: qs.ParsedQs[string] -): AirbnbLocation => { - if (!query) { - return {}; - } - - // Assume that the query value is a string - // eslint-disable-next-line no-param-reassign - query = query as string; +export const getSearchLocation = (pathname: string): AirbnbLocation => { + /** + * Index of the split pathname that includes the location. + * + * Example: `/s/United States/homes` + */ + const LOCATION_PATHNAME_INDEX = 2; - const locations = query - .split(', ') + const locations = pathname + .split('/') + [LOCATION_PATHNAME_INDEX].split('--') .map((location) => location.replace('-', ' ')); if (locations.length > 5) { @@ -196,7 +194,7 @@ export const getSearchLocation = ( `Failed to find expected number of locations in Airbnb location string! Num locations found: ${locations.length}` ); - return { unknown: query }; + return { unknown: pathname }; } const locationKeys: Array = [ diff --git a/src/content/utils/misc.ts b/src/content/utils/misc.ts index 6d19c3a..ae6cd75 100644 --- a/src/content/utils/misc.ts +++ b/src/content/utils/misc.ts @@ -26,11 +26,9 @@ export const sortReviewsByDateDesc = ( }; /** - * Parse a URL string using our default configuration + * Parse the search portion of a URL using our default configuration */ -export const getParsedUrlSearch = (url: string): qs.ParsedQs => { - const { search } = new URL(url); - +export const getParsedUrlSearch = (search: string): qs.ParsedQs => { return qs.parse(search, { ignoreQueryPrefix: true, }); diff --git a/src/utils/analytics.ts b/src/utils/analytics.ts index 2622764..85810e5 100644 --- a/src/utils/analytics.ts +++ b/src/utils/analytics.ts @@ -25,9 +25,11 @@ export const initAnalytics = (): void => { }; export const tryIdentifyUserFromUrlParam = (url: string): void => { - const search = getParsedUrlSearch(url); + const { search } = new URL(url); - const urlMixpanelId = search.mixpanel_id; + const parsedSearch = getParsedUrlSearch(search); + + const urlMixpanelId = parsedSearch.mixpanel_id; if (typeof urlMixpanelId !== 'string') { return; @@ -45,11 +47,14 @@ export const tryIdentifyUserFromUrlParam = (url: string): void => { } }; -export const logUrlChange = (urlStr: string): void => { - const search = getParsedUrlSearch(urlStr); +export const logUrlChange = (url: string): void => { + const { search, pathname } = new URL(url); + + const decodedPathname = decodeURIComponent(pathname); + const parsedSearch = getParsedUrlSearch(search); - const locations = getSearchLocation(search.query); - const filters = getMappedSearchFilters(search); + const locations = getSearchLocation(decodedPathname); + const filters = getMappedSearchFilters(parsedSearch); mixpanel.track(eventNames.AIRBNB_SEARCH_URL, { ...locations, ...filters }); };