modal.tsx 11 KB

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