modal.tsx 11 KB

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