Skip to content
Draft
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
2 changes: 2 additions & 0 deletions static/app/components/core/form/scrapsForm.tsx
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

will revert

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {FieldMeta} from '@sentry/scraps/form/field/meta';
import {FieldLayout} from '@sentry/scraps/form/layout';
import {FieldGroup} from '@sentry/scraps/form/layout/fieldGroup';

import {CheckboxField} from 'sentry/components/forms/fields/checkboxField';
import {RequestError} from 'sentry/utils/requestError/requestError';

import {InputField} from './field/inputField';
Expand Down Expand Up @@ -42,6 +43,7 @@ export const defaultFormOptions = formOptions({

const fieldComponents = {
Base: BaseField,
Checkbox: CheckboxField,
Input: InputField,
Number: NumberField,
Password: PasswordField,
Expand Down
170 changes: 0 additions & 170 deletions static/app/components/dataExport.spec.tsx

This file was deleted.

104 changes: 9 additions & 95 deletions static/app/components/dataExport.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
import {useCallback, useEffect, useRef, useState} from 'react';
import {useEffect, useRef} from 'react';
import debounce from 'lodash/debounce';

import {Button} from '@sentry/scraps/button';

import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
import Feature from 'sentry/components/acl/feature';
import {ExportQueryType, useDataExport} from 'sentry/components/useDataExport';
import {t} from 'sentry/locale';
import {useApi} from 'sentry/utils/useApi';
import {useOrganization} from 'sentry/utils/useOrganization';

// NOTE: Coordinate with other ExportQueryType (src/sentry/data_export/base.py)
export enum ExportQueryType {
ISSUES_BY_TAG = 'Issues-by-Tag',
DISCOVER = 'Discover',
EXPLORE = 'Explore',
}

interface DataExportPayload {
type DataExportPayload = {
queryInfo: any;
queryType: ExportQueryType; // TODO(ts): Formalize different possible payloads
}
};

interface DataExportProps {
payload: DataExportPayload;
Expand All @@ -31,69 +22,6 @@ interface DataExportProps {
size?: 'xs' | 'sm' | 'md';
}

export function useDataExport({
payload,
inProgressCallback,
unmountedRef,
}: {
payload: DataExportPayload;
inProgressCallback?: (inProgress: boolean) => void;
unmountedRef?: React.RefObject<boolean>;
}) {
const organization = useOrganization();
const api = useApi();

return useCallback(() => {
inProgressCallback?.(true);

// This is a fire and forget request.
api
.requestPromise(`/organizations/${organization.slug}/data-export/`, {
includeAllArgs: true,
method: 'POST',
data: {
query_type: payload.queryType,
query_info: payload.queryInfo,
},
})
.then(([_data, _, response]) => {
// If component has unmounted, don't do anything
if (unmountedRef?.current) {
return;
}

addSuccessMessage(
response?.status === 201
? t(
"Sit tight. We'll shoot you an email when your data is ready for download."
)
: t("It looks like we're already working on it. Sit tight, we'll email you.")
);
})
.catch(err => {
// If component has unmounted, don't do anything
if (unmountedRef?.current) {
return;
}
const message =
err?.responseJSON?.detail ??
t(
"We tried our hardest, but we couldn't export your data. Give it another go."
);

addErrorMessage(message);
inProgressCallback?.(false);
});
}, [
payload.queryInfo,
payload.queryType,
organization.slug,
api,
inProgressCallback,
unmountedRef,
]);
}

export function DataExport({
children,
disabled,
Expand All @@ -104,41 +32,27 @@ export function DataExport({
onClick,
}: DataExportProps): React.ReactElement {
const unmountedRef = useRef(false);
const [inProgress, setInProgress] = useState(false);
const handleDataExport = useDataExport({
const {isExportWorking, runExport} = useDataExport({
payload,
unmountedRef,
inProgressCallback: setInProgress,
});

// We clear the indicator if export props change so that the user
// can fire another export without having to wait for the previous one to finish.
useEffect(() => {
if (inProgress) {
setInProgress(false);
}
// We are skipping the inProgress dependency because it would have fired on each handleDataExport
// call and would have immediately turned off the value giving users no feedback on their click action.
// An alternative way to handle this would have probably been to key the component by payload/queryType,
// but that seems like it can be a complex object so tracking changes could result in very brittle behavior.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [payload.queryType, payload.queryInfo]);

// Tracking unmounting of the component to prevent setState call on unmounted component
useEffect(() => {
return () => {
unmountedRef.current = true;
};
}, []);

const handleClick = () => {
debounce(handleDataExport, 500)();
debounce(() => {
void runExport();
}, 500)();
onClick?.();
};

return (
<Feature features={overrideFeatureFlags ? [] : 'organizations:discover-query'}>
{inProgress ? (
{isExportWorking ? (
<Button
size={size}
priority="default"
Expand Down
Loading
Loading