modal.tsx 11 KB

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