export function CreateGitHubRepositoryModal()

in packages/serverless-logic-web-tools/src/editor/CreateGitHubRepositoryModal.tsx [59:337]


export function CreateGitHubRepositoryModal(props: {
  workspace: ActiveWorkspace;
  isOpen: boolean;
  onClose: () => void;
  onSuccess: (args: { url: string }) => void;
  currentFile: WorkspaceFile;
}) {
  const history = useHistory();
  const routes = useRoutes();
  const workspaces = useWorkspaces();
  const settingsDispatch = useSettingsDispatch();
  const githubAuthInfo = useGitHubAuthInfo();

  const { applyAccelerator, rollbackApply, canAcceleratorBeUsed, hasConflictsWithAccelerator, cleanUpTempResources } =
    useAccelerator({
      workspaceId: props.workspace.descriptor.workspaceId,
      currentFile: props.currentFile,
      accelerator: ACCELERATOR_TO_USE,
    });

  const [isPrivate, setPrivate] = useState(false);
  const [isLoading, setLoading] = useState(false);
  const [error, setError] = useState<string | undefined>(undefined);
  const [name, setName] = useState(getSuggestedRepositoryName(props.workspace.descriptor.name));
  const [shouldUseAccelerator, setShouldUseAccelerator] = useState(false);

  useEffect(() => {
    setName(getSuggestedRepositoryName(props.workspace.descriptor.name));
  }, [props.workspace.descriptor.name]);

  useEffect(() => {
    if (props.isOpen) {
      setShouldUseAccelerator(false);
    }
  }, [props.isOpen]);

  const create = useCallback(async () => {
    let applyAcceleratorResult: ApplyAcceleratorResult | undefined;
    if (!githubAuthInfo) {
      return;
    }

    setError(undefined);
    setLoading(true);

    try {
      const repo = await settingsDispatch.github.octokit.repos.createForAuthenticatedUser({ name, private: isPrivate });

      if (!repo.data.clone_url) {
        throw new Error("Repository creation failed");
      }

      if (canAcceleratorBeUsed && shouldUseAccelerator) {
        applyAcceleratorResult = await applyAccelerator();

        if (!applyAcceleratorResult.success) {
          throw new Error(applyAcceleratorResult.message);
        }
      } else {
        await workspaces.createSavePoint({
          workspaceId: props.workspace.descriptor.workspaceId,
          gitConfig: {
            name: githubAuthInfo.name,
            email: githubAuthInfo.email,
          },
        });
      }

      await workspaces.addRemote({
        workspaceId: props.workspace.descriptor.workspaceId,
        url: repo.data.clone_url,
        name: GIT_ORIGIN_REMOTE_NAME,
        force: true,
      });

      await workspaces.push({
        workspaceId: props.workspace.descriptor.workspaceId,
        remote: GIT_ORIGIN_REMOTE_NAME,
        ref: GIT_DEFAULT_BRANCH,
        remoteRef: `refs/heads/${GIT_DEFAULT_BRANCH}`,
        force: false,
        authInfo: githubAuthInfo,
      });

      await workspaces.initGitOnWorkspace({
        workspaceId: props.workspace.descriptor.workspaceId,
        remoteUrl: new URL(repo.data.clone_url),
      });

      await workspaces.renameWorkspace({
        workspaceId: props.workspace.descriptor.workspaceId,
        newName: new URL(repo.data.html_url).pathname.substring(1),
      });

      props.onClose();
      props.onSuccess({ url: repo.data.html_url });

      if (applyAcceleratorResult?.success) {
        history.replace({
          pathname: routes.workspaceWithFilePath.path({
            workspaceId: props.workspace.descriptor.workspaceId,
            fileRelativePath: applyAcceleratorResult.currentFileAfterMoving.relativePathWithoutExtension,
            extension: applyAcceleratorResult.currentFileAfterMoving.extension,
          }),
        });
      }
    } catch (err) {
      if (applyAcceleratorResult?.success) {
        await rollbackApply({ ...applyAcceleratorResult });
      }

      history.replace({
        pathname: routes.workspaceWithFilePath.path({
          extension: props.currentFile.extension,
          fileRelativePath: props.currentFile.relativePathWithoutExtension,
          workspaceId: props.workspace.descriptor.workspaceId,
        }),
      });

      console.error(err);
      setError(err);
    } finally {
      setLoading(false);

      if (applyAcceleratorResult) {
        await cleanUpTempResources({
          backupBranchName: applyAcceleratorResult.backupBranchName,
          tempBranchName: applyAcceleratorResult.tempBranchName,
        });
      }
    }
  }, [
    githubAuthInfo,
    canAcceleratorBeUsed,
    shouldUseAccelerator,
    settingsDispatch.github.octokit.repos,
    name,
    isPrivate,
    workspaces,
    props,
    applyAccelerator,
    history,
    routes.workspaceWithFilePath,
    rollbackApply,
    cleanUpTempResources,
  ]);

  const isNameValid = useMemo(() => {
    return name.match(/^[._\-\w\d]+$/g);
  }, [name]);

  const validated = useMemo(() => {
    if (isNameValid) {
      return ValidatedOptions.success;
    } else {
      return ValidatedOptions.error;
    }
  }, [isNameValid]);

  return (
    <Modal
      variant={ModalVariant.medium}
      aria-label={"Create a new GitHub repository"}
      isOpen={props.isOpen}
      onClose={props.onClose}
      title={"Create GitHub repository"}
      titleIconVariant={GithubIcon}
      description={`The contents of '${props.workspace.descriptor.name}' will be all in the new GitHub Repository.`}
      actions={[
        <Button isLoading={isLoading} key="create" variant="primary" onClick={create} isDisabled={!isNameValid}>
          Create
        </Button>,
      ]}
    >
      <br />
      <Form
        style={{ padding: "0 16px 0 16px" }}
        onSubmit={(e) => {
          e.preventDefault();
          e.stopPropagation();

          return create();
        }}
      >
        {error && (
          <FormAlert>
            <Alert variant="danger" title={"Error creating GitHub Repository. " + error} isInline={true} />
            <br />
          </FormAlert>
        )}
        <FormGroup label="Name" isRequired={true} fieldId="github-repository-name">
          <TextInput
            id={"github-repo-name"}
            validated={validated}
            isRequired={true}
            placeholder={"Name"}
            value={name}
            onChange={(_event, val) => setName(val)}
          />
          {validated === "error" ? (
            <FormHelperText>
              <HelperText>
                <HelperTextItem variant="error">
                  {"Invalid name. Only letters, numbers, dashes (-), dots (.), and underscores (_) are allowed."}
                </HelperTextItem>
              </HelperText>
            </FormHelperText>
          ) : (
            <FormHelperText>
              <HelperText>
                <HelperTextItem variant="success"></HelperTextItem>
              </HelperText>
            </FormHelperText>
          )}
        </FormGroup>
        <Divider inset={{ default: "inset3xl" }} />
        <FormGroup fieldId="github-repo-visibility">
          <Radio
            isChecked={!isPrivate}
            id={"github-repository-public"}
            name={"github-repository-public"}
            label={
              <>
                <UsersIcon />
                &nbsp;&nbsp; Public
              </>
            }
            description={"Anyone on the internet can see this repository. You choose who can commit."}
            onChange={() => setPrivate(false)}
          />
          <br />
          <Radio
            isChecked={isPrivate}
            id={"github-repository-private"}
            name={"github-repository-private"}
            label={
              <>
                <LockIcon />
                &nbsp;&nbsp; Private
              </>
            }
            description={"You choose who can see and commit to this repository."}
            onChange={() => setPrivate(true)}
          />
          <br />

          <Tooltip
            content={`${ACCELERATOR_TO_USE.name} Accelerator cannot be applied to this workspace.`}
            trigger={!canAcceleratorBeUsed ? "mouseenter click" : ""}
          >
            <Checkbox
              id="check-use-accelerator"
              label={`Use ${ACCELERATOR_TO_USE.name} Accelerator`}
              description={
                <>
                  {`A project structure is created and the workspace files are placed in '${ACCELERATOR_TO_USE.folderToMoveFiles}' folder.`}
                  <br />
                  {canAcceleratorBeUsed &&
                    hasConflictsWithAccelerator &&
                    `Note that conflicting files will be moved to '${ACCELERATOR_TO_USE.folderToMoveBlockedFiles}' folder.`}
                </>
              }
              isChecked={shouldUseAccelerator}
              onChange={(_event, checked) => setShouldUseAccelerator(checked)}
              isDisabled={!canAcceleratorBeUsed}
            />
          </Tooltip>
          {
            <FormHelperText>
              <HelperText>
                <HelperTextItem variant="default"></HelperTextItem>
              </HelperText>
            </FormHelperText>
          }
        </FormGroup>
      </Form>
    </Modal>
  );
}