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

ref(issue-views): Replace navigate/location with useSearchParams in issue views components #82875

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all 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
35 changes: 12 additions & 23 deletions static/app/views/issueList/issueViews/issueViewTab.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import {useContext} from 'react';
import {useSearchParams} from 'react-router-dom';
import styled from '@emotion/styled';
import {motion} from 'framer-motion';

import {TEMPORARY_TAB_KEY} from 'sentry/components/draggableTabs/draggableTabList';
import type {MenuItemProps} from 'sentry/components/dropdownMenu';
import {t} from 'sentry/locale';
import type {InjectedRouter} from 'sentry/types/legacyReactRouter';
import {useNavigate} from 'sentry/utils/useNavigate';
import EditableTabTitle from 'sentry/views/issueList/issueViews/editableTabTitle';
import {IssueViewEllipsisMenu} from 'sentry/views/issueList/issueViews/issueViewEllipsisMenu';
import {
Expand All @@ -18,21 +17,17 @@ import {
interface IssueViewTabProps {
editingTabKey: string | null;
initialTabKey: string;
router: InjectedRouter;
setEditingTabKey: (key: string | null) => void;
view: IssueView;
}

export function IssueViewTab({
editingTabKey,
initialTabKey,
router,
setEditingTabKey,
view,
}: IssueViewTabProps) {
const navigate = useNavigate();

const {cursor: _cursor, page: _page, ...queryParams} = router?.location?.query ?? {};
const [_searchParams, setSearchParams] = useSearchParams();
const {tabListState, state, dispatch} = useContext(IssueViewsContext);
const {views} = state;

Expand All @@ -43,29 +38,23 @@ export function IssueViewTab({
return;
}
dispatch({type: 'DUPLICATE_VIEW', newViewId, syncViews: true});
navigate({
...location,
query: {
...queryParams,
query: duplicatedTab.query,
sort: duplicatedTab.querySort,
viewId: newViewId,
},
setSearchParams(prev => {
prev.set('viewId', newViewId);
prev.set('query', duplicatedTab.query);
prev.set('sort', duplicatedTab.querySort);
return prev;
});
};

const handleDiscardChanges = () => {
dispatch({type: 'DISCARD_CHANGES'});
const originalTab = views.find(tab => tab.key === tabListState?.selectedKey);
if (originalTab) {
navigate({
...location,
query: {
...queryParams,
query: originalTab.query,
sort: originalTab.querySort,
viewId: originalTab.id,
},
setSearchParams(prev => {
prev.set('query', originalTab.query);
prev.set('sort', originalTab.querySort);
prev.set('viewId', originalTab.id);
return prev;
});
}
};
Expand Down
138 changes: 49 additions & 89 deletions static/app/views/issueList/issueViewsHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {useContext, useEffect, useMemo, useState} from 'react';
import {useContext, useEffect, useState} from 'react';
import {useSearchParams} from 'react-router-dom';
import styled from '@emotion/styled';
import type {Node} from '@react-types/shared';

Expand All @@ -12,7 +13,6 @@ import {
import type {DraggableTabListItemProps} from 'sentry/components/draggableTabs/item';
import GlobalEventProcessingAlert from 'sentry/components/globalEventProcessingAlert';
import * as Layout from 'sentry/components/layouts/thirds';
import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
import {PageHeadingQuestionTooltip} from 'sentry/components/pageHeadingQuestionTooltip';
import {IconMegaphone, IconPause, IconPlay} from 'sentry/icons';
import {t} from 'sentry/locale';
Expand All @@ -22,10 +22,7 @@ import {trackAnalytics} from 'sentry/utils/analytics';
import normalizeUrl from 'sentry/utils/url/normalizeUrl';
import {useFeedbackForm} from 'sentry/utils/useFeedbackForm';
import {useHotkeys} from 'sentry/utils/useHotkeys';
import {useLocation} from 'sentry/utils/useLocation';
import {useNavigate} from 'sentry/utils/useNavigate';
import useOrganization from 'sentry/utils/useOrganization';
import usePageFilters from 'sentry/utils/usePageFilters';
import useProjects from 'sentry/utils/useProjects';
import {
generateTempViewId,
Expand All @@ -46,10 +43,6 @@ type IssueViewsIssueListHeaderProps = {
selectedProjectIds: number[];
};

type IssueViewsIssueListHeaderTabsContentProps = {
router: InjectedRouter;
};

function IssueViewsIssueListHeader({
selectedProjectIds,
realtimeActive,
Expand Down Expand Up @@ -150,7 +143,7 @@ function IssueViewsIssueListHeader({
}
)}
>
<IssueViewsIssueListHeaderTabsContent router={router} />
<IssueViewsIssueListHeaderTabsContent />
</StyledIssueViews>
) : (
<div style={{height: 33}} />
Expand All @@ -159,53 +152,28 @@ function IssueViewsIssueListHeader({
);
}

function IssueViewsIssueListHeaderTabsContent({
router,
}: IssueViewsIssueListHeaderTabsContentProps) {
function IssueViewsIssueListHeaderTabsContent() {
const organization = useOrganization();
const navigate = useNavigate();
const location = useLocation();
const pageFilters = usePageFilters();
const [searchParams, setSearchParams] = useSearchParams();
const {query, sort, viewId} = Object.fromEntries(searchParams);

const {newViewActive, setNewViewActive} = useContext(NewTabContext);
const {tabListState, state, dispatch} = useContext(IssueViewsContext);
const {views, tempView} = state;

const [editingTabKey, setEditingTabKey] = useState<string | null>(null);

// TODO(msun): Use the location from useLocation instead of props router in the future
const {cursor: _cursor, page: _page, ...queryParams} = router?.location.query;
const {query, sort, viewId, project, environment} = queryParams;
const queryParamsWithPageFilters = useMemo(() => {
return {
...queryParams,
project: project ?? pageFilters.selection.projects,
environment: environment ?? pageFilters.selection.environments,
...normalizeDateTimeParams(pageFilters.selection.datetime),
};
}, [
environment,
pageFilters.selection.datetime,
pageFilters.selection.environments,
pageFilters.selection.projects,
project,
queryParams,
]);

// This insane useEffect ensures that the correct tab is selected when the url updates
useEffect(() => {
// If no query, sort, or viewId is present, set the first tab as the selected tab, update query accordingly
if (!query && !sort && !viewId) {
navigate(
normalizeUrl({
...location,
query: {
...queryParamsWithPageFilters,
query: views[0]!.query,
sort: views[0]!.querySort,
viewId: views[0]!.id,
},
}),
setSearchParams(
prev => {
prev.set('query', views[0]!.query);
prev.set('sort', views[0]!.querySort);
prev.set('viewId', views[0]!.id);
return prev;
},
{replace: true}
);
tabListState?.setSelectedKey(views[0]!.key);
Expand All @@ -215,14 +183,16 @@ function IssueViewsIssueListHeaderTabsContent({
if (viewId) {
const selectedTab = views.find(tab => tab.id === viewId);
if (selectedTab) {
const issueSortOption = Object.values(IssueSortOptions).includes(sort)
const issueSortOption = Object.values(IssueSortOptions).includes(
sort as IssueSortOptions
)
? sort
: IssueSortOptions.DATE;

const newUnsavedChanges: [string, IssueSortOptions] | undefined =
query === selectedTab.query && sort === selectedTab.querySort
? undefined
: [query ?? selectedTab.query, issueSortOption];
: [query ?? selectedTab.query, issueSortOption as IssueSortOptions];
if (
(newUnsavedChanges && !selectedTab.unsavedChanges) ||
selectedTab.unsavedChanges?.[0] !== newUnsavedChanges?.[0] ||
Expand All @@ -239,69 +209,64 @@ function IssueViewsIssueListHeaderTabsContent({
dispatch({type: 'UPDATE_UNSAVED_CHANGES', unsavedChanges: undefined});
}
if (!tabListState?.selectionManager.isSelected(selectedTab.key)) {
navigate(
normalizeUrl({
...location,
query: {
...queryParamsWithPageFilters,
query: newUnsavedChanges ? newUnsavedChanges[0] : selectedTab.query,
sort: newUnsavedChanges ? newUnsavedChanges[1] : selectedTab.querySort,
viewId: selectedTab.id,
},
}),
setSearchParams(
prev => {
prev.set(
'query',
newUnsavedChanges ? newUnsavedChanges[0] : selectedTab.query
);
prev.set(
'sort',
newUnsavedChanges ? newUnsavedChanges[1] : selectedTab.querySort
);
prev.set('viewId', selectedTab.id);
return prev;
},
{replace: true}
);
tabListState?.setSelectedKey(selectedTab.key);
}
} else {
// if a viewId does not exist, remove it from the query
tabListState?.setSelectedKey(TEMPORARY_TAB_KEY);
navigate(
normalizeUrl({
...location,
query: {
...queryParamsWithPageFilters,
viewId: undefined,
},
}),
setSearchParams(
prev => {
prev.delete('viewId');
return prev;
},
{replace: true}
);
trackAnalytics('issue_views.shared_view_opened', {
organization,
query,
query: query ?? '',
});
}
return;
}
if (query) {
if (!tabListState?.selectionManager.isSelected(TEMPORARY_TAB_KEY)) {
dispatch({type: 'SET_TEMP_VIEW', query, sort});
navigate(
normalizeUrl({
...location,
query: {
...queryParamsWithPageFilters,
viewId: undefined,
},
}),
dispatch({type: 'SET_TEMP_VIEW', query, sort: sort as IssueSortOptions});
setSearchParams(
prev => {
prev.set('viewId', TEMPORARY_TAB_KEY);
return prev;
},
{replace: true}
);
tabListState?.setSelectedKey(TEMPORARY_TAB_KEY);
return;
}
}
}, [
navigate,
organization.slug,
query,
sort,
viewId,
tabListState,
location,
queryParamsWithPageFilters,
views,
organization,
dispatch,
setSearchParams,
]);

// This useEffect ensures the "new view" page is displayed/hidden correctly
Expand All @@ -313,11 +278,11 @@ function IssueViewsIssueListHeaderTabsContent({
// If the user types in query manually while the new view flow is showing,
// then replace the add view flow with the issue stream with the query loaded,
// and persist the query
if (newViewActive && query !== '') {
if (newViewActive && query && query !== '') {
setNewViewActive(false);
dispatch({
type: 'UPDATE_UNSAVED_CHANGES',
unsavedChanges: [query, sort ?? IssueSortOptions.DATE],
unsavedChanges: [query, (sort as IssueSortOptions) ?? IssueSortOptions.DATE],
isCommitted: true,
syncViews: true,
});
Expand Down Expand Up @@ -354,13 +319,10 @@ function IssueViewsIssueListHeaderTabsContent({
const tempId = generateTempViewId();
dispatch({type: 'CREATE_NEW_VIEW', tempId});
tabListState?.setSelectedKey(tempId);
navigate({
...location,
query: {
...queryParams,
query: '',
viewId: tempId,
},
setSearchParams(prev => {
prev.set('query', '');
prev.set('viewId', tempId);
return prev;
});
};

Expand Down Expand Up @@ -394,7 +356,6 @@ function IssueViewsIssueListHeaderTabsContent({
key={view.key}
to={normalizeUrl({
query: {
...queryParams,
query: view.unsavedChanges?.[0] ?? view.query,
sort: view.unsavedChanges?.[1] ?? view.querySort,
viewId: view.id !== TEMPORARY_TAB_KEY ? view.id : undefined,
Expand All @@ -407,7 +368,6 @@ function IssueViewsIssueListHeaderTabsContent({
key={view.key}
view={view}
initialTabKey={initialTabKey}
router={router}
editingTabKey={editingTabKey}
setEditingTabKey={setEditingTabKey}
/>
Expand Down
Loading