Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(URL): Share link with search parameters #173

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ import {
Tooltip,
} from "@mui/joy";

import ShareIcon from "@mui/icons-material/Share";
import UnfoldLessIcon from "@mui/icons-material/UnfoldLess";
import UnfoldMoreIcon from "@mui/icons-material/UnfoldMore";

import {StateContext} from "../../../../../contexts/StateContextProvider";
import {
copyPermalinkToClipboard,
UrlContext,
} from "../../../../../contexts/UrlContextProvider";
import {
QUERY_PROGRESS_VALUE_MAX,
QueryArgs,
Expand Down Expand Up @@ -64,13 +69,21 @@ const getIsRegex =
*/
const SearchTabPanel = () => {
const {queryProgress, queryResults, startQuery, uiState} = useContext(StateContext);
const {queryString: urlQueryString} = useContext(UrlContext);
const [isAllExpanded, setIsAllExpanded] = useState<boolean>(true);
const [queryOptions, setQueryOptions] = useState<QUERY_OPTION[]>([]);
const [queryString, setQueryString] = useState<string>("");
const [queryString, setQueryString] = useState<string>(urlQueryString ?? "");

const handleCollapseAllButtonClick = () => {
setIsAllExpanded((v) => !v);
};
const handleShareButtonClick = () => {
copyPermalinkToClipboard({
queryString: queryString,
isCaseSensitive: getIsCaseSensitive(queryOptions),
isRegex: getIsRegex(queryOptions),
}, {});
};

const handleQuerySubmit = (newArgs: Partial<QueryArgs>) => {
startQuery({
Expand Down Expand Up @@ -104,16 +117,24 @@ const SearchTabPanel = () => {
tabName={TAB_NAME.SEARCH}
title={TAB_DISPLAY_NAMES[TAB_NAME.SEARCH]}
titleButtons={
<PanelTitleButton
title={isAllExpanded ?
"Collapse all" :
"Expand all"}
onClick={handleCollapseAllButtonClick}
>
{isAllExpanded ?
<UnfoldLessIcon/> :
<UnfoldMoreIcon/>}
</PanelTitleButton>
<>
<PanelTitleButton
title={isAllExpanded ?
"Collapse all" :
"Expand all"}
onClick={handleCollapseAllButtonClick}
>
{isAllExpanded ?
<UnfoldLessIcon/> :
<UnfoldMoreIcon/>}
</PanelTitleButton>
<PanelTitleButton
title={"Copy URL with search parameters"}
onClick={handleShareButtonClick}
>
<ShareIcon/>
</PanelTitleButton>
</>
}
>
<Box className={"search-tab-container"}>
Expand All @@ -123,6 +144,7 @@ const SearchTabPanel = () => {
maxRows={7}
placeholder={"Search"}
size={"sm"}
value={queryString}
endDecorator={
<ToggleButtonGroup
disabled={isQueryInputBoxDisabled}
Expand Down
19 changes: 17 additions & 2 deletions src/contexts/StateContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,14 +246,15 @@ const updateUrlIfEventOnPage = (
// eslint-disable-next-line max-lines-per-function, max-statements
const StateContextProvider = ({children}: StateContextProviderProps) => {
const {postPopUp} = useContext(NotificationContext);
const {filePath, logEventNum} = useContext(UrlContext);
const {filePath, isCaseSensitive, isRegex, logEventNum, queryString} = useContext(UrlContext);

// States
const [exportProgress, setExportProgress] =
useState<Nullable<number>>(STATE_DEFAULT.exportProgress);
const [fileName, setFileName] = useState<string>(STATE_DEFAULT.fileName);
const [isFileLoaded, setIsFileLoaded] = useState<boolean>(false);
const [isSettingsModalOpen, setIsSettingsModalOpen] =
useState<boolean>(STATE_DEFAULT.isSettingsModalOpen);
const [fileName, setFileName] = useState<string>(STATE_DEFAULT.fileName);
const [logData, setLogData] = useState<string>(STATE_DEFAULT.logData);
const [numEvents, setNumEvents] = useState<number>(STATE_DEFAULT.numEvents);
const [numPages, setNumPages] = useState<number>(STATE_DEFAULT.numPages);
Expand Down Expand Up @@ -305,6 +306,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => {
setFileName(args.fileName);
setNumEvents(args.numEvents);
setOnDiskFileSizeInBytes(args.onDiskFileSizeInBytes);
setIsFileLoaded(true);
break;
case WORKER_RESP_CODE.NOTIFICATION:
postPopUp({
Expand Down Expand Up @@ -419,6 +421,8 @@ const StateContextProvider = ({children}: StateContextProviderProps) => {
cursor: cursor,
decoderOptions: getConfig(CONFIG_KEY.DECODER_OPTIONS),
});
setQueryResults(STATE_DEFAULT.queryResults);
setQueryProgress(QUERY_PROGRESS_VALUE_MIN);
}, [
handleMainWorkerResp,
]);
Expand Down Expand Up @@ -452,6 +456,17 @@ const StateContextProvider = ({children}: StateContextProviderProps) => {
});
}, []);

useEffect(() => {
if (URL_SEARCH_PARAMS_DEFAULT.queryString !== queryString && URL_SEARCH_PARAMS_DEFAULT.isCaseSensitive !== isCaseSensitive && URL_SEARCH_PARAMS_DEFAULT.isRegex !== isRegex) {
startQuery({queryString, isCaseSensitive, isRegex});
}
updateWindowUrlSearchParams({
[SEARCH_PARAM_NAMES.QUERY_STRING]: URL_SEARCH_PARAMS_DEFAULT.queryString,
[SEARCH_PARAM_NAMES.IS_CASE_SENSITIVE]: URL_SEARCH_PARAMS_DEFAULT.isCaseSensitive,
[SEARCH_PARAM_NAMES.IS_REGEX]: URL_SEARCH_PARAMS_DEFAULT.isRegex,
});
}, [isFileLoaded]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel some of these handlings shall be done in the UrlContextProvider instead:

  1. Instead of expecting queryString, isCaseSensitive and isRegex from the URL search parameters, can we expect queryParams.queryString, queryParams.isCaseSensitive and queryParams.isRegex instead?
  2. Shall we assume both queryParams.isCaseSensitive and queryParams.isRegex are false when they are absent in the URL? i.e., shall we consider making those parameters optional?
  3. I think the onus to clear the query parameters shall be on the UrlContextProvider rather than the StateContextProvider.
  4. Then instead of exposing state variables queryString, isCaseSensitive and isRegex directly from the UrlContextProvider, can we expose a single object queryParams? That way in the StateContextProvider code, we can simply check if queryParams is null.


// Synchronize `logEventNumRef` with `logEventNum`.
useEffect(() => {
logEventNumRef.current = logEventNum;
Expand Down
7 changes: 7 additions & 0 deletions src/contexts/UrlContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const UrlContext = createContext <UrlParamsType>({} as UrlParamsType);
*/
const URL_SEARCH_PARAMS_DEFAULT = Object.freeze({
[SEARCH_PARAM_NAMES.FILE_PATH]: null,
[SEARCH_PARAM_NAMES.IS_CASE_SENSITIVE]: null,
[SEARCH_PARAM_NAMES.IS_REGEX]: null,
[SEARCH_PARAM_NAMES.QUERY_STRING]: null,
});

/**
Expand Down Expand Up @@ -181,6 +184,10 @@ const getWindowUrlSearchParams = () => {
);
const urlSearchParams = new URLSearchParams(window.location.search.substring(1));

urlSearchParams.forEach((value, key) => {
searchParams[key as keyof UrlSearchParams] = value;
});

if (urlSearchParams.has(SEARCH_PARAM_NAMES.FILE_PATH)) {
// Split the search string and take everything after as `filePath` value.
// This ensures any parameters following `filePath=` are incorporated into the `filePath`.
Expand Down
6 changes: 6 additions & 0 deletions src/typings/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import {Nullable} from "./common";

enum SEARCH_PARAM_NAMES {
FILE_PATH = "filePath",
IS_CASE_SENSITIVE = "isCaseSensitive",
IS_REGEX = "isRegex",
QUERY_STRING = "queryString",
}

enum HASH_PARAM_NAMES {
Expand All @@ -11,6 +14,9 @@ enum HASH_PARAM_NAMES {

interface UrlSearchParams {
[SEARCH_PARAM_NAMES.FILE_PATH]: string,
[SEARCH_PARAM_NAMES.IS_CASE_SENSITIVE]: boolean,
[SEARCH_PARAM_NAMES.IS_REGEX]: boolean,
[SEARCH_PARAM_NAMES.QUERY_STRING]: string,
}

interface UrlHashParams {
Expand Down
Loading