FollowerCollection.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import { FC, useEffect, useState } from 'react';
  2. import { Pagination, Spin } from 'antd';
  3. import { ErrorBoundary } from 'react-error-boundary';
  4. import { Follower } from '../../../../interfaces/follower';
  5. import { SingleFollower } from '../SingleFollower/SingleFollower';
  6. import styles from './FollowerCollection.module.scss';
  7. import { FollowButton } from '../../../action-buttons/FollowButton';
  8. import { ComponentError } from '../../ComponentError/ComponentError';
  9. export type FollowerCollectionProps = {
  10. name: string;
  11. onFollowButtonClick: () => void;
  12. };
  13. export const FollowerCollection: FC<FollowerCollectionProps> = ({ name, onFollowButtonClick }) => {
  14. const ENDPOINT = '/api/followers';
  15. const ITEMS_PER_PAGE = 24;
  16. const [followers, setFollowers] = useState<Follower[]>([]);
  17. const [total, setTotal] = useState(0);
  18. const [page, setPage] = useState(1);
  19. const [loading, setLoading] = useState(true);
  20. const getFollowers = async () => {
  21. try {
  22. setLoading(true);
  23. const response = await fetch(
  24. `${ENDPOINT}?offset=${(page - 1) * ITEMS_PER_PAGE}&limit=${ITEMS_PER_PAGE}`,
  25. );
  26. const data = await response.json();
  27. const { results, total: totalResults } = data;
  28. setFollowers(results);
  29. setTotal(totalResults);
  30. setLoading(false);
  31. } catch (error) {
  32. console.error(error);
  33. }
  34. };
  35. useEffect(() => {
  36. getFollowers();
  37. }, [page]);
  38. const noFollowers = (
  39. <div className={styles.noFollowers} id="followers-collection">
  40. <h2>Be the first follower!</h2>
  41. <p>
  42. {name !== 'Owncast' ? name : 'This server'} is a part of the{' '}
  43. <a href="https://owncast.online/join-fediverse">Fediverse</a>, an interconnected network of
  44. independent users and servers.
  45. </p>
  46. <p>
  47. By following {name !== 'Owncast' ? name : 'this server'} you&apos;ll be able to get updates
  48. from the stream, share it with others, and show your appreciation when it goes live, all
  49. from your own Fediverse account.
  50. </p>
  51. <FollowButton onClick={onFollowButtonClick} />
  52. </div>
  53. );
  54. if (!followers?.length && !loading) {
  55. return noFollowers;
  56. }
  57. return (
  58. <ErrorBoundary
  59. // eslint-disable-next-line react/no-unstable-nested-components
  60. fallbackRender={({ error, resetErrorBoundary }) => (
  61. <ComponentError
  62. componentName="FollowerCollection"
  63. message={error.message}
  64. retryFunction={resetErrorBoundary}
  65. />
  66. )}
  67. >
  68. <Spin spinning={loading} size="large">
  69. <div className={styles.followers} id="followers-collection">
  70. <div className={styles.followerRow}>
  71. {followers.map(follower => (
  72. <SingleFollower key={follower.link} follower={follower} />
  73. ))}
  74. </div>
  75. <Pagination
  76. className={styles.pagination}
  77. current={page}
  78. pageSize={ITEMS_PER_PAGE}
  79. defaultPageSize={ITEMS_PER_PAGE}
  80. total={total}
  81. showSizeChanger={false}
  82. onChange={p => {
  83. setPage(p);
  84. }}
  85. hideOnSinglePage
  86. />
  87. </div>
  88. </Spin>
  89. </ErrorBoundary>
  90. );
  91. };