modal.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. import type {ModalTypes} from 'sentry/components/globalModal';
  2. import type {CreateNewIntegrationModalOptions} from 'sentry/components/modals/createNewIntegrationModal';
  3. import type {CreateReleaseIntegrationModalOptions} from 'sentry/components/modals/createReleaseIntegrationModal';
  4. import type {DashboardWidgetQuerySelectorModalOptions} from 'sentry/components/modals/dashboardWidgetQuerySelectorModal';
  5. import type {InviteRow} from 'sentry/components/modals/inviteMembersModal/types';
  6. import type {ReprocessEventModalOptions} from 'sentry/components/modals/reprocessEventModal';
  7. import type {OverwriteWidgetModalProps} from 'sentry/components/modals/widgetBuilder/overwriteWidgetModal';
  8. import type {WidgetViewerModalOptions} from 'sentry/components/modals/widgetViewerModal';
  9. import type {Category} from 'sentry/components/platformPicker';
  10. import ModalStore from 'sentry/stores/modalStore';
  11. import type {AppStoreConnectStatusData, CustomRepoType} from 'sentry/types/debugFiles';
  12. import type {Event} from 'sentry/types/event';
  13. import type {Group, IssueOwnership} from 'sentry/types/group';
  14. import type {SentryApp} from 'sentry/types/integrations';
  15. import type {MissingMember, Organization, OrgRole, Team} from 'sentry/types/organization';
  16. import type {Project} from 'sentry/types/project';
  17. import {WidgetType} from 'sentry/views/dashboards/types';
  18. export type ModalOptions = ModalTypes['options'];
  19. export type ModalRenderProps = ModalTypes['renderProps'];
  20. /**
  21. * Show a modal
  22. */
  23. export function openModal(
  24. renderer: (renderProps: ModalRenderProps) => React.ReactNode,
  25. options?: ModalOptions
  26. ) {
  27. ModalStore.openModal(renderer, options ?? {});
  28. }
  29. /**
  30. * Close modal
  31. */
  32. export function closeModal() {
  33. ModalStore.closeModal();
  34. }
  35. type EmailVerificationModalOptions = {
  36. actionMessage?: string;
  37. emailVerified?: boolean;
  38. onClose?: () => void;
  39. };
  40. type InviteMembersModalOptions = {
  41. initialData?: Partial<InviteRow>[];
  42. onClose?: () => void;
  43. source?: string;
  44. };
  45. export async function openEmailVerification({
  46. onClose,
  47. ...args
  48. }: EmailVerificationModalOptions = {}) {
  49. const mod = await import('sentry/components/modals/emailVerificationModal');
  50. const {default: Modal} = mod;
  51. openModal(deps => <Modal {...deps} {...args} />, {onClose});
  52. }
  53. type OpenDiffModalOptions = {
  54. baseIssueId: Group['id'];
  55. orgId: Organization['id'];
  56. project: Project;
  57. targetIssueId: string;
  58. baseEventId?: Event['id'];
  59. shouldBeGrouped?: string;
  60. targetEventId?: string;
  61. };
  62. export async function openDiffModal(options: OpenDiffModalOptions) {
  63. const mod = await import('sentry/components/modals/diffModal');
  64. const {default: Modal, modalCss} = mod;
  65. openModal(deps => <Modal {...deps} {...options} />, {modalCss});
  66. }
  67. type CreateTeamModalOptions = {
  68. /**
  69. * The organization to create a team for
  70. */
  71. organization: Organization;
  72. onClose?: (team: Team) => void;
  73. /**
  74. * An initial project to add the team to. This may be deprecated soon as we may add a project selection inside of the modal flow
  75. */
  76. project?: Project;
  77. };
  78. export async function openCreateTeamModal(options: CreateTeamModalOptions) {
  79. const mod = await import('sentry/components/modals/createTeamModal');
  80. const {default: Modal} = mod;
  81. openModal(deps => <Modal {...deps} {...options} />);
  82. }
  83. type CreateOwnershipRuleModalOptions = {
  84. issueId: string;
  85. /**
  86. * The organization to create a rules for
  87. */
  88. organization: Organization;
  89. /**
  90. * The project to create a rules for
  91. */
  92. project: Project;
  93. /**
  94. * Suggestions will be created from the current event
  95. */
  96. eventData?: Event;
  97. };
  98. /**
  99. * Open the edit ownership modal within issue details
  100. */
  101. export async function openIssueOwnershipRuleModal(
  102. options: CreateOwnershipRuleModalOptions
  103. ) {
  104. const mod = await import('sentry/components/modals/issueOwnershipRuleModal');
  105. const {default: Modal, modalCss} = mod;
  106. openModal(deps => <Modal {...deps} {...options} />, {modalCss});
  107. }
  108. export type EditOwnershipRulesModalOptions = {
  109. onSave: (ownership: IssueOwnership) => void;
  110. organization: Organization;
  111. ownership: IssueOwnership;
  112. project: Project;
  113. };
  114. export async function openEditOwnershipRules(options: EditOwnershipRulesModalOptions) {
  115. const mod = await import('sentry/components/modals/editOwnershipRulesModal');
  116. const {default: Modal, modalCss} = mod;
  117. openModal(deps => <Modal {...deps} {...options} />, {
  118. closeEvents: 'escape-key',
  119. modalCss,
  120. });
  121. }
  122. export async function openCommandPalette(options: ModalOptions = {}) {
  123. const mod = await import('sentry/components/modals/commandPalette');
  124. const {default: Modal, modalCss} = mod;
  125. openModal(deps => <Modal {...deps} {...options} />, {modalCss});
  126. }
  127. type RecoveryModalOptions = {
  128. authenticatorName: string;
  129. };
  130. export async function openRecoveryOptions(options: RecoveryModalOptions) {
  131. const mod = await import('sentry/components/modals/recoveryOptionsModal');
  132. const {default: Modal} = mod;
  133. openModal(deps => <Modal {...deps} {...options} />);
  134. }
  135. export type TeamAccessRequestModalOptions = {
  136. memberId: string;
  137. orgId: string;
  138. teamId: string;
  139. };
  140. export async function openTeamAccessRequestModal(options: TeamAccessRequestModalOptions) {
  141. const mod = await import('sentry/components/modals/teamAccessRequestModal');
  142. const {default: Modal} = mod;
  143. openModal(deps => <Modal {...deps} {...options} />);
  144. }
  145. type HelpSearchModalOptions = {
  146. organization?: Organization;
  147. placeholder?: string;
  148. };
  149. export async function openHelpSearchModal(options?: HelpSearchModalOptions) {
  150. const mod = await import('sentry/components/modals/helpSearchModal');
  151. const {default: Modal, modalCss} = mod;
  152. openModal(deps => <Modal {...deps} {...options} />, {modalCss});
  153. }
  154. export type SentryAppDetailsModalOptions = {
  155. isInstalled: boolean;
  156. onInstall: () => Promise<void>;
  157. organization: Organization;
  158. sentryApp: SentryApp;
  159. onCloseModal?: () => void; // used for analytics
  160. };
  161. type DebugFileSourceModalOptions = {
  162. appStoreConnectSourcesQuantity: number;
  163. onSave: (data: Record<string, any>) => Promise<void>;
  164. organization: Organization;
  165. sourceType: CustomRepoType;
  166. appStoreConnectStatusData?: AppStoreConnectStatusData;
  167. onClose?: () => void;
  168. sourceConfig?: Record<string, any>;
  169. };
  170. export async function openDebugFileSourceModal({
  171. onClose,
  172. ...restOptions
  173. }: DebugFileSourceModalOptions) {
  174. const mod = await import('sentry/components/modals/debugFileCustomRepository');
  175. const {default: Modal, modalCss} = mod;
  176. openModal(deps => <Modal {...deps} {...restOptions} />, {
  177. modalCss,
  178. onClose,
  179. });
  180. }
  181. export async function openInviteMembersModal({
  182. onClose,
  183. ...args
  184. }: InviteMembersModalOptions = {}) {
  185. const mod = await import('sentry/components/modals/inviteMembersModal');
  186. const {default: Modal, modalCss} = mod;
  187. openModal(deps => <Modal {...deps} {...args} />, {modalCss, onClose});
  188. }
  189. type InviteMissingMembersModalOptions = {
  190. allowedRoles: OrgRole[];
  191. missingMembers: MissingMember[];
  192. onClose: () => void;
  193. organization: Organization;
  194. };
  195. export async function openInviteMissingMembersModal({
  196. onClose,
  197. ...args
  198. }: InviteMissingMembersModalOptions) {
  199. const mod = await import('sentry/components/modals/inviteMissingMembersModal');
  200. const {default: Modal, modalCss} = mod;
  201. openModal(deps => <Modal {...deps} {...args} />, {modalCss, onClose});
  202. }
  203. export async function openWidgetBuilderOverwriteModal(
  204. options: OverwriteWidgetModalProps
  205. ) {
  206. const mod = await import('sentry/components/modals/widgetBuilder/overwriteWidgetModal');
  207. const {default: Modal, modalCss} = mod;
  208. openModal(deps => <Modal {...deps} {...options} />, {
  209. closeEvents: 'escape-key',
  210. modalCss,
  211. });
  212. }
  213. export async function openAddToDashboardModal(options) {
  214. const mod = await import('sentry/components/modals/widgetBuilder/addToDashboardModal');
  215. const {default: Modal, modalCss} = mod;
  216. openModal(deps => <Modal {...deps} {...options} />, {
  217. closeEvents: 'escape-key',
  218. modalCss,
  219. });
  220. }
  221. export async function openImportDashboardFromFileModal(options) {
  222. const mod = await import('sentry/components/modals/importDashboardFromFileModal');
  223. const {default: Modal, modalCss} = mod;
  224. openModal(deps => <Modal {...deps} {...options} />, {
  225. closeEvents: 'escape-key',
  226. modalCss,
  227. });
  228. }
  229. export async function openCreateDashboardFromScratchpad(options) {
  230. const mod = await import('sentry/components/modals/createDashboardFromScratchpadModal');
  231. const {default: Modal, modalCss} = mod;
  232. openModal(deps => <Modal {...deps} {...options} />, {
  233. closeEvents: 'escape-key',
  234. modalCss,
  235. });
  236. }
  237. export async function openReprocessEventModal({
  238. onClose,
  239. ...options
  240. }: ReprocessEventModalOptions & {onClose?: () => void}) {
  241. const {ReprocessingEventModal} = await import(
  242. 'sentry/components/modals/reprocessEventModal'
  243. );
  244. openModal(deps => <ReprocessingEventModal {...deps} {...options} />, {onClose});
  245. }
  246. export async function demoSignupModal(options: ModalOptions = {}) {
  247. const mod = await import('sentry/components/modals/demoSignUp');
  248. const {default: Modal, modalCss} = mod;
  249. openModal(deps => <Modal {...deps} {...options} />, {modalCss});
  250. }
  251. export type DemoEndModalOptions = {
  252. orgSlug: string | null;
  253. tour: string;
  254. };
  255. export async function demoEndModal(options: DemoEndModalOptions) {
  256. const mod = await import('sentry/components/modals/demoEndModal');
  257. const {default: Modal, modalCss} = mod;
  258. openModal(deps => <Modal {...deps} {...options} />, {modalCss});
  259. }
  260. export async function openDashboardWidgetQuerySelectorModal(
  261. options: DashboardWidgetQuerySelectorModalOptions
  262. ) {
  263. const mod = await import('sentry/components/modals/dashboardWidgetQuerySelectorModal');
  264. const {default: Modal, modalCss} = mod;
  265. openModal(deps => <Modal {...deps} {...options} />, {
  266. closeEvents: 'escape-key',
  267. modalCss,
  268. });
  269. }
  270. export async function openWidgetViewerModal({
  271. onClose,
  272. ...options
  273. }: WidgetViewerModalOptions & {onClose?: () => void}) {
  274. const modalPromise =
  275. options.widget.widgetType === WidgetType.METRICS
  276. ? import('sentry/components/modals/metricWidgetViewerModal')
  277. : import('sentry/components/modals/widgetViewerModal');
  278. const mod = await modalPromise;
  279. const {default: Modal, modalCss} = mod;
  280. openModal(deps => <Modal {...deps} {...options} />, {
  281. closeEvents: 'escape-key',
  282. modalCss,
  283. onClose,
  284. });
  285. }
  286. export async function openCreateNewIntegrationModal(
  287. options: CreateNewIntegrationModalOptions
  288. ) {
  289. const mod = await import('sentry/components/modals/createNewIntegrationModal');
  290. const {default: Modal} = mod;
  291. openModal(deps => <Modal {...deps} {...options} />);
  292. }
  293. export async function openCreateReleaseIntegration(
  294. options: CreateReleaseIntegrationModalOptions
  295. ) {
  296. const mod = await import('sentry/components/modals/createReleaseIntegrationModal');
  297. const {default: Modal} = mod;
  298. openModal(deps => <Modal {...deps} {...options} />);
  299. }
  300. export type NavigateToExternalLinkModalOptions = {
  301. linkText: string;
  302. };
  303. export async function openNavigateToExternalLinkModal(
  304. options: NavigateToExternalLinkModalOptions
  305. ) {
  306. const mod = await import('sentry/components/modals/navigateToExternalLinkModal');
  307. const {default: Modal} = mod;
  308. openModal(deps => <Modal {...deps} {...options} />);
  309. }
  310. export async function openProjectCreationModal(options: {defaultCategory: Category}) {
  311. const mod = await import('sentry/components/modals/projectCreationModal');
  312. const {default: Modal, modalCss} = mod;
  313. openModal(deps => <Modal {...deps} {...options} />, {modalCss});
  314. }
  315. export async function openBulkEditMonitorsModal({onClose, ...options}: ModalOptions) {
  316. const mod = await import('sentry/components/modals/bulkEditMonitorsModal');
  317. const {default: Modal, modalCss} = mod;
  318. openModal(deps => <Modal {...deps} {...options} />, {modalCss, onClose});
  319. }