Skip to content

Commit 76df6cb

Browse files
committed
refactor: use Redux for contents/models state management
1 parent 2cfb3ea commit 76df6cb

File tree

4 files changed

+158
-102
lines changed

4 files changed

+158
-102
lines changed

src/shell/components/GlobalSearch/index.tsx

Lines changed: 83 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,14 @@ import { isEmpty } from "lodash";
2121
import { theme } from "@zesty-io/material";
2222

2323
import { useMetaKey } from "../../../shell/hooks/useMetaKey";
24-
import {
25-
useGetContentModelsQuery,
26-
useSearchContentQuery,
27-
} from "../../services/instance";
2824
import { notify } from "../../store/notifications";
2925
import { AdvancedSearch } from "./components/AdvancedSearch";
3026
import useRecentSearches from "../../hooks/useRecentSearches";
3127
import { GlobalSearchItem } from "./components/GlobalSearchItem";
3228
import { getContentTitle, getItemIcon } from "./utils";
3329
import { useSearchModelsByKeyword } from "../../hooks/useSearchModelsByKeyword";
3430
import { useSearchCodeFilesByKeywords } from "../../hooks/useSearchCodeFilesByKeyword";
35-
import { ResourceType } from "../../services/types";
31+
import { ContentItem, Language, ResourceType } from "../../services/types";
3632
import { SearchAccelerator } from "./components/SearchAccelerator";
3733
import { SEARCH_ACCELERATORS } from "./components/config";
3834
import { useGetActiveApp } from "../../hooks/useGetActiveApp";
@@ -47,6 +43,10 @@ import { KeywordSearchItem } from "./components/KeywordSearchItem";
4743
import { useParams } from "../../hooks/useParams";
4844
import { withCursorPosition } from "../../components/withCursorPosition";
4945
import { useSearchBlocksByKeyword } from "../../hooks/useSearchBlocksByKeyword";
46+
import { AppState } from "shell/store/types";
47+
import { fetchModels } from "shell/store/models";
48+
import { useDebounce } from "react-use";
49+
import { searchItems } from "shell/store/content";
5050

5151
// List of dropdown options that are NOT suggestions
5252
const AdditionalDropdownOptions = [
@@ -81,11 +81,7 @@ export const GlobalSearch = () => {
8181
const [isAdvancedSearchOpen, setIsAdvancedSearchOpen] = useState(false);
8282
const [recentSearches, addSearchTerm, deleteSearchTerm] = useRecentSearches();
8383
const [models, setModelKeyword] = useSearchModelsByKeyword();
84-
const {
85-
blocks,
86-
setBlockKeyword,
87-
isLoading: isFetchingBlocksResults,
88-
} = useSearchBlocksByKeyword();
84+
8985
const [codeFiles, setFileKeyword] = useSearchCodeFilesByKeywords();
9086
const [mediaFolders, setMediaFolderKeyword] =
9187
useSearchMediaFoldersByKeyword();
@@ -97,22 +93,29 @@ export const GlobalSearch = () => {
9793
const dispatch = useDispatch();
9894
const [params, setParams] = useParams();
9995
const textfieldRef = useRef<HTMLDivElement>();
100-
const { data: allModels } = useGetContentModelsQuery();
101-
102-
const apiQueryTerm = useMemo(() => {
103-
if (!!typedSearchAccelerator && !!searchKeyword) {
104-
// Remove the accelerator from the keyword
105-
return searchKeyword.replace(typedAcceleratorRegex, "")?.trim();
106-
}
107-
108-
return searchKeyword;
109-
}, [searchKeyword]);
110-
111-
const {
112-
data: contents,
113-
isError: isContentFetchingFailed,
114-
isFetching: isFetchingContentSearchResults,
115-
} = useSearchContentQuery({ query: apiQueryTerm });
96+
const allModels = useSelector((state: AppState) => state?.models);
97+
const [searchedContents, setSearchedContents] = useState<ContentItem[] | []>(
98+
[]
99+
);
100+
const [isFetchingContentSearchResults, setIsFetchingContentSearchResults] =
101+
useState(false);
102+
103+
const [apiQueryTerm, setApiQueryTerm] = useState("");
104+
105+
//debounce to reduce API calls during user input.
106+
useDebounce(
107+
() => {
108+
if (!!typedSearchAccelerator && !!searchKeyword) {
109+
// Remove the accelerator from the keyword
110+
setApiQueryTerm(
111+
searchKeyword.replace(typedAcceleratorRegex, "")?.trim()
112+
);
113+
}
114+
setApiQueryTerm(searchKeyword);
115+
},
116+
700,
117+
[searchKeyword]
118+
);
116119

117120
const { data: bins } = useGetBinsQuery({ instanceId, ecoId });
118121
const { data: mediaFiles, isFetching: isFetchingMediaSearchResults } =
@@ -128,35 +131,57 @@ export const GlobalSearch = () => {
128131
{ skip: !bins?.length }
129132
);
130133

134+
const { blocks, setBlockKeyword } = useSearchBlocksByKeyword({
135+
isLoading: isFetchingContentSearchResults,
136+
});
137+
131138
const isLoading =
132139
isFetchingAllMediaFiles ||
133140
isFetchingMediaSearchResults ||
134-
isFetchingContentSearchResults ||
135-
isFetchingBlocksResults;
141+
isFetchingContentSearchResults;
142+
143+
useEffect(() => {
144+
//Update models in store to provide access to other search hooks
145+
dispatch(fetchModels());
146+
}, []);
147+
148+
useEffect(() => {
149+
if (isEmpty(apiQueryTerm)) return setSearchedContents([]);
150+
setIsFetchingContentSearchResults(true);
151+
//Update contents in store to provide access to other search hooks
152+
dispatch(searchItems(apiQueryTerm))
153+
//@ts-ignore
154+
.then((res) => {
155+
if (!!res?.data?.length) {
156+
setSearchedContents(res?.data);
157+
} else {
158+
setSearchedContents([]);
159+
}
160+
setIsFetchingContentSearchResults(false);
161+
});
162+
}, [apiQueryTerm]);
136163

137164
const suggestions: Suggestion[] = useMemo(() => {
165+
//Filter out redundant block model items
166+
const filteredContents = searchedContents?.filter(
167+
(item: ContentItem) =>
168+
allModels?.[item?.meta?.contentModelZUID]?.type !== "block"
169+
);
138170
// Content data needs to be reset to [] when api call fails
139-
const contentSuggestions: Suggestion[] =
140-
isContentFetchingFailed || isEmpty(contents)
141-
? []
142-
: contents?.map((content) => {
143-
return {
144-
type: "content",
145-
ZUID: content.meta?.ZUID,
146-
title: getContentTitle(content, languages),
147-
updatedAt: content.meta?.updatedAt,
148-
url: isEmpty(content.meta)
149-
? ""
150-
: `/${
151-
allModels?.find(
152-
(model) => model.ZUID === content.meta.contentModelZUID
153-
)?.type === "block"
154-
? "blocks"
155-
: "content"
156-
}/${content.meta.contentModelZUID}/${content.meta.ZUID}`,
157-
noUrlErrorMessage: "Selected item is missing meta data",
158-
};
159-
});
171+
const contentSuggestions: Suggestion[] = isEmpty(filteredContents)
172+
? []
173+
: filteredContents?.map((content) => {
174+
return {
175+
type: "content",
176+
ZUID: content.meta?.ZUID,
177+
title: getContentTitle(content, languages),
178+
updatedAt: content.meta?.updatedAt,
179+
url: isEmpty(content.meta)
180+
? ""
181+
: `/content/${content.meta.contentModelZUID}/${content.meta.ZUID}`,
182+
noUrlErrorMessage: "Selected item is missing meta data",
183+
};
184+
});
160185

161186
const modelSuggestions: Suggestion[] =
162187
models?.map((model) => {
@@ -171,12 +196,17 @@ export const GlobalSearch = () => {
171196

172197
const blocksSuggestions: Suggestion[] =
173198
blocks?.map((block) => {
199+
const langCode = languages?.find(
200+
(lang: Language) => lang?.ID === block?.langID
201+
)?.code;
202+
const titlePrefix = !!langCode ? `(${langCode}) ` : "";
203+
174204
return {
175205
type: "block",
176206
ZUID: block?.ZUID,
177-
title: block?.label,
207+
title: block?.title, //`${titlePrefix}${block?.label}`,
178208
updatedAt: block?.updatedAt,
179-
url: `/blocks/${block?.ZUID}`,
209+
url: block?.url,
180210
};
181211
}) || [];
182212

@@ -265,13 +295,12 @@ export const GlobalSearch = () => {
265295
new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
266296
);
267297
}, [
268-
contents,
298+
searchedContents,
269299
models,
270300
blocks,
271301
codeFiles,
272302
mediaFiles,
273303
mediaFolders,
274-
isContentFetchingFailed,
275304
chipSearchAccelerator,
276305
allMediaFiles,
277306
]);
@@ -356,7 +385,7 @@ export const GlobalSearch = () => {
356385
});
357386
const url = `/search?${searchParams.toString()}`;
358387

359-
// Do not save term as a recent keyword if it's empty
388+
// Do not save term as a recent keyword if it's empty1
360389
if (!!queryTerm.trim()) {
361390
// If the user has selected a search accelerator, also save it in
362391
// the format of `[in:RESOURCE_TYPE]`

src/shell/hooks/useSearchBlocksByKeyword.ts

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
import { useEffect, useMemo, useState } from "react";
2-
import { ContentItem, ContentModel } from "../services/types";
3-
import {
4-
useGetContentModelsQuery,
5-
useGetLangsQuery,
6-
useSearchContentQuery,
7-
} from "../services/instance";
8-
import { useGetUsersQuery } from "../services/accounts";
2+
import { ContentItem, ContentModel, Language, User } from "../services/types";
93
import { BlockModel } from "../../shell/views/SearchPage/List/Block";
4+
import { AppState } from "shell/store/types";
5+
import { useSelector } from "react-redux";
106

117
type UsersMap = Record<string, string>;
128
type LanguageMap = Record<string, { code: string }>;
139

1410
type SearchResult = {
1511
blocks: BlockModel[];
1612
setBlockKeyword: (term: string) => void;
17-
isLoading: boolean;
1813
};
1914

2015
const toProperCase = (str?: string): string => {
@@ -27,23 +22,36 @@ const toProperCase = (str?: string): string => {
2722
.trim();
2823
};
2924

30-
export const useSearchBlocksByKeyword = (): SearchResult => {
25+
export const useSearchBlocksByKeyword = (
26+
contents?: ContentItem[],
27+
isLoading?: boolean
28+
): SearchResult => {
3129
const [searchTerm, setSearchTerm] = useState("");
30+
const [blockVariants, setBlockVariants] = useState([]);
3231

33-
const { data: modelsRaw, isLoading: isLoadingModels } =
34-
useGetContentModelsQuery();
35-
const { data: contentsRaw, isLoading: isLoadingContents } =
36-
useSearchContentQuery({ query: "", limit: 10000 });
32+
const modelsRaw: ContentModel[] = useSelector(
33+
(state: AppState) => state?.models
34+
);
35+
const users: User[] = useSelector((state: AppState) => state?.users);
36+
const languages: Language[] = useSelector(
37+
(state: AppState) => state?.languages
38+
);
39+
40+
const normalizedSearch = searchTerm.toLowerCase()?.trim();
3741

38-
const { data: users = [], isLoading: isLoadingUsers } = useGetUsersQuery();
39-
const { data: languages = [], isLoading: isLoadingLanguages } =
40-
useGetLangsQuery({});
42+
const blockModels = useMemo<BlockModel[]>(() => {
43+
if (!Object.values(modelsRaw)?.length) return [];
44+
return Object.values(modelsRaw)
45+
?.filter((block: ContentModel) => block?.type === "block")
46+
.map((block) => block as unknown as BlockModel);
47+
}, [modelsRaw]);
4148

42-
const isLoading =
43-
isLoadingModels ||
44-
isLoadingContents ||
45-
isLoadingUsers ||
46-
isLoadingLanguages;
49+
const blockModelsMap = useMemo(
50+
() =>
51+
!!blockModels?.length &&
52+
Object.fromEntries(blockModels?.map((model) => [model?.ZUID, model])),
53+
[blockModels]
54+
);
4755

4856
const usersMap = useMemo<UsersMap>(
4957
() =>
@@ -64,21 +72,26 @@ export const useSearchBlocksByKeyword = (): SearchResult => {
6472
[languages]
6573
);
6674

67-
const modelsMap = useMemo(
68-
() =>
69-
!!modelsRaw?.length &&
70-
Object.fromEntries(modelsRaw?.map((model) => [model?.ZUID, model])),
71-
[modelsRaw]
72-
);
73-
74-
const normalizedSearch = searchTerm.toLowerCase()?.trim();
75+
useEffect(() => {
76+
if (!normalizedSearch) return;
77+
const blockModelZUIDs = blockModels?.map((block) => block?.ZUID);
78+
const langIDs = Object.keys(languageMap);
79+
const variants = !contents?.length
80+
? []
81+
: contents?.filter(
82+
(content: ContentItem) =>
83+
blockModelZUIDs?.includes(content?.meta?.contentModelZUID) &&
84+
langIDs?.includes(String(content?.meta?.langID))
85+
);
86+
setBlockVariants(variants);
87+
}, [normalizedSearch, blockModels, languageMap, contents]);
7588

7689
const parsedBlocks: BlockModel[] = useMemo(() => {
7790
if (isLoading) return [];
7891

79-
const processedModels: BlockModel[] = !modelsRaw?.length
92+
const processedModels: BlockModel[] = !blockModels?.length
8093
? []
81-
: modelsRaw?.map((model) => ({
94+
: blockModels?.map((model: BlockModel) => ({
8295
ZUID: model?.ZUID,
8396
label: model?.label,
8497
type: model?.type,
@@ -88,12 +101,14 @@ export const useSearchBlocksByKeyword = (): SearchResult => {
88101
createdAt: model?.createdAt,
89102
createdByUserZUID: model?.createdByUserZUID,
90103
langID: null,
104+
url: `/blocks/${model?.ZUID}`,
91105
}));
92106

93-
const processedContent = !contentsRaw?.length
107+
const processedContent = !blockVariants?.length
94108
? []
95-
: contentsRaw?.map((item) => {
96-
const parentModel = modelsMap?.[item?.meta?.contentModelZUID] || null;
109+
: blockVariants?.map((item) => {
110+
const parentModel =
111+
blockModelsMap?.[item?.meta?.contentModelZUID] || null;
97112
return {
98113
ZUID: item?.meta?.ZUID,
99114
label: item?.web?.metaTitle || item?.web?.metaLinkText,
@@ -107,6 +122,7 @@ export const useSearchBlocksByKeyword = (): SearchResult => {
107122
createdByUserZUID:
108123
item?.meta?.createdByUserZUID || item?.web?.createdByUserZUID,
109124
langID: item?.meta?.langID,
125+
url: `/blocks/${item?.meta?.contentModelZUID}/${item?.meta?.ZUID}`,
110126
};
111127
});
112128

@@ -118,7 +134,6 @@ export const useSearchBlocksByKeyword = (): SearchResult => {
118134
title: item.label,
119135
}))
120136
.filter((item) => {
121-
if (item.type !== "block") return false;
122137
if (!normalizedSearch) return true;
123138

124139
const searchFields = [
@@ -137,16 +152,16 @@ export const useSearchBlocksByKeyword = (): SearchResult => {
137152
});
138153
}, [
139154
isLoading,
140-
contentsRaw,
141-
modelsRaw,
155+
blockVariants,
156+
blockModels,
142157
languageMap,
143158
usersMap,
159+
blockModelsMap,
144160
normalizedSearch,
145161
]);
146162

147163
return {
148164
blocks: parsedBlocks,
149165
setBlockKeyword: setSearchTerm,
150-
isLoading,
151166
};
152167
};

src/shell/views/SearchPage/List/Block.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ export type BlockModel = Partial<ContentModel> & {
1616
createdAt?: string;
1717
updatedAt?: string;
1818
lang?: string;
19-
langID?: number;
19+
langID: number | null;
2020
title?: string;
21+
url?: string | null;
2122
};
2223

2324
type Block = {

0 commit comments

Comments
 (0)