@usereactify/search 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,3 +11,4 @@ export * from "./useFilterStack";
11
11
  export * from "./useProductPrice";
12
12
  export * from "./useFilterListProps";
13
13
  export * from "./useFilterCollapsedState";
14
+ export * from "./useLiveConfig";
@@ -23,3 +23,4 @@ __exportStar(require("./useFilterStack"), exports);
23
23
  __exportStar(require("./useProductPrice"), exports);
24
24
  __exportStar(require("./useFilterListProps"), exports);
25
25
  __exportStar(require("./useFilterCollapsedState"), exports);
26
+ __exportStar(require("./useLiveConfig"), exports);
@@ -0,0 +1,5 @@
1
+ import { Config } from "../types/config";
2
+ export declare const useLiveConfig: (shopifyPermanentDomain: string) => {
3
+ config: Config;
4
+ configLoading: boolean;
5
+ };
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.useLiveConfig = void 0;
16
+ const react_1 = __importDefault(require("react"));
17
+ const debug = require("debug")("reactify-search:useLiveConfig");
18
+ // 5 minute cache expiry
19
+ const CACHE_EXPIRY = 5 * 60 * 1000;
20
+ const useLiveConfig = (shopifyPermanentDomain) => {
21
+ // synchronously returns cached and non-expired config from session storage
22
+ const cachedConfig = react_1.default.useMemo(() => {
23
+ var _a;
24
+ // skip checking cache if url search param "nocache" is set
25
+ const skipCache = new URLSearchParams(window.location.href.split("?")[1]).get("nocache") !== null;
26
+ if (skipCache) {
27
+ debug("skipping cache");
28
+ return;
29
+ }
30
+ const sessionConfig = JSON.parse((_a = window.sessionStorage.getItem("reactify-search:config")) !== null && _a !== void 0 ? _a : "null");
31
+ if (sessionConfig) {
32
+ const sessionConfigTtl = sessionConfig.expiresAt - Date.now();
33
+ debug(`found ${sessionConfigTtl > 0 ? "cached" : "expired"} config`);
34
+ if (sessionConfigTtl > 0) {
35
+ debug(`config expires in ${(sessionConfigTtl / 1000).toFixed()} seconds`);
36
+ return sessionConfig.config;
37
+ }
38
+ }
39
+ return;
40
+ }, []);
41
+ const [config, setConfig] = react_1.default.useState(cachedConfig !== null && cachedConfig !== void 0 ? cachedConfig : {
42
+ fields: [],
43
+ filters: [],
44
+ sort: [],
45
+ curations: [],
46
+ redirects: [],
47
+ });
48
+ const [configLoading, setConfigLoading] = react_1.default.useState(cachedConfig ? false : true);
49
+ react_1.default.useEffect(() => {
50
+ // fetch remote config if no cached config is found
51
+ if (!cachedConfig) {
52
+ (() => __awaiter(void 0, void 0, void 0, function* () {
53
+ debug("fetching fresh config");
54
+ setConfigLoading(true);
55
+ const json = yield fetch(`https://config.search.reactify.app/?shop=${shopifyPermanentDomain}`).then((response) => response.json());
56
+ setConfig(json.body);
57
+ setConfigLoading(false);
58
+ window.sessionStorage.setItem("reactify-search:config", JSON.stringify({
59
+ expiresAt: new Date().getTime() + CACHE_EXPIRY,
60
+ config: json.body,
61
+ }));
62
+ }))();
63
+ }
64
+ }, [shopifyPermanentDomain]);
65
+ return {
66
+ config,
67
+ configLoading,
68
+ };
69
+ };
70
+ exports.useLiveConfig = useLiveConfig;
@@ -3,6 +3,7 @@ import type { Config, ConfigSort, ConfigFilter, ConfigCuration } from "./types/c
3
3
  declare type Context = {
4
4
  index: string;
5
5
  config: Config;
6
+ configLoading: boolean;
6
7
  shopifyPermanentDomain: string;
7
8
  sortOption: ConfigSort;
8
9
  sortOptions: ConfigSort[];
@@ -24,7 +25,6 @@ declare type Context = {
24
25
  declare const Context: React.Context<Context | undefined>;
25
26
  declare type Props = {
26
27
  index: string;
27
- config: Config;
28
28
  shopifyPermanentDomain: string;
29
29
  filterStackId?: string;
30
30
  collection?: Collection;
package/dist/provider.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.useContext = exports.Provider = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const UtilityAuthenticatedReactiveBase_1 = require("./utility/UtilityAuthenticatedReactiveBase");
9
+ const hooks_1 = require("./hooks");
9
10
  const debug = require("debug")("reactify-search:Provider");
10
11
  const Context = react_1.default.createContext(undefined);
11
12
  const defaultCredentials = {
@@ -15,7 +16,7 @@ const defaultCredentials = {
15
16
  };
16
17
  const Provider = (props) => {
17
18
  var _a, _b;
18
- const { index, config, shopifyPermanentDomain, children, collection, instantSearch, filterStackId, noReactiveBase, additionalComponentIds, } = props;
19
+ const { index, shopifyPermanentDomain, children, collection, instantSearch, filterStackId, noReactiveBase, additionalComponentIds, } = props;
19
20
  const credentials = (_a = props.credentials) !== null && _a !== void 0 ? _a : defaultCredentials;
20
21
  const theme = (_b = props.theme) !== null && _b !== void 0 ? _b : {
21
22
  typography: {
@@ -33,6 +34,7 @@ const Provider = (props) => {
33
34
  react_1.default.useEffect(() => {
34
35
  debug("props", props);
35
36
  }, [props]);
37
+ const { config, configLoading } = (0, hooks_1.useLiveConfig)(props.shopifyPermanentDomain);
36
38
  // @todo make this hackable with a prop
37
39
  // https://gitlab.com/reactifyapps/reactify-search-frontend/-/issues/1
38
40
  const searchQueryFromURL = react_1.default.useMemo(() => {
@@ -64,6 +66,7 @@ const Provider = (props) => {
64
66
  const contextValue = react_1.default.useMemo(() => ({
65
67
  index,
66
68
  config,
69
+ configLoading,
67
70
  shopifyPermanentDomain,
68
71
  collection,
69
72
  credentials,
@@ -110,13 +113,14 @@ exports.Provider = Provider;
110
113
  const useContext = () => react_1.default.useContext(Context);
111
114
  exports.useContext = useContext;
112
115
  const useSortState = (config, collection) => {
116
+ var _a;
113
117
  const sortOptions = react_1.default.useMemo(() => {
114
118
  const type = !!collection ? "collection" : "search";
115
119
  return config.sort
116
120
  .sort((a, b) => `${a.position}`.localeCompare(`${b.position}`))
117
121
  .filter(({ visibility }) => ["all", type].includes(visibility));
118
122
  }, [config, collection]);
119
- const [sortOptionState, setSortOptionState] = react_1.default.useState(sortOptions[0].id);
123
+ const [sortOptionState, setSortOptionState] = react_1.default.useState((_a = sortOptions[0]) === null || _a === void 0 ? void 0 : _a.id);
120
124
  const sortOption = react_1.default.useMemo(() => sortOptions.find(({ id }) => id === sortOptionState) || sortOptions[0], [sortOptions, sortOptionState]);
121
125
  const setSortOption = react_1.default.useCallback((sortOptionId) => setSortOptionState(sortOptionId), []);
122
126
  return react_1.default.useMemo(() => ({ sortOptions, sortOption, setSortOption }), [sortOption, sortOptions, setSortOption]);
@@ -14,6 +14,7 @@ declare type Props = {
14
14
  pageSize?: number;
15
15
  gridColumns?: number;
16
16
  listClassName?: string;
17
+ renderBooting?: () => JSX.Element | null;
17
18
  renderLoading?: () => JSX.Element | null;
18
19
  renderNoResults?: () => JSX.Element | null;
19
20
  renderResultCard?: Parameters<typeof ResultCard>[0]["render"];
@@ -11,6 +11,7 @@ const ResultPagination_1 = require("./ResultPagination");
11
11
  const ResultCardCallout_1 = require("./ResultCardCallout");
12
12
  const ResultLoadMoreButton_1 = require("./ResultLoadMoreButton");
13
13
  const ResultPaginationNextPrev_1 = require("./ResultPaginationNextPrev");
14
+ const provider_1 = require("../provider");
14
15
  const hooks_1 = require("../hooks");
15
16
  const elastic_1 = require("../types/elastic");
16
17
  const ResultList = (props) => {
@@ -22,9 +23,10 @@ const ResultList = (props) => {
22
23
  };
23
24
  exports.ResultList = ResultList;
24
25
  const ResultListInner = (props) => {
25
- const { gridColumns, renderError, renderAfter, renderBefore, renderLoading, listClassName, renderResults, renderNoResults, renderResultCard, renderLoadMoreButton, renderResultCardCallout, reactivesearchResultProps, } = props;
26
+ const { gridColumns, renderError, renderAfter, renderBefore, renderBooting, renderLoading, listClassName, renderResults, renderNoResults, renderResultCard, renderLoadMoreButton, renderResultCardCallout, reactivesearchResultProps, } = props;
26
27
  const filterStack = (0, hooks_1.useFilterStack)();
27
28
  const { instantSearch } = (0, hooks_1.useSearch)();
29
+ const { configLoading } = (0, provider_1.useContext)();
28
30
  const initialSearchHasRun = react_1.default.useMemo(() => "undefined" !==
29
31
  typeof reactivesearchResultProps.resultStats.numberOfResults, [reactivesearchResultProps]);
30
32
  const styleProp = react_1.default.useMemo(() => ({
@@ -32,6 +34,11 @@ const ResultListInner = (props) => {
32
34
  gridTemplateColumns: `repeat(${gridColumns !== null && gridColumns !== void 0 ? gridColumns : 4}, minmax(0, 1fr))`,
33
35
  }), [gridColumns]);
34
36
  const resultProps = react_1.default.useMemo(() => (Object.assign(Object.assign({}, reactivesearchResultProps), { products: reactivesearchResultProps.data.filter((document) => elastic_1.ElasticDocumentType.Product === document.type || !document.type), callouts: reactivesearchResultProps.data.filter((document) => elastic_1.ElasticDocumentType.Callout === document.type) })), [reactivesearchResultProps]);
37
+ if (configLoading) {
38
+ if (renderBooting)
39
+ return renderBooting();
40
+ return react_1.default.createElement("div", null, "Loading...");
41
+ }
35
42
  if (reactivesearchResultProps.error) {
36
43
  if (renderError)
37
44
  return renderError({ error: reactivesearchResultProps.error });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@usereactify/search",
3
3
  "description": "React UI library for Reactify Search",
4
- "version": "3.3.0",
4
+ "version": "3.4.0",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",