Browse Source

ref(proguard): Replace AsyncView with useApiQuery (#53046)

Priscila Oliveira 1 year ago
parent
commit
79bd47dcb7

+ 147 - 8
static/app/views/settings/projectProguard/index.tsx

@@ -1,13 +1,152 @@
-import {Component} from 'react';
+import {Fragment, useCallback, useState} from 'react';
+import {RouteComponentProps} from 'react-router';
+import styled from '@emotion/styled';
 
-import withOrganization from 'sentry/utils/withOrganization';
+import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
+import ExternalLink from 'sentry/components/links/externalLink';
+import Pagination from 'sentry/components/pagination';
+import PanelTable from 'sentry/components/panels/panelTable';
+import SearchBar from 'sentry/components/searchBar';
+import {t, tct} from 'sentry/locale';
+import {Organization, Project} from 'sentry/types';
+import {DebugFile} from 'sentry/types/debugFiles';
+import {useApiQuery} from 'sentry/utils/queryClient';
+import useApi from 'sentry/utils/useApi';
+import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
+import TextBlock from 'sentry/views/settings/components/text/textBlock';
 
-import ProjectProguard from './projectProguard';
+import ProjectProguardRow from './projectProguardRow';
 
-class ProjectProguardContainer extends Component<ProjectProguard['props']> {
-  render() {
-    return <ProjectProguard {...this.props} />;
-  }
+export type ProjectProguardProps = RouteComponentProps<{projectId: string}, {}> & {
+  organization: Organization;
+  project: Project;
+};
+
+function ProjectProguard({organization, location, router, params}: ProjectProguardProps) {
+  const api = useApi();
+  const {projectId} = params;
+  const [loading, setLoading] = useState(false);
+
+  const {
+    data: mappings,
+    isLoading: dataLoading,
+    getResponseHeader,
+    refetch: fetchData,
+  } = useApiQuery<DebugFile[]>(
+    [
+      `/projects/${organization.slug}/${projectId}/files/dsyms/`,
+      {query: {query: location.query.query, file_formats: 'proguard'}},
+    ],
+    {
+      staleTime: 0,
+    }
+  );
+
+  const mappingsPageLinks = getResponseHeader?.('Link');
+
+  const handleSearch = useCallback(
+    (query: string) => {
+      router.push({
+        ...location,
+        query: {...location.query, cursor: undefined, query},
+      });
+    },
+    [location, router]
+  );
+
+  const handleDelete = useCallback(
+    async (id: string) => {
+      setLoading(true);
+      try {
+        await api.requestPromise(
+          `/projects/${
+            organization.slug
+          }/${projectId}/files/dsyms/?id=${encodeURIComponent(id)}`,
+          {
+            method: 'DELETE',
+          }
+        );
+        setLoading(false);
+        addSuccessMessage('Successfully deleted the mapping file');
+        fetchData();
+      } catch {
+        setLoading(false);
+        addErrorMessage('An error occurred while deleting the mapping file');
+      }
+    },
+    [api, fetchData, organization.slug, projectId]
+  );
+
+  const query =
+    typeof location.query.query === 'string' ? location.query.query : undefined;
+
+  const isLoading = loading || dataLoading;
+
+  return (
+    <Fragment>
+      <SettingsPageHeader
+        title={t('ProGuard Mappings')}
+        action={
+          <SearchBar
+            placeholder={t('Filter mappings')}
+            onSearch={handleSearch}
+            query={query}
+            width="280px"
+          />
+        }
+      />
+
+      <TextBlock>
+        {tct(
+          `ProGuard mapping files are used to convert minified classes, methods and field names into a human readable format. To learn more about proguard mapping files, [link: read the docs].`,
+          {
+            link: (
+              <ExternalLink href="https://docs.sentry.io/platforms/android/proguard/" />
+            ),
+          }
+        )}
+      </TextBlock>
+
+      <StyledPanelTable
+        headers={[t('Mapping'), <SizeColumn key="size">{t('File Size')}</SizeColumn>, '']}
+        emptyMessage={
+          query
+            ? t('There are no mappings that match your search.')
+            : t('There are no mappings for this project.')
+        }
+        isEmpty={mappings?.length === 0}
+        isLoading={isLoading}
+      >
+        {!mappings?.length
+          ? null
+          : mappings.map(mapping => {
+              const downloadUrl = `${api.baseUrl}/projects/${
+                organization.slug
+              }/${projectId}/files/dsyms/?id=${encodeURIComponent(mapping.id)}`;
+
+              return (
+                <ProjectProguardRow
+                  mapping={mapping}
+                  downloadUrl={downloadUrl}
+                  onDelete={handleDelete}
+                  downloadRole={organization.debugFilesRole}
+                  key={mapping.id}
+                  orgSlug={organization.slug}
+                />
+              );
+            })}
+      </StyledPanelTable>
+      <Pagination pageLinks={mappingsPageLinks} />
+    </Fragment>
+  );
 }
 
-export default withOrganization(ProjectProguardContainer);
+export default ProjectProguard;
+
+const StyledPanelTable = styled(PanelTable)`
+  grid-template-columns: minmax(220px, 1fr) max-content 120px;
+`;
+
+const SizeColumn = styled('div')`
+  text-align: right;
+`;

+ 0 - 184
static/app/views/settings/projectProguard/projectProguard.tsx

@@ -1,184 +0,0 @@
-import {Fragment} from 'react';
-import {RouteComponentProps} from 'react-router';
-import styled from '@emotion/styled';
-
-import ExternalLink from 'sentry/components/links/externalLink';
-import Pagination from 'sentry/components/pagination';
-import PanelTable from 'sentry/components/panels/panelTable';
-import SearchBar from 'sentry/components/searchBar';
-import {t, tct} from 'sentry/locale';
-import {Organization, Project} from 'sentry/types';
-import {DebugFile} from 'sentry/types/debugFiles';
-import routeTitleGen from 'sentry/utils/routeTitle';
-import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
-import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
-import TextBlock from 'sentry/views/settings/components/text/textBlock';
-
-import ProjectProguardRow from './projectProguardRow';
-
-type Props = RouteComponentProps<{projectId: string}, {}> & {
-  organization: Organization;
-  project: Project;
-};
-
-type State = DeprecatedAsyncView['state'] & {
-  mappings: DebugFile[];
-};
-
-class ProjectProguard extends DeprecatedAsyncView<Props, State> {
-  getTitle() {
-    const {projectId} = this.props.params;
-
-    return routeTitleGen(t('ProGuard Mappings'), projectId, false);
-  }
-
-  getDefaultState(): State {
-    return {
-      ...super.getDefaultState(),
-      mappings: [],
-    };
-  }
-
-  getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
-    const {organization, params, location} = this.props;
-    const {projectId} = params;
-
-    const endpoints: ReturnType<DeprecatedAsyncView['getEndpoints']> = [
-      [
-        'mappings',
-        `/projects/${organization.slug}/${projectId}/files/dsyms/`,
-        {query: {query: location.query.query, file_formats: 'proguard'}},
-      ],
-    ];
-
-    return endpoints;
-  }
-
-  handleDelete = (id: string) => {
-    const {organization} = this.props;
-    const {projectId} = this.props.params;
-
-    this.setState({
-      loading: true,
-    });
-
-    this.api.request(
-      `/projects/${organization.slug}/${projectId}/files/dsyms/?id=${encodeURIComponent(
-        id
-      )}`,
-      {
-        method: 'DELETE',
-        complete: () => this.fetchData(),
-      }
-    );
-  };
-
-  handleSearch = (query: string) => {
-    const {location, router} = this.props;
-
-    router.push({
-      ...location,
-      query: {...location.query, cursor: undefined, query},
-    });
-  };
-
-  getQuery() {
-    const {query} = this.props.location.query;
-
-    return typeof query === 'string' ? query : undefined;
-  }
-
-  getEmptyMessage() {
-    if (this.getQuery()) {
-      return t('There are no mappings that match your search.');
-    }
-
-    return t('There are no mappings for this project.');
-  }
-
-  renderLoading() {
-    return this.renderBody();
-  }
-
-  renderMappings() {
-    const {mappings} = this.state;
-    const {organization, params} = this.props;
-    const {projectId} = params;
-
-    if (!mappings?.length) {
-      return null;
-    }
-
-    return mappings.map(mapping => {
-      const downloadUrl = `${this.api.baseUrl}/projects/${
-        organization.slug
-      }/${projectId}/files/dsyms/?id=${encodeURIComponent(mapping.id)}`;
-
-      return (
-        <ProjectProguardRow
-          mapping={mapping}
-          downloadUrl={downloadUrl}
-          onDelete={this.handleDelete}
-          downloadRole={organization.debugFilesRole}
-          key={mapping.id}
-          orgSlug={organization.slug}
-        />
-      );
-    });
-  }
-
-  renderBody() {
-    const {loading, mappings, mappingsPageLinks} = this.state;
-
-    return (
-      <Fragment>
-        <SettingsPageHeader
-          title={t('ProGuard Mappings')}
-          action={
-            <SearchBar
-              placeholder={t('Filter mappings')}
-              onSearch={this.handleSearch}
-              query={this.getQuery()}
-              width="280px"
-            />
-          }
-        />
-
-        <TextBlock>
-          {tct(
-            `ProGuard mapping files are used to convert minified classes, methods and field names into a human readable format. To learn more about proguard mapping files, [link: read the docs].`,
-            {
-              link: (
-                <ExternalLink href="https://docs.sentry.io/platforms/android/proguard/" />
-              ),
-            }
-          )}
-        </TextBlock>
-
-        <StyledPanelTable
-          headers={[
-            t('Mapping'),
-            <SizeColumn key="size">{t('File Size')}</SizeColumn>,
-            '',
-          ]}
-          emptyMessage={this.getEmptyMessage()}
-          isEmpty={mappings?.length === 0}
-          isLoading={loading}
-        >
-          {this.renderMappings()}
-        </StyledPanelTable>
-        <Pagination pageLinks={mappingsPageLinks} />
-      </Fragment>
-    );
-  }
-}
-
-const StyledPanelTable = styled(PanelTable)`
-  grid-template-columns: minmax(220px, 1fr) max-content 120px;
-`;
-
-const SizeColumn = styled('div')`
-  text-align: right;
-`;
-
-export default ProjectProguard;