react-icons/io#IoMdLogOut TypeScript Examples

The following examples show how to use react-icons/io#IoMdLogOut. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Modal.tsx    From hub with Apache License 2.0 4 votes vote down vote up
OptOutModal = (props: Props) => {
  const { ctx } = useContext(AppCtx);
  const [apiError, setApiError] = useState<string | null>(null);
  const [repoItem, setRepoItem] = useState<Repository | null>(null);
  const [eventKind, setEventKind] = useState<EventKind>(EventKind.RepositoryTrackingErrors);
  const [isSending, setIsSending] = useState<boolean>(false);
  const [userOrganizations, setUserOrganizations] = useState<string[] | undefined>(undefined);

  const onCloseModal = () => {
    setRepoItem(null);
    props.onClose();
  };

  const submitForm = () => {
    if (!isNull(repoItem)) {
      addOptOut();
    }
  };

  const onRepoSelect = (repo: Repository): void => {
    setRepoItem(repo);
  };

  async function getAllUserOrganizations() {
    try {
      const orgs = await API.getAllUserOrganizations();
      const orgsList = orgs.map((org: Organization) => org.name);
      setUserOrganizations(orgsList);
    } catch (err: any) {
      setUserOrganizations([]);
    }
  }

  useEffect(() => {
    getAllUserOrganizations();
  }, []); /* eslint-disable-line react-hooks/exhaustive-deps */

  async function addOptOut() {
    try {
      setIsSending(true);
      await API.addOptOut(repoItem!.repositoryId!, eventKind);
      setRepoItem(null);
      setIsSending(false);
      props.onSuccess();
      props.onClose();
    } catch (err: any) {
      setIsSending(false);
      if (err.kind !== ErrorKind.Unauthorized) {
        alertDispatcher.postAlert({
          type: 'danger',
          message: `An error occurred adding the opt-out entry for ${props.getNotificationTitle(
            eventKind
          )} notifications for repository ${repoItem!.displayName || repoItem!.name}, please try again later.`,
        });
      }
    }
  }

  const getPublisher = (repo: Repository) => (
    <small className="ms-0 ms-sm-2">
      <span className="d-none d-sm-inline">(</span>
      <small className="d-none d-md-inline text-muted me-1 text-uppercase">Publisher: </small>
      <div className={`d-inline me-1 ${styles.tinyIcon}`}>{repo.userAlias ? <FaUser /> : <MdBusiness />}</div>
      <span>{repo.userAlias || repo.organizationDisplayName || repo.organizationName}</span>
      <span className="d-none d-sm-inline">)</span>
    </small>
  );

  return (
    <Modal
      header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Add opt-out entry</div>}
      open={props.open}
      modalDialogClassName={styles.modal}
      closeButton={
        <button
          className="btn btn-sm btn-outline-secondary"
          type="button"
          disabled={isNull(repoItem) || isSending}
          onClick={submitForm}
          aria-label="Add opt-out entry"
        >
          {isSending ? (
            <>
              <span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
              <span className="ms-2">Opting out</span>
            </>
          ) : (
            <div className="d-flex flex-row align-items-center text-uppercase">
              <IoMdLogOut className="me-2" />
              <div>Opt-out</div>
            </div>
          )}
        </button>
      }
      onClose={onCloseModal}
      error={apiError}
      cleanError={() => setApiError(null)}
      noScrollable
    >
      <div className="w-100 position-relative">
        <label className={`form-label fw-bold ${styles.label}`} htmlFor="kind">
          Events
        </label>
        <div className="d-flex flex-column flex-wrap pb-2">
          {REPOSITORY_SUBSCRIPTIONS_LIST.map((subs: SubscriptionItem) => {
            return (
              <div className="mb-2" key={`radio_${subs.name}`}>
                <div className="form-check text-nowrap my-1 my-md-0">
                  <input
                    className="form-check-input"
                    type="radio"
                    name="kind"
                    id={subs.name}
                    value={subs.kind}
                    disabled={!subs.enabled}
                    checked={subs.kind === eventKind}
                    onChange={() => setEventKind(subs.kind)}
                    required
                  />
                  <label className="form-check-label" htmlFor={subs.name}>
                    <div className="d-flex flex-row align-items-center ms-2">
                      {subs.icon}
                      <div className="ms-1">{subs.title}</div>
                    </div>
                  </label>
                </div>
              </div>
            );
          })}
        </div>

        <div className="d-flex flex-column mb-3">
          <label className={`form-label fw-bold ${styles.label}`} htmlFor="description">
            Repository
          </label>

          <small className="mb-2">Select repository:</small>

          {!isNull(repoItem) ? (
            <div
              data-testid="activeRepoItem"
              className={`border border-secondary w-100 rounded mt-1 ${styles.repoWrapper}`}
            >
              <div className="d-flex flex-row flex-nowrap align-items-stretch justify-content-between">
                <div className="flex-grow-1 text-truncate py-2">
                  <div className="d-flex flex-row align-items-center h-100 text-truncate">
                    <div className="d-none d-md-inline">
                      <RepositoryIcon kind={repoItem.kind} className={`mx-3 ${styles.icon}`} />
                    </div>

                    <div className="ms-2 me-2 me-sm-0 fw-bold mb-0 text-truncate">
                      {repoItem.name}
                      <span className="d-inline d-sm-none">
                        <span className="mx-2">/</span>
                        {getPublisher(repoItem)}
                      </span>
                    </div>

                    <div className="px-2 ms-auto w-50 text-dark text-truncate d-none d-sm-inline">
                      {getPublisher(repoItem)}
                    </div>
                  </div>
                </div>

                <div>
                  <button
                    className={`btn btn-close h-100 rounded-0 border-start px-3 py-0 ${styles.closeButton}`}
                    onClick={() => setRepoItem(null)}
                    aria-label="Close"
                  ></button>
                </div>
              </div>
            </div>
          ) : (
            <div className={`mt-2 ${styles.searchWrapper}`}>
              <SearchRepositories
                label="repo-subscriptions"
                disabledRepositories={{
                  ids: props.disabledList,
                }}
                extraQueryParams={{ user: ctx.user ? [ctx.user.alias] : [], org: userOrganizations || [] }}
                onSelection={onRepoSelect}
                onAuthError={props.onAuthError}
                visibleUrl={false}
              />
            </div>
          )}
        </div>
      </div>
    </Modal>
  );
}
Example #2
Source File: index.tsx    From hub with Apache License 2.0 4 votes vote down vote up
RepositoriesSection = (props: Props) => {
  const title = useRef<HTMLDivElement>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [optOutList, setOptOutList] = useState<OptOutByRepo[] | undefined>(undefined);
  const [optOutFullList, setOptOutFullList] = useState<OptOutByRepo[] | undefined>(undefined);
  const [repoIdsList, setRepoIdsList] = useState<string[]>([]);
  const [optOutItems, setOptOutItems] = useState<OptOutItem[] | undefined>(undefined);
  const [modalStatus, setModalStatus] = useState<boolean>(false);
  const [activePage, setActivePage] = useState<number>(1);

  const calculateOffset = (pageNumber?: number): number => {
    return DEFAULT_LIMIT * ((pageNumber || activePage) - 1);
  };

  const [offset, setOffset] = useState<number>(calculateOffset());
  const [total, setTotal] = useState<number | undefined>(undefined);

  const onPageNumberChange = (pageNumber: number): void => {
    setOffset(calculateOffset(pageNumber));
    setActivePage(pageNumber);
    if (title && title.current) {
      title.current.scrollIntoView({ block: 'start', inline: 'nearest', behavior: 'smooth' });
    }
  };

  const getNotificationTitle = (kind: EventKind): string => {
    let title = '';
    const notif = REPOSITORY_SUBSCRIPTIONS_LIST.find((subs: SubscriptionItem) => subs.kind === kind);
    if (!isUndefined(notif)) {
      title = notif.title.toLowerCase();
    }
    return title;
  };

  const getVisibleOptOut = (items: OptOutByRepo[]): OptOutByRepo[] => {
    if (isUndefined(items)) return [];
    return items.slice(offset, offset + DEFAULT_LIMIT);
  };

  const sortOptOutList = (items: OptOutItem[]): OptOutByRepo[] => {
    let list: OptOutByRepo[] = [];

    items.forEach((item: OptOutItem) => {
      const itemIndex = list.findIndex(
        (obr: OptOutByRepo) => obr.repository.repositoryId === item.repository.repositoryId
      );
      if (itemIndex >= 0) {
        list[itemIndex] = { ...list[itemIndex], optOutItems: [...list[itemIndex].optOutItems, item] };
      } else {
        list.push({
          repository: item.repository,
          optOutItems: [item],
        });
      }
    });

    return sortBy(list, 'repository.name');
  };

  async function getOptOutList(callback?: () => void) {
    try {
      setIsLoading(true);
      const items = await API.getAllOptOut();
      const formattedItems = sortOptOutList(items);
      setOptOutItems(items);
      setOptOutFullList(formattedItems);
      setRepoIdsList(formattedItems ? formattedItems.map((item: OptOutByRepo) => item.repository.repositoryId!) : []);
      setTotal(formattedItems.length);
      const newVisibleItems = getVisibleOptOut(formattedItems);
      // When current page is empty after changes
      if (newVisibleItems.length === 0 && activePage !== 1) {
        onPageNumberChange(1);
      } else {
        setOptOutList(newVisibleItems);
      }
      setIsLoading(false);
    } catch (err: any) {
      setIsLoading(false);
      if (err.kind !== ErrorKind.Unauthorized) {
        alertDispatcher.postAlert({
          type: 'danger',
          message: 'An error occurred getting your opt-out entries list, please try again later.',
        });
        setOptOutFullList([]);
        setOptOutList([]);
      } else {
        props.onAuthError();
      }
    } finally {
      if (callback) {
        callback();
      }
    }
  }

  async function changeSubscription(changeProps: ChangeSubsProps) {
    const { data, callback } = { ...changeProps };
    try {
      if (!isUndefined(data.optOutId)) {
        await API.deleteOptOut(data.optOutId);
      } else {
        await API.addOptOut(data.repoId, data.kind);
      }
      getOptOutList(callback);
    } catch (err: any) {
      callback();
      if (err.kind !== ErrorKind.Unauthorized) {
        alertDispatcher.postAlert({
          type: 'danger',
          message: `An error occurred ${
            !isUndefined(data.optOutId) ? 'deleting' : 'adding'
          } the opt-out entry for ${getNotificationTitle(data.kind)} notifications for repository ${
            data.repoName
          }, please try again later.`,
        });
        getOptOutList(); // Get opt-out if changeSubscription fails
      } else {
        props.onAuthError();
      }
    }
  }

  useEffect(() => {
    getOptOutList();
  }, []); /* eslint-disable-line react-hooks/exhaustive-deps */

  useEffect(() => {
    setOptOutList(getVisibleOptOut(optOutFullList || []));
  }, [activePage]); /* eslint-disable-line react-hooks/exhaustive-deps */

  return (
    <div className="mt-5 pt-3">
      {(isUndefined(optOutList) || isLoading) && <Loading />}
      <div className="d-flex flex-row align-items-start justify-content-between pb-2">
        <div ref={title} className={`h4 pb-0 ${styles.title}`}>
          Repositories
        </div>
        <div>
          <button
            className={`btn btn-outline-secondary btn-sm text-uppercase ${styles.btnAction}`}
            onClick={() => setModalStatus(true)}
            aria-label="Open opt-out modal"
          >
            <div className="d-flex flex-row align-items-center justify-content-center">
              <IoMdLogOut />
              <span className="d-none d-md-inline ms-2">Opt-out</span>
            </div>
          </button>
        </div>
      </div>

      <div className="mt-3 mt-md-3">
        <p>
          Repositories notifications are <span className="fw-bold">enabled by default</span>. However, you can opt-out
          of notifications for certain kinds of events that happen in any of the repositories you can manage.
        </p>

        <p>
          You will <span className="fw-bold">NOT</span> receive notifications when an event that matches any of the
          repositories in the list is fired.
        </p>

        <div className="mt-4 mt-md-5">
          {!isUndefined(optOutList) && optOutList.length > 0 && (
            <div className="row">
              <div className="col-12 col-xxxl-10">
                <table className={`table table-bordered table-hover ${styles.table}`} data-testid="repositoriesList">
                  <thead>
                    <tr className={styles.tableTitle}>
                      <th scope="col" className={`align-middle text-center d-none d-sm-table-cell ${styles.fitCell}`}>
                        Kind
                      </th>
                      <th scope="col" className="align-middle w-50">
                        Repository
                      </th>
                      <th scope="col" className="align-middle w-50 d-none d-sm-table-cell">
                        Publisher
                      </th>
                      {REPOSITORY_SUBSCRIPTIONS_LIST.map((subs: SubscriptionItem) => (
                        <th
                          scope="col"
                          className={`align-middle text-nowrap ${styles.fitCell}`}
                          key={`title_${subs.kind}`}
                        >
                          <div className="d-flex flex-row align-items-center justify-content-center">
                            {subs.icon}
                            {subs.shortTitle && <span className="d-inline d-lg-none ms-2">{subs.shortTitle}</span>}
                            <span className="d-none d-lg-inline ms-2">{subs.title}</span>
                          </div>
                        </th>
                      ))}
                    </tr>
                  </thead>
                  <tbody className={styles.body}>
                    {optOutList.map((item: OptOutByRepo) => {
                      const repoInfo: Repository = item.repository;
                      return (
                        <tr key={`subs_${repoInfo.repositoryId}`} data-testid="optOutRow">
                          <td className="align-middle text-center d-none d-sm-table-cell">
                            <RepositoryIcon kind={repoInfo.kind} className={`h-auto ${styles.icon}`} />
                          </td>
                          <td className="align-middle">
                            <div className="d-flex flex-row align-items-center">
                              <Link
                                data-testid="repoLink"
                                className="text-dark text-capitalize"
                                to={{
                                  pathname: '/packages/search',
                                  search: prepareQueryString({
                                    pageNumber: 1,
                                    filters: {
                                      repo: [repoInfo.name],
                                    },
                                  }),
                                }}
                              >
                                {repoInfo.name}
                              </Link>
                            </div>
                          </td>
                          <td className="align-middle position-relative d-none d-sm-table-cell">
                            <span className={`mx-1 mb-1 ${styles.tinyIcon}`}>
                              {repoInfo.userAlias ? <FaUser /> : <MdBusiness />}
                            </span>{' '}
                            {repoInfo.userAlias ? (
                              <Link
                                data-testid="userLink"
                                className="text-dark"
                                to={{
                                  pathname: '/packages/search',
                                  search: prepareQueryString({
                                    pageNumber: 1,
                                    filters: {
                                      user: [repoInfo.userAlias!],
                                    },
                                  }),
                                }}
                              >
                                {repoInfo.userAlias}
                              </Link>
                            ) : (
                              <Link
                                data-testid="orgLink"
                                className="text-dark"
                                to={{
                                  pathname: '/packages/search',
                                  search: prepareQueryString({
                                    pageNumber: 1,
                                    filters: {
                                      org: [repoInfo.organizationName!],
                                    },
                                  }),
                                }}
                              >
                                {repoInfo.organizationDisplayName || repoInfo.organizationName}
                              </Link>
                            )}
                          </td>
                          {REPOSITORY_SUBSCRIPTIONS_LIST.map((subs: SubscriptionItem, index: number) => {
                            const optItem = item.optOutItems.find((opt: OptOutItem) => subs.kind === opt.eventKind);

                            return (
                              <td className="align-middle text-center" key={`td_${repoInfo.name}_${subs.kind}`}>
                                <div className="text-center position-relative">
                                  <SubscriptionSwitch
                                    repoInfo={repoInfo}
                                    kind={subs.kind}
                                    enabled={subs.enabled}
                                    optOutItem={optItem}
                                    changeSubscription={changeSubscription}
                                  />
                                </div>
                              </td>
                            );
                          })}
                        </tr>
                      );
                    })}
                    {!isUndefined(total) && total > DEFAULT_LIMIT && (
                      <tr className={styles.paginationCell}>
                        <td className="align-middle text-center" colSpan={5}>
                          <Pagination
                            limit={DEFAULT_LIMIT}
                            offset={offset}
                            total={total}
                            active={activePage}
                            className="my-3"
                            onChange={onPageNumberChange}
                          />
                        </td>
                      </tr>
                    )}
                  </tbody>
                </table>
              </div>
            </div>
          )}
        </div>
      </div>

      {modalStatus && (
        <OptOutModal
          disabledList={repoIdsList}
          optOutList={optOutItems}
          onSuccess={getOptOutList}
          onClose={() => setModalStatus(false)}
          onAuthError={props.onAuthError}
          getNotificationTitle={getNotificationTitle}
          open
        />
      )}
    </div>
  );
}