Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bundlesize.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
},
{
"path": "./packages/instantsearch.js/dist/instantsearch.development.js",
"maxSize": "181.50 kB"
"maxSize": "185 kB"
},
{
"path": "packages/react-instantsearch-core/dist/umd/ReactInstantSearchCore.min.js",
"maxSize": "51.25 kB"
},
{
"path": "packages/react-instantsearch/dist/umd/ReactInstantSearch.min.js",
"maxSize": "65 kB"
"maxSize": "65.50 kB"
},
{
"path": "packages/vue-instantsearch/vue2/umd/index.js",
Expand Down
4 changes: 1 addition & 3 deletions examples/react/next-app-router/app/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';

import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { Hit as AlgoliaHit } from 'instantsearch.js';
import React from 'react';
import {
Expand All @@ -13,8 +12,7 @@ import {
import { InstantSearchNext } from 'react-instantsearch-nextjs';

import { Panel } from '../components/Panel';

const client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76');
import { client } from '../lib/client';

type HitProps = {
hit: AlgoliaHit<{
Expand Down
4 changes: 4 additions & 0 deletions examples/react/next-app-router/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import React from 'react';

import { responsesCache } from '../lib/client';

import Search from './Search';

export const dynamic = 'force-dynamic';

export default function Page() {
responsesCache.clear();

return <Search />;
}
9 changes: 9 additions & 0 deletions examples/react/next-app-router/lib/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createMemoryCache } from '@algolia/client-common';
import { liteClient as algoliasearch } from 'algoliasearch/lite';

export const responsesCache = createMemoryCache();
export const client = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76',
{ responsesCache }
);
7 changes: 6 additions & 1 deletion examples/react/next-routing/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createMemoryCache } from '@algolia/client-common';
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { Hit as AlgoliaHit } from 'instantsearch.js';
import { GetServerSideProps } from 'next';
Expand All @@ -21,7 +22,10 @@ import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs

import { Panel } from '../components/Panel';

const client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76');
const requestsCache = createMemoryCache();
const client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76', {
requestsCache,
});

type HitProps = {
hit: AlgoliaHit<{
Expand Down Expand Up @@ -99,6 +103,7 @@ export const getServerSideProps: GetServerSideProps<HomePageProps> =
const serverState = await getServerState(<HomePage url={url} />, {
renderToString,
});
requestsCache.clear();

return {
props: {
Expand Down
7 changes: 6 additions & 1 deletion examples/react/next-routing/pages/test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// This is only to test `onStateChange` does not get called twice
import { createMemoryCache } from '@algolia/client-common';
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { GetServerSideProps } from 'next';
import Head from 'next/head';
Expand All @@ -16,7 +17,10 @@ import {
} from 'react-instantsearch';
import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';

const client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76');
const requestsCache = createMemoryCache();
const client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76', {
requestsCache,
});

type HomePageProps = {
serverState?: InstantSearchServerState;
Expand Down Expand Up @@ -74,6 +78,7 @@ export const getServerSideProps: GetServerSideProps<HomePageProps> =
const serverState = await getServerState(<HomePage url={url} />, {
renderToString,
});
requestsCache.clear();

return {
props: {
Expand Down
11 changes: 10 additions & 1 deletion examples/react/next/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createMemoryCache } from '@algolia/client-common';
import { liteClient as algoliasearch } from 'algoliasearch/lite';
import { Hit as AlgoliaHit } from 'instantsearch.js';
import { GetServerSideProps } from 'next';
Expand All @@ -20,7 +21,10 @@ import { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs

import { Panel } from '../components/Panel';

const client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76');
const responsesCache = createMemoryCache();
const client = algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76', {
responsesCache,
});

type HitProps = {
hit: AlgoliaHit<{
Expand Down Expand Up @@ -63,6 +67,9 @@ export default function HomePage({ serverState, url }: HomePageProps) {
}),
}}
insights={true}
future={{
preserveSharedStateOnUnmount: true,
}}
>
<div className="Container">
<div>
Expand Down Expand Up @@ -94,6 +101,8 @@ export const getServerSideProps: GetServerSideProps<HomePageProps> =
renderToString,
});

responsesCache.clear();

return {
props: {
serverState,
Expand Down
7 changes: 6 additions & 1 deletion examples/react/ssr/src/searchClient.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { createMemoryCache } from '@algolia/client-common';
import { liteClient as algoliasearch } from 'algoliasearch/lite';

export const requestsCache = createMemoryCache();
export const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
'6be0576ff61c053d5f9a3225e2a90f76',
{
requestsCache,
}
);
2 changes: 2 additions & 0 deletions examples/react/ssr/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { renderToString } from 'react-dom/server';
import { getServerState } from 'react-instantsearch';

import App from './App';
import { requestsCache } from './searchClient';

const app = express();

Expand All @@ -18,6 +19,7 @@ app.get('/', async (req, res) => {
const serverState = await getServerState(<App location={location} />, {
renderToString,
});
requestsCache.clear();
const html = renderToString(
<App serverState={serverState} location={location} />
);
Expand Down
6 changes: 6 additions & 0 deletions packages/algoliasearch-helper/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,12 @@ declare namespace algoliasearchHelper {
* https://www.algolia.com/doc/api-reference/api-parameters/optionalFilters/
*/
optionalFilters?: Array<string | string[]>;
/**
* Unique pseudonymous or anonymous user identifier.
* This helps with analytics and click and conversion events.
* For more information, see [user token](https://www.algolia.com/doc/guides/sending-events/concepts/usertoken/).
*/
userToken?: string;
/**
* If set to false, this query will not be taken into account in the analytics feature.
* default true
Expand Down
72 changes: 72 additions & 0 deletions packages/instantsearch.js/src/lib/__tests__/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,4 +421,76 @@ describe('getInitialResults', () => {
]
`);
});

test('injects clickAnalytics and userToken into the state', async () => {
const search = instantsearch({
indexName: 'indexName',
searchClient: createSearchClient(),
initialUiState: {
indexName: {
query: 'apple',
},
},
insights: true,
});

search.addWidgets([connectSearchBox(() => {})({})]);

search.start();

const requestParams = await waitForResults(search);

expect(requestParams).toEqual([
{
clickAnalytics: true,
query: 'apple',
userToken: expect.stringMatching(/^anonymous-/),
},
]);

const initialResults = getInitialResults(search.mainIndex, requestParams);
expect(initialResults).toEqual({
indexName: {
requestParams: [
{
clickAnalytics: true,
query: 'apple',
userToken: expect.stringMatching(/^anonymous-/),
},
],
results: [
{
exhaustiveFacetsCount: true,
exhaustiveNbHits: true,
hits: [],
hitsPerPage: 20,
nbHits: 0,
nbPages: 0,
page: 0,
params: '',
processingTimeMS: 0,
query: '',
},
],
state: {
disjunctiveFacets: [],
disjunctiveFacetsRefinements: {},
facets: [],
facetsExcludes: {},
facetsRefinements: {},
hierarchicalFacets: [],
hierarchicalFacetsRefinements: {},
index: 'indexName',
numericRefinements: {},
query: 'apple',
tagRefinements: [],
clickAnalytics: true,
userToken: expect.stringMatching(/^anonymous-/),
},
},
});
expect(initialResults.indexName.requestParams![0].userToken).toEqual(
initialResults.indexName.state!.userToken
);
});
});
6 changes: 5 additions & 1 deletion packages/instantsearch.js/src/lib/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ export function getInitialResults(
// We convert the Helper state to a plain object to pass parsable data
// structures from server to client.
...(searchResults && {
state: { ...searchResults._state },
state: {
...searchResults._state,
clickAnalytics: requestParams?.[0]?.clickAnalytics,
userToken: requestParams?.[0]?.userToken,
},
results: searchResults._rawResults,
}),
...(recommendResults && {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,28 @@ describe('insights', () => {
expect(getUserToken()).toBe('def');
});

it('uses `userToken` from initial results', () => {
const { insightsClient, instantSearchInstance, getUserToken } =
createTestEnvironment();

instantSearchInstance._initialResults = {
[instantSearchInstance.indexName]: {
state: {
userToken: 'from-initial-results',
clickAnalytics: true,
},
},
};

instantSearchInstance.use(
createInsightsMiddleware({
insightsClient,
})
);

expect(getUserToken()).toEqual('from-initial-results');
});

describe('authenticatedUserToken', () => {
describe('before `init`', () => {
it('uses the `authenticatedUserToken` as the `userToken` when defined', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
InsightsMethod,
InsightsMethodMap,
InternalMiddleware,
InstantSearch,
} from '../types';
import type {
AlgoliaSearchHelper,
Expand Down Expand Up @@ -222,10 +223,7 @@ export function createInsightsMiddleware<
});
}

initialParameters = {
userToken: (helper.state as PlainSearchParameters).userToken,
clickAnalytics: helper.state.clickAnalytics,
};
initialParameters = getInitialParameters(instantSearchInstance);

// We don't want to force clickAnalytics when the insights is enabled from the search response.
// This means we don't enable insights for indices that don't opt in
Expand Down Expand Up @@ -522,6 +520,23 @@ See documentation: https://www.algolia.com/doc/guides/building-search-ui/going-f
};
}

function getInitialParameters(
instantSearchInstance: InstantSearch
): PlainSearchParameters {
// in SSR, the initial state we use in this domain is set on the main index
const stateFromInitialResults =
instantSearchInstance._initialResults?.[instantSearchInstance.indexName]
?.state || {};

const stateFromHelper = instantSearchInstance.mainHelper!.state;

return {
userToken: stateFromInitialResults.userToken || stateFromHelper.userToken,
clickAnalytics:
stateFromInitialResults.clickAnalytics || stateFromHelper.clickAnalytics,
};
}

function saveTokenAsCookie(token: string, cookieDuration?: number) {
const MONTH = 30 * 24 * 60 * 60 * 1000;
const d = new Date();
Expand Down
Loading