@stackoverflow/backstage-plugin-stack-overflow-teams 1.5.0 → 1.6.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.
- package/README.md +3 -3
- package/config.d.ts +1 -1
- package/package.json +4 -12
- package/dist/api/StackOverflowAPI.esm.js +0 -125
- package/dist/api/StackOverflowAPI.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowMe.esm.js +0 -110
- package/dist/components/StackOverflow/StackOverflowMe.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowPostQuestionModal.esm.js +0 -406
- package/dist/components/StackOverflow/StackOverflowPostQuestionModal.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowPosts.esm.js +0 -402
- package/dist/components/StackOverflow/StackOverflowPosts.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowSearchResultListItem.esm.js +0 -147
- package/dist/components/StackOverflow/StackOverflowSearchResultListItem.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowTags.esm.js +0 -117
- package/dist/components/StackOverflow/StackOverflowTags.esm.js.map +0 -1
- package/dist/components/StackOverflow/StackOverflowUsers.esm.js +0 -193
- package/dist/components/StackOverflow/StackOverflowUsers.esm.js.map +0 -1
- package/dist/components/StackOverflow/TiptapEditor.esm.js +0 -308
- package/dist/components/StackOverflow/TiptapEditor.esm.js.map +0 -1
- package/dist/components/StackOverflow/hooks/useStackOverflowData.esm.js +0 -128
- package/dist/components/StackOverflow/hooks/useStackOverflowData.esm.js.map +0 -1
- package/dist/components/StackOverflow/hooks/useStackOverflowSearch.esm.js +0 -53
- package/dist/components/StackOverflow/hooks/useStackOverflowSearch.esm.js.map +0 -1
- package/dist/components/StackOverflow/hooks/useStackOverflowStyles.esm.js +0 -39
- package/dist/components/StackOverflow/hooks/useStackOverflowStyles.esm.js.map +0 -1
- package/dist/components/StackOverflowAuth/StackAuthCallback.esm.js +0 -47
- package/dist/components/StackOverflowAuth/StackAuthCallback.esm.js.map +0 -1
- package/dist/components/StackOverflowAuth/StackAuthFailed.esm.js +0 -35
- package/dist/components/StackOverflowAuth/StackAuthFailed.esm.js.map +0 -1
- package/dist/components/StackOverflowAuth/StackAuthLoading.esm.js +0 -20
- package/dist/components/StackOverflowAuth/StackAuthLoading.esm.js.map +0 -1
- package/dist/components/StackOverflowAuth/StackAuthStart.esm.js +0 -225
- package/dist/components/StackOverflowAuth/StackAuthStart.esm.js.map +0 -1
- package/dist/components/StackOverflowAuth/StackAuthSuccess.esm.js +0 -37
- package/dist/components/StackOverflowAuth/StackAuthSuccess.esm.js.map +0 -1
- package/dist/icons/LogoutIcon.esm.js +0 -24
- package/dist/icons/LogoutIcon.esm.js.map +0 -1
- package/dist/icons/StackOverflowIcon.esm.js +0 -25
- package/dist/icons/StackOverflowIcon.esm.js.map +0 -1
- package/dist/index.d.ts +0 -48
- package/dist/index.esm.js +0 -18
- package/dist/index.esm.js.map +0 -1
- package/dist/package.json.esm.js +0 -98
- package/dist/package.json.esm.js.map +0 -1
- package/dist/pages/StackOverflowHub.esm.js +0 -82
- package/dist/pages/StackOverflowHub.esm.js.map +0 -1
- package/dist/pages/StackOverflowTeamsPage.esm.js +0 -42
- package/dist/pages/StackOverflowTeamsPage.esm.js.map +0 -1
- package/dist/pages/index.esm.js +0 -3
- package/dist/pages/index.esm.js.map +0 -1
- package/dist/plugin.esm.js +0 -27
- package/dist/plugin.esm.js.map +0 -1
- package/dist/routes.esm.js +0 -8
- package/dist/routes.esm.js.map +0 -1
- package/dist/utils/decodeHtml.esm.js +0 -8
- package/dist/utils/decodeHtml.esm.js.map +0 -1
- package/dist/utils/getTimeAgo.esm.js +0 -30
- package/dist/utils/getTimeAgo.esm.js.map +0 -1
|
@@ -1,402 +0,0 @@
|
|
|
1
|
-
import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
2
|
-
import { makeStyles, Box, TextField, InputAdornment, Paper, ButtonGroup, Button, Typography, Grid } from '@material-ui/core';
|
|
3
|
-
import Skeleton from '@mui/material/Skeleton';
|
|
4
|
-
import { ResponseErrorPanel } from '@backstage/core-components';
|
|
5
|
-
import { useStackOverflowData } from './hooks/useStackOverflowData.esm.js';
|
|
6
|
-
import { useStackOverflowSearch } from './hooks/useStackOverflowSearch.esm.js';
|
|
7
|
-
import './hooks/useStackOverflowStyles.esm.js';
|
|
8
|
-
import { StackOverflowSearchResultListItem } from './StackOverflowSearchResultListItem.esm.js';
|
|
9
|
-
import SearchIcon from '@material-ui/icons/Search';
|
|
10
|
-
import { StackOverflowIcon } from '../../icons/StackOverflowIcon.esm.js';
|
|
11
|
-
|
|
12
|
-
const useStyles = makeStyles((theme) => ({
|
|
13
|
-
filters: {
|
|
14
|
-
padding: theme.spacing(2),
|
|
15
|
-
marginTop: theme.spacing(2)
|
|
16
|
-
},
|
|
17
|
-
buttonGroup: {
|
|
18
|
-
flexWrap: "wrap"
|
|
19
|
-
},
|
|
20
|
-
resultCount: {
|
|
21
|
-
marginTop: theme.spacing(1),
|
|
22
|
-
fontSize: "0.875rem",
|
|
23
|
-
color: theme.palette.text.secondary
|
|
24
|
-
},
|
|
25
|
-
searchField: {
|
|
26
|
-
borderRadius: 40,
|
|
27
|
-
paddingLeft: 16,
|
|
28
|
-
paddingRight: 16
|
|
29
|
-
},
|
|
30
|
-
pagination: {
|
|
31
|
-
display: "flex",
|
|
32
|
-
justifyContent: "center",
|
|
33
|
-
alignItems: "center",
|
|
34
|
-
marginTop: theme.spacing(2),
|
|
35
|
-
marginBottom: theme.spacing(2)
|
|
36
|
-
},
|
|
37
|
-
loadingContainer: {
|
|
38
|
-
minHeight: "600px"
|
|
39
|
-
// Fixed height for consistency
|
|
40
|
-
},
|
|
41
|
-
loadingSkeletonItem: {
|
|
42
|
-
padding: theme.spacing(2),
|
|
43
|
-
marginBottom: theme.spacing(2),
|
|
44
|
-
borderRadius: theme.shape.borderRadius
|
|
45
|
-
},
|
|
46
|
-
skeletonContent: {
|
|
47
|
-
display: "flex",
|
|
48
|
-
flexDirection: "column",
|
|
49
|
-
gap: theme.spacing(1)
|
|
50
|
-
},
|
|
51
|
-
skeletonHeader: {
|
|
52
|
-
display: "flex",
|
|
53
|
-
alignItems: "center",
|
|
54
|
-
gap: theme.spacing(1),
|
|
55
|
-
marginBottom: theme.spacing(1)
|
|
56
|
-
},
|
|
57
|
-
skeletonTags: {
|
|
58
|
-
display: "flex",
|
|
59
|
-
gap: theme.spacing(1),
|
|
60
|
-
marginTop: theme.spacing(1)
|
|
61
|
-
}
|
|
62
|
-
}));
|
|
63
|
-
const FILTERS = [
|
|
64
|
-
{ id: "unanswered", label: "Unanswered" },
|
|
65
|
-
{ id: "newest", label: "Newest" },
|
|
66
|
-
{ id: "active", label: "Active" },
|
|
67
|
-
{ id: "score", label: "Score" }
|
|
68
|
-
];
|
|
69
|
-
const CLIENT_ITEMS_PER_PAGE = 5;
|
|
70
|
-
const SERVER_ITEMS_PER_PAGE = 30;
|
|
71
|
-
const SEARCH_ITEMS_PER_PAGE = 30;
|
|
72
|
-
const useDebounce = (value, delay) => {
|
|
73
|
-
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
74
|
-
useEffect(() => {
|
|
75
|
-
const handler = setTimeout(() => {
|
|
76
|
-
setDebouncedValue(value);
|
|
77
|
-
}, delay);
|
|
78
|
-
return () => {
|
|
79
|
-
clearTimeout(handler);
|
|
80
|
-
};
|
|
81
|
-
}, [value, delay]);
|
|
82
|
-
return debouncedValue;
|
|
83
|
-
};
|
|
84
|
-
const LoadingSkeleton = () => {
|
|
85
|
-
const classes = useStyles();
|
|
86
|
-
const skeletonKeys = useMemo(
|
|
87
|
-
() => Array.from(
|
|
88
|
-
{ length: CLIENT_ITEMS_PER_PAGE },
|
|
89
|
-
(_, index) => `skeleton-${index}-${Math.random().toString(36).substr(2, 9)}`
|
|
90
|
-
),
|
|
91
|
-
[]
|
|
92
|
-
);
|
|
93
|
-
return /* @__PURE__ */ React.createElement("div", { className: classes.loadingContainer }, skeletonKeys.map((key) => /* @__PURE__ */ React.createElement(Paper, { key, className: classes.loadingSkeletonItem, elevation: 1 }, /* @__PURE__ */ React.createElement("div", { className: classes.skeletonContent }, /* @__PURE__ */ React.createElement("div", { className: classes.skeletonHeader }, /* @__PURE__ */ React.createElement(Skeleton, { variant: "circular", width: 32, height: 32 }), /* @__PURE__ */ React.createElement("div", { style: { flex: 1 } }, /* @__PURE__ */ React.createElement(Skeleton, { variant: "text", width: "60%", height: 20 }), /* @__PURE__ */ React.createElement(Skeleton, { variant: "text", width: "40%", height: 16 })), /* @__PURE__ */ React.createElement(Skeleton, { variant: "rectangular", width: 60, height: 24 })), /* @__PURE__ */ React.createElement(Skeleton, { variant: "text", width: "90%", height: 24 }), /* @__PURE__ */ React.createElement(Skeleton, { variant: "text", width: "75%", height: 20 }), /* @__PURE__ */ React.createElement(Skeleton, { variant: "text", width: "60%", height: 20 }), /* @__PURE__ */ React.createElement("div", { className: classes.skeletonTags }, /* @__PURE__ */ React.createElement(Skeleton, { variant: "rectangular", width: 60, height: 20 }), /* @__PURE__ */ React.createElement(Skeleton, { variant: "rectangular", width: 80, height: 20 }), /* @__PURE__ */ React.createElement(Skeleton, { variant: "rectangular", width: 45, height: 20 })), /* @__PURE__ */ React.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 8 } }, /* @__PURE__ */ React.createElement(Skeleton, { variant: "text", width: 120, height: 16 }), /* @__PURE__ */ React.createElement(Skeleton, { variant: "text", width: 80, height: 16 }))))));
|
|
94
|
-
};
|
|
95
|
-
const calculateServerPage = (clientPage, serverPageSize = SERVER_ITEMS_PER_PAGE) => {
|
|
96
|
-
return Math.ceil(clientPage * CLIENT_ITEMS_PER_PAGE / serverPageSize);
|
|
97
|
-
};
|
|
98
|
-
const calculateItemsRange = (clientPage, serverPage, serverPageSize = SERVER_ITEMS_PER_PAGE) => {
|
|
99
|
-
const globalStartIndex = (clientPage - 1) * CLIENT_ITEMS_PER_PAGE;
|
|
100
|
-
const serverStartIndex = (serverPage - 1) * serverPageSize;
|
|
101
|
-
const localStartIndex = globalStartIndex - serverStartIndex;
|
|
102
|
-
const localEndIndex = localStartIndex + CLIENT_ITEMS_PER_PAGE;
|
|
103
|
-
return { localStartIndex, localEndIndex };
|
|
104
|
-
};
|
|
105
|
-
const useEnhancedSearch = () => {
|
|
106
|
-
const searchHook = useStackOverflowSearch();
|
|
107
|
-
const [searchCache, setSearchCache] = useState({});
|
|
108
|
-
const lastSearchRef = useRef(null);
|
|
109
|
-
const getActualSearchPageSize = useCallback(() => {
|
|
110
|
-
return searchHook.searchData?.pageSize || SEARCH_ITEMS_PER_PAGE;
|
|
111
|
-
}, [searchHook.searchData?.pageSize]);
|
|
112
|
-
const enhancedSearch = useCallback((term, clientPage) => {
|
|
113
|
-
const actualPageSize = getActualSearchPageSize();
|
|
114
|
-
const serverPage = calculateServerPage(clientPage, actualPageSize);
|
|
115
|
-
const cacheKey = `${term}-${serverPage}`;
|
|
116
|
-
const currentSearch = { term, page: serverPage };
|
|
117
|
-
if (lastSearchRef.current && lastSearchRef.current.term === currentSearch.term && lastSearchRef.current.page === currentSearch.page) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
if (searchCache[cacheKey]) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
lastSearchRef.current = currentSearch;
|
|
124
|
-
searchHook.search(term, serverPage);
|
|
125
|
-
}, [getActualSearchPageSize, searchCache, searchHook]);
|
|
126
|
-
useEffect(() => {
|
|
127
|
-
if (searchHook.searchData && lastSearchRef.current) {
|
|
128
|
-
const serverPage = searchHook.searchData.page;
|
|
129
|
-
const searchTerm = lastSearchRef.current.term;
|
|
130
|
-
const cacheKey = `${searchTerm}-${serverPage}`;
|
|
131
|
-
setSearchCache((prev) => ({
|
|
132
|
-
...prev,
|
|
133
|
-
[cacheKey]: {
|
|
134
|
-
items: searchHook?.searchData?.items,
|
|
135
|
-
totalCount: searchHook?.searchData?.totalCount,
|
|
136
|
-
pageSize: searchHook?.searchData?.pageSize,
|
|
137
|
-
timestamp: Date.now()
|
|
138
|
-
}
|
|
139
|
-
}));
|
|
140
|
-
}
|
|
141
|
-
}, [searchHook.searchData]);
|
|
142
|
-
const getSearchDisplayData = useCallback((term, clientPage) => {
|
|
143
|
-
const actualPageSize = getActualSearchPageSize();
|
|
144
|
-
const serverPage = calculateServerPage(clientPage, actualPageSize);
|
|
145
|
-
const cacheKey = `${term}-${serverPage}`;
|
|
146
|
-
const cachedData = searchCache[cacheKey];
|
|
147
|
-
if (!cachedData) {
|
|
148
|
-
return {
|
|
149
|
-
currentPageData: [],
|
|
150
|
-
totalCount: 0,
|
|
151
|
-
loading: searchHook.loading,
|
|
152
|
-
error: searchHook.error
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
const { localStartIndex, localEndIndex } = calculateItemsRange(
|
|
156
|
-
clientPage,
|
|
157
|
-
serverPage,
|
|
158
|
-
cachedData.pageSize
|
|
159
|
-
);
|
|
160
|
-
const currentPageData = cachedData.items.slice(localStartIndex, localEndIndex);
|
|
161
|
-
return {
|
|
162
|
-
currentPageData,
|
|
163
|
-
totalCount: cachedData.totalCount,
|
|
164
|
-
loading: false,
|
|
165
|
-
error: null
|
|
166
|
-
};
|
|
167
|
-
}, [searchCache, searchHook.loading, searchHook.error, getActualSearchPageSize]);
|
|
168
|
-
const clearSearchCache = useCallback(() => {
|
|
169
|
-
setSearchCache({});
|
|
170
|
-
lastSearchRef.current = null;
|
|
171
|
-
}, []);
|
|
172
|
-
return {
|
|
173
|
-
enhancedSearch,
|
|
174
|
-
getSearchDisplayData,
|
|
175
|
-
clearSearch: searchHook.clearSearch,
|
|
176
|
-
clearSearchCache,
|
|
177
|
-
loading: searchHook.loading,
|
|
178
|
-
error: searchHook.error
|
|
179
|
-
};
|
|
180
|
-
};
|
|
181
|
-
const StackOverflowQuestions = () => {
|
|
182
|
-
const classes = useStyles();
|
|
183
|
-
const {
|
|
184
|
-
data: questionsData,
|
|
185
|
-
loading: questionsLoading,
|
|
186
|
-
error: questionsError,
|
|
187
|
-
fetchActiveQuestions,
|
|
188
|
-
fetchNewestQuestions,
|
|
189
|
-
fetchTopScoredQuestions,
|
|
190
|
-
fetchUnansweredQuestions
|
|
191
|
-
} = useStackOverflowData("questions");
|
|
192
|
-
const {
|
|
193
|
-
enhancedSearch,
|
|
194
|
-
getSearchDisplayData,
|
|
195
|
-
clearSearch,
|
|
196
|
-
clearSearchCache
|
|
197
|
-
} = useEnhancedSearch();
|
|
198
|
-
const [searchTerm, setSearchTerm] = useState("");
|
|
199
|
-
const [activeFilter, setActiveFilter] = useState("active");
|
|
200
|
-
const [currentPage, setCurrentPage] = useState(1);
|
|
201
|
-
const [questionsPage, setQuestionsPage] = useState(1);
|
|
202
|
-
const prevSearchModeRef = useRef(false);
|
|
203
|
-
const enhancedSearchRef = useRef(enhancedSearch);
|
|
204
|
-
const clearSearchRef = useRef(clearSearch);
|
|
205
|
-
const clearSearchCacheRef = useRef(clearSearchCache);
|
|
206
|
-
useEffect(() => {
|
|
207
|
-
enhancedSearchRef.current = enhancedSearch;
|
|
208
|
-
clearSearchRef.current = clearSearch;
|
|
209
|
-
clearSearchCacheRef.current = clearSearchCache;
|
|
210
|
-
});
|
|
211
|
-
const debouncedSearchTerm = useDebounce(searchTerm, 500);
|
|
212
|
-
const isSearchMode = !!searchTerm.trim();
|
|
213
|
-
const requiredServerPage = useMemo(() => {
|
|
214
|
-
return calculateServerPage(questionsPage, SERVER_ITEMS_PER_PAGE);
|
|
215
|
-
}, [questionsPage]);
|
|
216
|
-
useEffect(() => {
|
|
217
|
-
if (!isSearchMode) {
|
|
218
|
-
switch (activeFilter) {
|
|
219
|
-
case "active":
|
|
220
|
-
fetchActiveQuestions(requiredServerPage);
|
|
221
|
-
break;
|
|
222
|
-
case "newest":
|
|
223
|
-
fetchNewestQuestions(requiredServerPage);
|
|
224
|
-
break;
|
|
225
|
-
case "score":
|
|
226
|
-
fetchTopScoredQuestions(requiredServerPage);
|
|
227
|
-
break;
|
|
228
|
-
case "unanswered":
|
|
229
|
-
fetchUnansweredQuestions(requiredServerPage);
|
|
230
|
-
break;
|
|
231
|
-
default:
|
|
232
|
-
fetchActiveQuestions(requiredServerPage);
|
|
233
|
-
break;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}, [activeFilter, requiredServerPage, isSearchMode, fetchActiveQuestions, fetchNewestQuestions, fetchTopScoredQuestions, fetchUnansweredQuestions]);
|
|
237
|
-
useEffect(() => {
|
|
238
|
-
const wasInSearchMode = prevSearchModeRef.current;
|
|
239
|
-
if (isSearchMode && !wasInSearchMode) {
|
|
240
|
-
setCurrentPage(1);
|
|
241
|
-
} else if (!isSearchMode && wasInSearchMode) {
|
|
242
|
-
setQuestionsPage(1);
|
|
243
|
-
setCurrentPage(1);
|
|
244
|
-
}
|
|
245
|
-
prevSearchModeRef.current = isSearchMode;
|
|
246
|
-
}, [isSearchMode]);
|
|
247
|
-
useEffect(() => {
|
|
248
|
-
if (!isSearchMode) {
|
|
249
|
-
setQuestionsPage(1);
|
|
250
|
-
setCurrentPage(1);
|
|
251
|
-
}
|
|
252
|
-
}, [activeFilter, isSearchMode]);
|
|
253
|
-
useEffect(() => {
|
|
254
|
-
if (debouncedSearchTerm.trim()) {
|
|
255
|
-
if (isSearchMode) {
|
|
256
|
-
enhancedSearchRef.current(debouncedSearchTerm, currentPage);
|
|
257
|
-
}
|
|
258
|
-
} else {
|
|
259
|
-
clearSearchRef.current();
|
|
260
|
-
clearSearchCacheRef.current();
|
|
261
|
-
}
|
|
262
|
-
}, [debouncedSearchTerm, currentPage, isSearchMode]);
|
|
263
|
-
const handleKeyPress = (e) => {
|
|
264
|
-
if (e.key === "Enter" && searchTerm.trim()) {
|
|
265
|
-
enhancedSearchRef.current(searchTerm, currentPage);
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
const displayInfo = useMemo(() => {
|
|
269
|
-
if (isSearchMode) {
|
|
270
|
-
const searchDisplayData = getSearchDisplayData(searchTerm, currentPage);
|
|
271
|
-
const totalPages2 = Math.ceil(searchDisplayData.totalCount / CLIENT_ITEMS_PER_PAGE);
|
|
272
|
-
return {
|
|
273
|
-
currentPageData: searchDisplayData.currentPageData,
|
|
274
|
-
totalPages: totalPages2,
|
|
275
|
-
totalCount: searchDisplayData.totalCount,
|
|
276
|
-
loading: searchDisplayData.loading,
|
|
277
|
-
error: searchDisplayData.error
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
if (!questionsData?.questions) {
|
|
281
|
-
return {
|
|
282
|
-
currentPageData: [],
|
|
283
|
-
totalPages: 1,
|
|
284
|
-
totalCount: 0,
|
|
285
|
-
loading: questionsLoading,
|
|
286
|
-
error: questionsError
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
const { localStartIndex, localEndIndex } = calculateItemsRange(
|
|
290
|
-
questionsPage,
|
|
291
|
-
requiredServerPage,
|
|
292
|
-
SERVER_ITEMS_PER_PAGE
|
|
293
|
-
);
|
|
294
|
-
const currentPageData = questionsData.questions.slice(localStartIndex, localEndIndex);
|
|
295
|
-
const totalServerItems = questionsData.totalCount || 0;
|
|
296
|
-
const totalPages = Math.ceil(totalServerItems / CLIENT_ITEMS_PER_PAGE);
|
|
297
|
-
return {
|
|
298
|
-
currentPageData,
|
|
299
|
-
totalPages,
|
|
300
|
-
totalCount: totalServerItems,
|
|
301
|
-
loading: questionsLoading,
|
|
302
|
-
error: questionsError
|
|
303
|
-
};
|
|
304
|
-
}, [
|
|
305
|
-
isSearchMode,
|
|
306
|
-
getSearchDisplayData,
|
|
307
|
-
searchTerm,
|
|
308
|
-
currentPage,
|
|
309
|
-
questionsPage,
|
|
310
|
-
questionsData?.questions,
|
|
311
|
-
questionsData?.totalCount,
|
|
312
|
-
requiredServerPage,
|
|
313
|
-
questionsLoading,
|
|
314
|
-
questionsError
|
|
315
|
-
]);
|
|
316
|
-
const handlePageChange = (newPage) => {
|
|
317
|
-
if (isSearchMode) {
|
|
318
|
-
setCurrentPage(newPage);
|
|
319
|
-
if (searchTerm.trim()) {
|
|
320
|
-
enhancedSearchRef.current(searchTerm, newPage);
|
|
321
|
-
}
|
|
322
|
-
} else {
|
|
323
|
-
setQuestionsPage(newPage);
|
|
324
|
-
setCurrentPage(newPage);
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
const toggleFilter = (filterId) => {
|
|
328
|
-
setActiveFilter(filterId);
|
|
329
|
-
};
|
|
330
|
-
const displayPageNumber = isSearchMode ? currentPage : questionsPage;
|
|
331
|
-
const PaginationControls = () => {
|
|
332
|
-
if (displayInfo.totalPages <= 1 && displayInfo.totalCount === 0 && !displayInfo.loading) {
|
|
333
|
-
return null;
|
|
334
|
-
}
|
|
335
|
-
return /* @__PURE__ */ React.createElement("div", { className: classes.pagination }, /* @__PURE__ */ React.createElement(
|
|
336
|
-
Button,
|
|
337
|
-
{
|
|
338
|
-
disabled: displayPageNumber <= 1,
|
|
339
|
-
onClick: () => handlePageChange(displayPageNumber - 1),
|
|
340
|
-
variant: "outlined"
|
|
341
|
-
},
|
|
342
|
-
"Previous"
|
|
343
|
-
), /* @__PURE__ */ React.createElement(Typography, { variant: "body1", style: { margin: "0 16px" } }, "Page ", displayPageNumber, " of ", displayInfo.totalPages || 1), /* @__PURE__ */ React.createElement(
|
|
344
|
-
Button,
|
|
345
|
-
{
|
|
346
|
-
disabled: displayPageNumber >= (displayInfo.totalPages || 1),
|
|
347
|
-
onClick: () => handlePageChange(displayPageNumber + 1),
|
|
348
|
-
variant: "outlined"
|
|
349
|
-
},
|
|
350
|
-
"Next"
|
|
351
|
-
));
|
|
352
|
-
};
|
|
353
|
-
if (displayInfo.error) return /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error: displayInfo.error });
|
|
354
|
-
return /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Box, { mb: 3 }, /* @__PURE__ */ React.createElement(
|
|
355
|
-
TextField,
|
|
356
|
-
{
|
|
357
|
-
fullWidth: true,
|
|
358
|
-
variant: "outlined",
|
|
359
|
-
placeholder: "Search questions...",
|
|
360
|
-
value: searchTerm,
|
|
361
|
-
onChange: (e) => setSearchTerm(e.target.value),
|
|
362
|
-
onKeyDown: handleKeyPress,
|
|
363
|
-
InputProps: {
|
|
364
|
-
startAdornment: /* @__PURE__ */ React.createElement(InputAdornment, { position: "start" }, /* @__PURE__ */ React.createElement(SearchIcon, null)),
|
|
365
|
-
className: classes.searchField
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
)), /* @__PURE__ */ React.createElement(PaginationControls, null), !isSearchMode && /* @__PURE__ */ React.createElement(Paper, { className: classes.filters }, /* @__PURE__ */ React.createElement(ButtonGroup, { className: classes.buttonGroup }, FILTERS.map(({ id, label }) => /* @__PURE__ */ React.createElement(
|
|
369
|
-
Button,
|
|
370
|
-
{
|
|
371
|
-
key: id,
|
|
372
|
-
variant: activeFilter === id ? "contained" : "outlined",
|
|
373
|
-
color: "primary",
|
|
374
|
-
onClick: () => toggleFilter(id)
|
|
375
|
-
},
|
|
376
|
-
label
|
|
377
|
-
)))), /* @__PURE__ */ React.createElement(Typography, { className: classes.resultCount }, isSearchMode ? `Search results: ${displayInfo.totalCount} total found` : `Showing ${displayInfo.currentPageData.length} of ${displayInfo.totalCount} results`), displayInfo.loading && /* @__PURE__ */ React.createElement(LoadingSkeleton, null), !displayInfo.loading && /* @__PURE__ */ React.createElement(React.Fragment, null, displayInfo.currentPageData && displayInfo.totalCount === 0 && /* @__PURE__ */ React.createElement(Box, { textAlign: "center", py: 4 }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", gutterBottom: true }, searchTerm.trim() ? `No questions found matching "${searchTerm}"` : "No questions found")), displayInfo.currentPageData.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, displayInfo.currentPageData.map((question) => /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, key: question.id }, /* @__PURE__ */ React.createElement(
|
|
378
|
-
StackOverflowSearchResultListItem,
|
|
379
|
-
{
|
|
380
|
-
result: {
|
|
381
|
-
location: question.webUrl,
|
|
382
|
-
title: question.title,
|
|
383
|
-
text: question.owner?.name,
|
|
384
|
-
answers: question.answerCount,
|
|
385
|
-
tags: question.tags,
|
|
386
|
-
created: question.creationDate,
|
|
387
|
-
author: question.owner?.name,
|
|
388
|
-
score: question.score,
|
|
389
|
-
isAnswered: question.isAnswered,
|
|
390
|
-
creationDate: question.creationDate,
|
|
391
|
-
userRole: question.owner?.role,
|
|
392
|
-
userProfile: question.owner?.webUrl,
|
|
393
|
-
avatar: question.owner?.avatarUrl,
|
|
394
|
-
userReputation: question.owner?.reputation
|
|
395
|
-
},
|
|
396
|
-
icon: /* @__PURE__ */ React.createElement(StackOverflowIcon, null)
|
|
397
|
-
}
|
|
398
|
-
)))))), /* @__PURE__ */ React.createElement(PaginationControls, null));
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
export { StackOverflowQuestions };
|
|
402
|
-
//# sourceMappingURL=StackOverflowPosts.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StackOverflowPosts.esm.js","sources":["../../../src/components/StackOverflow/StackOverflowPosts.tsx"],"sourcesContent":["import React, { useEffect, useState, useCallback, useMemo, useRef } from 'react';\nimport {\n makeStyles,\n Theme,\n Grid,\n Paper,\n Typography,\n Button,\n ButtonGroup,\n TextField,\n InputAdornment,\n Box,\n} from '@material-ui/core';\nimport Skeleton from '@mui/material/Skeleton';\nimport { ResponseErrorPanel } from '@backstage/core-components';\nimport { useStackOverflowSearch } from './hooks';\nimport { useStackOverflowData } from './hooks';\nimport { StackOverflowSearchResultListItem } from './StackOverflowSearchResultListItem';\nimport SearchIcon from '@material-ui/icons/Search';\nimport { StackOverflowIcon } from '../../icons';\n\nconst useStyles = makeStyles((theme: Theme) => ({\n filters: {\n padding: theme.spacing(2),\n marginTop: theme.spacing(2),\n },\n buttonGroup: {\n flexWrap: 'wrap',\n },\n resultCount: {\n marginTop: theme.spacing(1),\n fontSize: '0.875rem',\n color: theme.palette.text.secondary,\n },\n searchField: {\n borderRadius: 40,\n paddingLeft: 16,\n paddingRight: 16,\n },\n pagination: {\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n },\n loadingContainer: {\n minHeight: '600px', // Fixed height for consistency\n },\n loadingSkeletonItem: {\n padding: theme.spacing(2),\n marginBottom: theme.spacing(2),\n borderRadius: theme.shape.borderRadius,\n },\n skeletonContent: {\n display: 'flex',\n flexDirection: 'column',\n gap: theme.spacing(1),\n },\n skeletonHeader: {\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(1),\n marginBottom: theme.spacing(1),\n },\n skeletonTags: {\n display: 'flex',\n gap: theme.spacing(1),\n marginTop: theme.spacing(1),\n },\n}));\n\ntype FilterType = {\n id: string;\n label: string;\n};\n\nconst FILTERS: FilterType[] = [\n { id: 'unanswered', label: 'Unanswered' },\n { id: 'newest', label: 'Newest' },\n { id: 'active', label: 'Active' },\n { id: 'score', label: 'Score' },\n];\n\nconst CLIENT_ITEMS_PER_PAGE = 5;\nconst SERVER_ITEMS_PER_PAGE = 30;\nconst SEARCH_ITEMS_PER_PAGE = 30;\n\n// Custom debounce hook\nconst useDebounce = (value: string, delay: number) => {\n const [debouncedValue, setDebouncedValue] = useState(value);\n\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n clearTimeout(handler);\n };\n }, [value, delay]);\n\n return debouncedValue;\n};\n\n// Loading skeleton component for consistent UI height\n \nconst LoadingSkeleton = () => {\n const classes = useStyles();\n \n // Generate stable, unique keys for skeleton items\n const skeletonKeys = useMemo(() => \n Array.from({ length: CLIENT_ITEMS_PER_PAGE }, (_, index) => \n `skeleton-${index}-${Math.random().toString(36).substr(2, 9)}`\n ), []\n );\n \n return (\n <div className={classes.loadingContainer}>\n {skeletonKeys.map(key => (\n <Paper key={key} className={classes.loadingSkeletonItem} elevation={1}>\n <div className={classes.skeletonContent}>\n <div className={classes.skeletonHeader}>\n <Skeleton variant=\"circular\" width={32} height={32} />\n <div style={{ flex: 1 }}>\n <Skeleton variant=\"text\" width=\"60%\" height={20} />\n <Skeleton variant=\"text\" width=\"40%\" height={16} />\n </div>\n <Skeleton variant=\"rectangular\" width={60} height={24} />\n </div>\n \n <Skeleton variant=\"text\" width=\"90%\" height={24} />\n <Skeleton variant=\"text\" width=\"75%\" height={20} />\n <Skeleton variant=\"text\" width=\"60%\" height={20} />\n \n <div className={classes.skeletonTags}>\n <Skeleton variant=\"rectangular\" width={60} height={20} />\n <Skeleton variant=\"rectangular\" width={80} height={20} />\n <Skeleton variant=\"rectangular\" width={45} height={20} />\n </div>\n \n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 8 }}>\n <Skeleton variant=\"text\" width={120} height={16} />\n <Skeleton variant=\"text\" width={80} height={16} />\n </div>\n </div>\n </Paper>\n ))}\n </div>\n );\n};\n\n// Universal pagination utility functions\nconst calculateServerPage = (clientPage: number, serverPageSize: number = SERVER_ITEMS_PER_PAGE): number => {\n return Math.ceil((clientPage * CLIENT_ITEMS_PER_PAGE) / serverPageSize);\n};\n\nconst calculateItemsRange = (clientPage: number, serverPage: number, serverPageSize: number = SERVER_ITEMS_PER_PAGE) => {\n const globalStartIndex = (clientPage - 1) * CLIENT_ITEMS_PER_PAGE;\n const serverStartIndex = (serverPage - 1) * serverPageSize;\n const localStartIndex = globalStartIndex - serverStartIndex;\n const localEndIndex = localStartIndex + CLIENT_ITEMS_PER_PAGE;\n \n return { localStartIndex, localEndIndex };\n};\n\n// Enhanced search data management\nconst useEnhancedSearch = () => {\n const searchHook = useStackOverflowSearch();\n const [searchCache, setSearchCache] = useState<{[key: string]: any}>({});\n const lastSearchRef = useRef<{term: string, page: number} | null>(null);\n\n // Get the actual page size from the search response, fallback to our constant\n const getActualSearchPageSize = useCallback(() => {\n return searchHook.searchData?.pageSize || SEARCH_ITEMS_PER_PAGE;\n }, [searchHook.searchData?.pageSize]);\n\n const enhancedSearch = useCallback((term: string, clientPage: number) => {\n const actualPageSize = getActualSearchPageSize();\n const serverPage = calculateServerPage(clientPage, actualPageSize);\n const cacheKey = `${term}-${serverPage}`;\n \n // Prevent duplicate requests\n const currentSearch = { term, page: serverPage };\n if (lastSearchRef.current && \n lastSearchRef.current.term === currentSearch.term && \n lastSearchRef.current.page === currentSearch.page) {\n return;\n }\n \n if (searchCache[cacheKey]) {\n return;\n }\n \n // Store the search reference to prevent duplicates\n lastSearchRef.current = currentSearch;\n \n // Perform the search for the required server page\n searchHook.search(term, serverPage);\n }, [getActualSearchPageSize, searchCache, searchHook]);\n\n // Cache search results when they arrive\n useEffect(() => {\n if (searchHook.searchData && lastSearchRef.current) {\n const serverPage = searchHook.searchData.page;\n const searchTerm = lastSearchRef.current.term;\n const cacheKey = `${searchTerm}-${serverPage}`;\n \n setSearchCache(prev => ({\n ...prev,\n [cacheKey]: {\n items: searchHook?.searchData?.items,\n totalCount: searchHook?.searchData?.totalCount,\n pageSize: searchHook?.searchData?.pageSize,\n timestamp: Date.now()\n }\n }));\n }\n }, [searchHook.searchData]);\n\n const getSearchDisplayData = useCallback((term: string, clientPage: number) => {\n const actualPageSize = getActualSearchPageSize();\n const serverPage = calculateServerPage(clientPage, actualPageSize);\n const cacheKey = `${term}-${serverPage}`;\n const cachedData = searchCache[cacheKey];\n \n if (!cachedData) {\n return {\n currentPageData: [],\n totalCount: 0,\n loading: searchHook.loading,\n error: searchHook.error\n };\n }\n \n const { localStartIndex, localEndIndex } = calculateItemsRange(\n clientPage, \n serverPage, \n cachedData.pageSize\n );\n \n const currentPageData = cachedData.items.slice(localStartIndex, localEndIndex);\n \n return {\n currentPageData,\n totalCount: cachedData.totalCount,\n loading: false,\n error: null\n };\n }, [searchCache, searchHook.loading, searchHook.error, getActualSearchPageSize]);\n\n const clearSearchCache = useCallback(() => {\n setSearchCache({});\n lastSearchRef.current = null;\n }, []);\n\n return {\n enhancedSearch,\n getSearchDisplayData,\n clearSearch: searchHook.clearSearch,\n clearSearchCache,\n loading: searchHook.loading,\n error: searchHook.error\n };\n};\n\nexport const StackOverflowQuestions = () => {\n const classes = useStyles();\n \n const { \n data: questionsData, \n loading: questionsLoading, \n error: questionsError,\n fetchActiveQuestions,\n fetchNewestQuestions,\n fetchTopScoredQuestions,\n fetchUnansweredQuestions\n } = useStackOverflowData('questions');\n \n const { \n enhancedSearch,\n getSearchDisplayData,\n clearSearch,\n clearSearchCache,\n } = useEnhancedSearch();\n \n const [searchTerm, setSearchTerm] = useState('');\n const [activeFilter, setActiveFilter] = useState<string>('active');\n const [currentPage, setCurrentPage] = useState(1);\n const [questionsPage, setQuestionsPage] = useState(1);\n const prevSearchModeRef = useRef(false);\n\n // Store stable references to search functions\n const enhancedSearchRef = useRef(enhancedSearch);\n const clearSearchRef = useRef(clearSearch);\n const clearSearchCacheRef = useRef(clearSearchCache);\n\n // Update refs when functions change\n useEffect(() => {\n enhancedSearchRef.current = enhancedSearch;\n clearSearchRef.current = clearSearch;\n clearSearchCacheRef.current = clearSearchCache;\n });\n\n const debouncedSearchTerm = useDebounce(searchTerm, 500);\n\n const isSearchMode = !!searchTerm.trim();\n\n // Calculate required server page for current client page (only for questions)\n const requiredServerPage = useMemo(() => {\n return calculateServerPage(questionsPage, SERVER_ITEMS_PER_PAGE);\n }, [questionsPage]);\n\n // Load questions when filter or required server page changes (only in non-search mode)\n useEffect(() => {\n if (!isSearchMode) {\n switch (activeFilter) {\n case 'active':\n fetchActiveQuestions(requiredServerPage);\n break;\n case 'newest':\n fetchNewestQuestions(requiredServerPage);\n break;\n case 'score':\n fetchTopScoredQuestions(requiredServerPage);\n break;\n case 'unanswered':\n fetchUnansweredQuestions(requiredServerPage);\n break;\n default:\n fetchActiveQuestions(requiredServerPage);\n break;\n }\n }\n }, [activeFilter, requiredServerPage, isSearchMode, fetchActiveQuestions, fetchNewestQuestions, fetchTopScoredQuestions, fetchUnansweredQuestions]);\n\n // Handle search mode transitions\n useEffect(() => {\n const wasInSearchMode = prevSearchModeRef.current;\n \n if (isSearchMode && !wasInSearchMode) {\n // Entering search mode - reset search page to 1\n setCurrentPage(1);\n } else if (!isSearchMode && wasInSearchMode) {\n // Exiting search mode - reset questions page to 1 and current page to 1\n setQuestionsPage(1);\n setCurrentPage(1);\n }\n \n prevSearchModeRef.current = isSearchMode;\n }, [isSearchMode]);\n\n // Reset pagination when filter changes (only in non-search mode)\n useEffect(() => {\n if (!isSearchMode) {\n setQuestionsPage(1);\n setCurrentPage(1);\n }\n }, [activeFilter, isSearchMode]);\n\n // Handle search when debounced term changes\n useEffect(() => {\n if (debouncedSearchTerm.trim()) {\n // Only search if we're in search mode and current page is set\n if (isSearchMode) {\n enhancedSearchRef.current(debouncedSearchTerm, currentPage);\n }\n } else {\n clearSearchRef.current();\n clearSearchCacheRef.current();\n }\n }, [debouncedSearchTerm, currentPage, isSearchMode]);\n\n // Handle Enter key press for immediate search\n const handleKeyPress = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && searchTerm.trim()) {\n enhancedSearchRef.current(searchTerm, currentPage);\n }\n };\n\n // Calculate display data and pagination info\n const displayInfo = useMemo(() => {\n if (isSearchMode) {\n const searchDisplayData = getSearchDisplayData(searchTerm, currentPage);\n const totalPages = Math.ceil(searchDisplayData.totalCount / CLIENT_ITEMS_PER_PAGE);\n \n return {\n currentPageData: searchDisplayData.currentPageData,\n totalPages,\n totalCount: searchDisplayData.totalCount,\n loading: searchDisplayData.loading,\n error: searchDisplayData.error\n };\n }\n\n // For regular questions, slice the server data\n if (!questionsData?.questions) {\n return {\n currentPageData: [],\n totalPages: 1,\n totalCount: 0,\n loading: questionsLoading,\n error: questionsError\n };\n }\n\n const { localStartIndex, localEndIndex } = calculateItemsRange(\n questionsPage, \n requiredServerPage, \n SERVER_ITEMS_PER_PAGE\n );\n const currentPageData = questionsData.questions.slice(localStartIndex, localEndIndex);\n \n // Calculate total pages based on total items from server\n const totalServerItems = questionsData.totalCount || 0;\n const totalPages = Math.ceil(totalServerItems / CLIENT_ITEMS_PER_PAGE);\n\n return {\n currentPageData,\n totalPages,\n totalCount: totalServerItems,\n loading: questionsLoading,\n error: questionsError\n };\n }, [\n isSearchMode, \n getSearchDisplayData,\n searchTerm,\n currentPage,\n questionsPage,\n questionsData?.questions,\n questionsData?.totalCount,\n requiredServerPage,\n questionsLoading,\n questionsError\n ]);\n\n // Unified pagination handler\n const handlePageChange = (newPage: number) => {\n if (isSearchMode) {\n setCurrentPage(newPage);\n if (searchTerm.trim()) {\n enhancedSearchRef.current(searchTerm, newPage);\n }\n } else {\n setQuestionsPage(newPage);\n setCurrentPage(newPage);\n }\n };\n\n const toggleFilter = (filterId: string) => {\n setActiveFilter(filterId);\n };\n\n // Get the correct page number to display\n const displayPageNumber = isSearchMode ? currentPage : questionsPage;\n\n // Pagination component to reuse\n const PaginationControls = () => {\n // Only show pagination if we have data or are in a valid state\n if (displayInfo.totalPages <= 1 && displayInfo.totalCount === 0 && !displayInfo.loading) {\n return null;\n }\n\n return (\n <div className={classes.pagination}>\n <Button\n disabled={displayPageNumber <= 1}\n onClick={() => handlePageChange(displayPageNumber - 1)}\n variant=\"outlined\"\n >\n Previous\n </Button>\n <Typography variant=\"body1\" style={{ margin: '0 16px' }}>\n Page {displayPageNumber} of {displayInfo.totalPages || 1}\n </Typography>\n <Button\n disabled={displayPageNumber >= (displayInfo.totalPages || 1)}\n onClick={() => handlePageChange(displayPageNumber + 1)}\n variant=\"outlined\"\n >\n Next\n </Button>\n </div>\n );\n };\n\n // Show error state\n if (displayInfo.error) return <ResponseErrorPanel error={displayInfo.error} />;\n\n return (\n <div>\n <Box mb={3}>\n <TextField\n fullWidth\n variant=\"outlined\"\n placeholder=\"Search questions...\"\n value={searchTerm}\n onChange={e => setSearchTerm(e.target.value)}\n onKeyDown={handleKeyPress}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <SearchIcon />\n </InputAdornment>\n ),\n className: classes.searchField,\n }}\n />\n </Box>\n\n <PaginationControls />\n\n {/* Only show filters when not in search mode */}\n {!isSearchMode && (\n <Paper className={classes.filters}>\n <ButtonGroup className={classes.buttonGroup}>\n {FILTERS.map(({ id, label }) => (\n <Button\n key={id}\n variant={activeFilter === id ? 'contained' : 'outlined'}\n color=\"primary\"\n onClick={() => toggleFilter(id)}\n >\n {label}\n </Button>\n ))}\n </ButtonGroup>\n </Paper>\n )}\n\n <Typography className={classes.resultCount}>\n {isSearchMode \n ? `Search results: ${displayInfo.totalCount} total found` \n : `Showing ${displayInfo.currentPageData.length} of ${displayInfo.totalCount} results`}\n </Typography>\n\n {displayInfo.loading && <LoadingSkeleton />}\n\n {!displayInfo.loading && (\n <>\n {/* No results */}\n {displayInfo.currentPageData && displayInfo.totalCount === 0 && (\n <Box textAlign=\"center\" py={4}>\n <Typography variant=\"body1\" gutterBottom>\n {searchTerm.trim()\n ? `No questions found matching \"${searchTerm}\"`\n : \"No questions found\"\n }\n </Typography>\n </Box>\n )}\n\n {/* Results */}\n {displayInfo.currentPageData.length > 0 && (\n <>\n <Grid container spacing={2}>\n {displayInfo.currentPageData.map((question: any) => (\n <Grid item xs={12} key={question.id}>\n <StackOverflowSearchResultListItem\n result={{\n location: question.webUrl,\n title: question.title,\n text: question.owner?.name,\n answers: question.answerCount,\n tags: question.tags,\n created: question.creationDate,\n author: question.owner?.name,\n score: question.score,\n isAnswered: question.isAnswered,\n creationDate: question.creationDate,\n userRole: question.owner?.role,\n userProfile: question.owner?.webUrl,\n avatar: question.owner?.avatarUrl,\n userReputation: question.owner?.reputation,\n }}\n icon={<StackOverflowIcon />}\n />\n </Grid>\n ))}\n </Grid>\n </>\n )}\n </>\n )}\n\n <PaginationControls />\n </div>\n );\n};"],"names":["totalPages"],"mappings":"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"}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Link } from '@backstage/core-components';
|
|
3
|
-
import Divider from '@material-ui/core/Divider';
|
|
4
|
-
import ListItem from '@material-ui/core/ListItem';
|
|
5
|
-
import ListItemText from '@material-ui/core/ListItemText';
|
|
6
|
-
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
|
7
|
-
import Box from '@material-ui/core/Box';
|
|
8
|
-
import Chip from '@material-ui/core/Chip';
|
|
9
|
-
import { useAnalytics } from '@backstage/core-plugin-api';
|
|
10
|
-
import { HighlightedSearchResultText } from '@backstage/plugin-search-react';
|
|
11
|
-
import { decodeHtml } from '../../utils/decodeHtml.esm.js';
|
|
12
|
-
import { getTimeAgo } from '../../utils/getTimeAgo.esm.js';
|
|
13
|
-
import { Typography, Avatar } from '@material-ui/core';
|
|
14
|
-
|
|
15
|
-
const StackOverflowSearchResultListItem = (props) => {
|
|
16
|
-
const { result, highlight } = props;
|
|
17
|
-
const analytics = useAnalytics();
|
|
18
|
-
const handleClick = () => {
|
|
19
|
-
analytics.captureEvent("discover", result.title, {
|
|
20
|
-
attributes: { to: result.location },
|
|
21
|
-
value: props.rank
|
|
22
|
-
});
|
|
23
|
-
};
|
|
24
|
-
if (!result) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
const timeAgo = getTimeAgo(result.creationDate);
|
|
28
|
-
const userRole = result.userRole;
|
|
29
|
-
const isModerator = userRole === "Moderator";
|
|
30
|
-
const isAdmin = userRole === "Admin";
|
|
31
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ListItem, { alignItems: "center" }, props.icon && /* @__PURE__ */ React.createElement(ListItemIcon, null, props.icon), /* @__PURE__ */ React.createElement(
|
|
32
|
-
Box,
|
|
33
|
-
{
|
|
34
|
-
display: "flex",
|
|
35
|
-
flexDirection: "column",
|
|
36
|
-
alignItems: "center",
|
|
37
|
-
mr: 2,
|
|
38
|
-
minWidth: "80px"
|
|
39
|
-
},
|
|
40
|
-
/* @__PURE__ */ React.createElement(Typography, { variant: "subtitle2", color: "textSecondary" }, result.score, " votes"),
|
|
41
|
-
/* @__PURE__ */ React.createElement(
|
|
42
|
-
Typography,
|
|
43
|
-
{
|
|
44
|
-
variant: "subtitle2",
|
|
45
|
-
color: result.answers > 0 ? "primary" : "textSecondary",
|
|
46
|
-
style: {
|
|
47
|
-
fontWeight: result.isAnswered ? "bold" : "normal"
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
result.answers,
|
|
51
|
-
" answers"
|
|
52
|
-
),
|
|
53
|
-
result.isAnswered && /* @__PURE__ */ React.createElement(
|
|
54
|
-
Typography,
|
|
55
|
-
{
|
|
56
|
-
variant: "subtitle2",
|
|
57
|
-
color: "primary",
|
|
58
|
-
style: { fontWeight: "bold" }
|
|
59
|
-
},
|
|
60
|
-
"\u2714 Accepted"
|
|
61
|
-
)
|
|
62
|
-
), /* @__PURE__ */ React.createElement(Box, { flexWrap: "wrap", flexGrow: 1 }, /* @__PURE__ */ React.createElement(
|
|
63
|
-
ListItemText,
|
|
64
|
-
{
|
|
65
|
-
primaryTypographyProps: { variant: "h6" },
|
|
66
|
-
primary: (
|
|
67
|
-
// Currently, the backend send the referrer to fill out the questions index, however, this component is shared for both the questions that come from the index and also the question that are actively with a live API request, my solution for now is to add the referrer when it doesn't exist already until this is changed in the future.
|
|
68
|
-
/* @__PURE__ */ React.createElement(
|
|
69
|
-
Link,
|
|
70
|
-
{
|
|
71
|
-
to: result.location.includes("?r=") ? result.location : `${result.location}?r=Backstage_Plugin`,
|
|
72
|
-
noTrack: true,
|
|
73
|
-
onClick: handleClick
|
|
74
|
-
},
|
|
75
|
-
highlight?.fields?.title ? /* @__PURE__ */ React.createElement(
|
|
76
|
-
HighlightedSearchResultText,
|
|
77
|
-
{
|
|
78
|
-
text: decodeHtml(highlight.fields.title),
|
|
79
|
-
preTag: highlight.preTag,
|
|
80
|
-
postTag: highlight.postTag
|
|
81
|
-
}
|
|
82
|
-
) : decodeHtml(result.title)
|
|
83
|
-
)
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
), /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center", mt: 1 }, result.avatar && /* @__PURE__ */ React.createElement(Link, { to: result.userProfile, noTrack: true }, /* @__PURE__ */ React.createElement(
|
|
87
|
-
Avatar,
|
|
88
|
-
{
|
|
89
|
-
src: result.avatar,
|
|
90
|
-
alt: result.text,
|
|
91
|
-
style: { width: 20, height: 20, marginRight: 8 }
|
|
92
|
-
}
|
|
93
|
-
)), /* @__PURE__ */ React.createElement(Link, { key: result.text, to: result.userProfile, noTrack: true }, /* @__PURE__ */ React.createElement(
|
|
94
|
-
Typography,
|
|
95
|
-
{
|
|
96
|
-
variant: "body2",
|
|
97
|
-
color: "textPrimary",
|
|
98
|
-
component: "span"
|
|
99
|
-
},
|
|
100
|
-
decodeHtml(result.text)
|
|
101
|
-
)), result.userReputation && /* @__PURE__ */ React.createElement(
|
|
102
|
-
Typography,
|
|
103
|
-
{
|
|
104
|
-
variant: "body2",
|
|
105
|
-
color: "textSecondary",
|
|
106
|
-
component: "span",
|
|
107
|
-
style: { marginLeft: 4 }
|
|
108
|
-
},
|
|
109
|
-
"(",
|
|
110
|
-
result.userReputation,
|
|
111
|
-
")"
|
|
112
|
-
), (isModerator || isAdmin) && /* @__PURE__ */ React.createElement(
|
|
113
|
-
Chip,
|
|
114
|
-
{
|
|
115
|
-
label: isModerator ? "Moderator" : "Admin",
|
|
116
|
-
size: "small",
|
|
117
|
-
style: {
|
|
118
|
-
marginTop: 6,
|
|
119
|
-
marginLeft: 8,
|
|
120
|
-
backgroundColor: isModerator ? "lightblue" : "lightcoral",
|
|
121
|
-
color: isModerator ? "black" : "#fff",
|
|
122
|
-
fontWeight: "bold"
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
), /* @__PURE__ */ React.createElement(
|
|
126
|
-
Typography,
|
|
127
|
-
{
|
|
128
|
-
variant: "body2",
|
|
129
|
-
color: "textSecondary",
|
|
130
|
-
component: "span",
|
|
131
|
-
style: { marginLeft: 4 }
|
|
132
|
-
},
|
|
133
|
-
"asked ",
|
|
134
|
-
timeAgo
|
|
135
|
-
)), result.tags && /* @__PURE__ */ React.createElement(Box, { display: "flex", flexWrap: "wrap", mt: 1 }, result.tags.map((tag) => /* @__PURE__ */ React.createElement(Link, { key: tag.name, to: tag.location, noTrack: true }, /* @__PURE__ */ React.createElement(
|
|
136
|
-
Chip,
|
|
137
|
-
{
|
|
138
|
-
label: tag.name,
|
|
139
|
-
size: "small",
|
|
140
|
-
clickable: true,
|
|
141
|
-
style: { marginRight: 4, marginBottom: 4 }
|
|
142
|
-
}
|
|
143
|
-
)))))), /* @__PURE__ */ React.createElement(Divider, null));
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
export { StackOverflowSearchResultListItem };
|
|
147
|
-
//# sourceMappingURL=StackOverflowSearchResultListItem.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StackOverflowSearchResultListItem.esm.js","sources":["../../../src/components/StackOverflow/StackOverflowSearchResultListItem.tsx"],"sourcesContent":["/* \n * This specific component is a modified version of https://github.com/backstage/community-plugins/blob/main/workspaces/stack-overflow/plugins/stack-overflow/src/search/StackOverflowSearchResultListItem/StackOverflowSearchResultListItem.tsx\n *\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport React from 'react';\nimport { Link } from '@backstage/core-components';\nimport Divider from '@material-ui/core/Divider';\nimport ListItem from '@material-ui/core/ListItem';\nimport ListItemText from '@material-ui/core/ListItemText';\nimport ListItemIcon from '@material-ui/core/ListItemIcon';\nimport Box from '@material-ui/core/Box';\nimport Chip from '@material-ui/core/Chip';\nimport { useAnalytics } from '@backstage/core-plugin-api';\nimport type { ResultHighlight } from '@backstage/plugin-search-common';\nimport { HighlightedSearchResultText } from '@backstage/plugin-search-react';\nimport { decodeHtml, getTimeAgo } from '../../utils';\nimport { Avatar, Typography } from '@material-ui/core';\n/**\n * Props for {@link StackOverflowSearchResultListItem}\n *\n * @public\n */\nexport type StackOverflowSearchResultListItemProps = {\n result?: any; // TODO(emmaindal): type to StackOverflowDocument.\n icon?: React.ReactNode;\n rank?: number;\n highlight?: ResultHighlight;\n};\nexport const StackOverflowSearchResultListItem = (\n props: StackOverflowSearchResultListItemProps,\n) => {\n const { result, highlight } = props;\n const analytics = useAnalytics();\n const handleClick = () => {\n analytics.captureEvent('discover', result.title, {\n attributes: { to: result.location },\n value: props.rank,\n });\n };\n if (!result) {\n return null;\n }\n const timeAgo = getTimeAgo(result.creationDate);\n // Recording role of the user\n const userRole = result.userRole;\n const isModerator = userRole === 'Moderator';\n const isAdmin = userRole === 'Admin';\n return (\n <>\n <ListItem alignItems=\"center\">\n {props.icon && <ListItemIcon>{props.icon}</ListItemIcon>}\n <Box\n display=\"flex\"\n flexDirection=\"column\"\n alignItems=\"center\"\n mr={2}\n minWidth=\"80px\"\n >\n <Typography variant=\"subtitle2\" color=\"textSecondary\">\n {result.score} votes\n </Typography>\n <Typography\n variant=\"subtitle2\"\n color={result.answers > 0 ? 'primary' : 'textSecondary'}\n style={{\n fontWeight: result.isAnswered ? 'bold' : 'normal',\n }}\n >\n {result.answers} answers\n </Typography>\n {result.isAnswered && (\n <Typography\n variant=\"subtitle2\"\n color=\"primary\"\n style={{ fontWeight: 'bold' }}\n >\n ✔ Accepted\n </Typography>\n )}\n </Box>\n <Box flexWrap=\"wrap\" flexGrow={1}>\n <ListItemText\n primaryTypographyProps={{ variant: 'h6' }}\n primary={\n // Currently, the backend send the referrer to fill out the questions index, however, this component is shared for both the questions that come from the index and also the question that are actively with a live API request, my solution for now is to add the referrer when it doesn't exist already until this is changed in the future.\n <Link\n to={\n result.location.includes('?r=')\n ? result.location\n : `${result.location}?r=Backstage_Plugin`\n }\n noTrack\n onClick={handleClick}\n >\n {highlight?.fields?.title ? (\n <HighlightedSearchResultText\n text={decodeHtml(highlight.fields.title)}\n preTag={highlight.preTag}\n postTag={highlight.postTag}\n />\n ) : (\n decodeHtml(result.title)\n )}\n </Link>\n }\n />\n \n <Box display=\"flex\" alignItems=\"center\" mt={1}>\n {/* Author Avatar */}\n {result.avatar && (\n <Link to={result.userProfile} noTrack>\n <Avatar\n src={result.avatar}\n alt={result.text}\n style={{ width: 20, height: 20, marginRight: 8 }}\n />\n </Link>\n )}\n {/* Author Name and Reputation */}\n <Link key={result.text} to={result.userProfile} noTrack>\n <Typography\n variant=\"body2\"\n color=\"textPrimary\"\n component=\"span\"\n >\n {decodeHtml(result.text)}\n </Typography>\n </Link>\n {/* User Reputation */}\n {result.userReputation && (\n <Typography\n variant=\"body2\"\n color=\"textSecondary\"\n component=\"span\"\n style={{ marginLeft: 4 }}\n >\n ({result.userReputation})\n </Typography>\n )}\n {(isModerator || isAdmin) && (\n <Chip\n label={isModerator ? 'Moderator' : 'Admin'}\n size=\"small\"\n style={{\n marginTop: 6,\n marginLeft: 8,\n backgroundColor: isModerator ? 'lightblue' : 'lightcoral',\n color: isModerator ? 'black' : '#fff',\n fontWeight: 'bold',\n }}\n />\n )}\n {/* Time Ago */}\n <Typography\n variant=\"body2\"\n color=\"textSecondary\"\n component=\"span\"\n style={{ marginLeft: 4 }}\n >\n asked {timeAgo}\n </Typography>\n </Box>\n\n {/* Tags section */}\n {result.tags && (\n <Box display=\"flex\" flexWrap=\"wrap\" mt={1}>\n {result.tags.map((tag: { name: string; location: string }) => (\n <Link key={tag.name} to={tag.location} noTrack>\n <Chip\n label={tag.name}\n size=\"small\"\n clickable\n style={{ marginRight: 4, marginBottom: 4 }}\n />\n </Link>\n ))}\n </Box>\n )}\n </Box>\n </ListItem>\n <Divider />\n </>\n );\n};"],"names":[],"mappings":"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"}
|