GameListView.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import React from "react";
  2. import { Link, useLocation } from "react-router-dom";
  3. import { useTranslation } from "react-i18next";
  4. import { getGames } from "../utils/api";
  5. import Account from "../components/Account";
  6. import useAuth from "../hooks/useAuth";
  7. import "react-confirm-alert/src/react-confirm-alert.css";
  8. import logo from "../images/logo-mono.png";
  9. import header from "../images/header.jpg";
  10. import useLocalStorage from "../hooks/useLocalStorage";
  11. import styled from "styled-components";
  12. const Header = styled.div`
  13. height: 15em;
  14. padding-top: 1em;
  15. margin-bottom: 2em;
  16. background-color: var(--bg-secondary-color);
  17. background-image: url(${header});
  18. position: relative;
  19. & .baseline {
  20. font-family: "Merriweather Sans", sans-serif;
  21. text-align: center;
  22. position: absolute;
  23. bottom: 0px;
  24. background-color: #00000099;
  25. width: 100%;
  26. margin: 0;
  27. }
  28. & .login {
  29. float: right;
  30. margin-right: 0.5em;
  31. }
  32. & .login button {
  33. background-color: var(--color-primary);
  34. }
  35. & .new-game {
  36. position: absolute;
  37. right: 0.5em;
  38. top: 4em;
  39. background-color: var(--color-secondary);
  40. }
  41. `;
  42. const Brand = styled.div`
  43. background-color: var(--color-secondary);
  44. display: flex;
  45. width: 550px;
  46. align-items: center;
  47. padding: 0 1em;
  48. & h1 {
  49. font-size: 4em;
  50. margin: 0;
  51. padding: 0;
  52. line-height: 75px;
  53. margin-left: 0em;
  54. letter-spacing: -4px;
  55. font-weight: bold;
  56. padding-left: 0.2em;
  57. }
  58. & img {
  59. height: 55px;
  60. margin-top: 8px;
  61. }
  62. `;
  63. const GameView = styled.div``;
  64. const GameList = styled.ul`
  65. width: 960px;
  66. list-style: none;
  67. margin: 0;
  68. margin: 0 auto;
  69. padding: 0 2em;
  70. display: flex;
  71. flex-wrap: wrap;
  72. flex-direction: row;
  73. `;
  74. const Game = styled.li`
  75. width: 100%;
  76. background-color: var(--bg-secondary-color);
  77. position: relative;
  78. min-width: 250px;
  79. max-width: 440px;
  80. height: 150px;
  81. padding: 0.5em;
  82. margin: 0.3em;
  83. flex: 1 1 0%;
  84. width: & .game-name {
  85. margin: 0 1em;
  86. }
  87. & .button.play {
  88. margin: 0 2px;
  89. background-color: var(--color-secondary);
  90. }
  91. & .play {
  92. position: absolute;
  93. bottom: 0.5em;
  94. right: 0.5em;
  95. }
  96. & .extra-actions {
  97. position: absolute;
  98. top: 0.5em;
  99. right: 0.5em;
  100. display: none;
  101. }
  102. &:hover .extra-actions {
  103. display: block;
  104. }
  105. `;
  106. const useQuery = () => {
  107. return new URLSearchParams(useLocation().search);
  108. };
  109. const GameListView = () => {
  110. const { t } = useTranslation();
  111. const [isBeta, setIsBeta] = useLocalStorage("isBeta", false);
  112. let query = useQuery();
  113. const [gameList, setGameList] = React.useState([]);
  114. const { isAuthenticated, userId } = useAuth();
  115. React.useEffect(() => {
  116. getGames().then((content) => {
  117. setGameList(content);
  118. });
  119. }, [isAuthenticated]);
  120. const forceBeta = query.get("beta") === "true";
  121. React.useEffect(() => {
  122. if (forceBeta) {
  123. setIsBeta(true);
  124. }
  125. }, [forceBeta, setIsBeta]);
  126. /*const handleRemove = (idToRemove) => async () => {
  127. confirmAlert({
  128. title: t("Confirmation"),
  129. message: t("Do you really want to remove selected items ?"),
  130. buttons: [
  131. {
  132. label: t("Yes"),
  133. onClick: () => {
  134. deleteGame(idToRemove);
  135. setGameList(gameList.filter(({ id }) => id !== idToRemove));
  136. },
  137. },
  138. {
  139. label: t("No"),
  140. onClick: () => {},
  141. },
  142. ],
  143. });
  144. };*/
  145. return (
  146. <GameView>
  147. <Header>
  148. {isBeta && isAuthenticated && (
  149. <Link to={`/game/`} className="button new-game">
  150. {t("Create new game")}
  151. </Link>
  152. )}
  153. {isBeta && <Account className="login" />}
  154. <Brand className="brand">
  155. <a href="/">
  156. <img src={logo} alt="logo" />
  157. </a>
  158. <h1>Air Board Game</h1>
  159. </Brand>
  160. <h2 className="baseline">
  161. {t("Play your favorite games online with your friends")}
  162. </h2>
  163. </Header>
  164. <GameList>
  165. {gameList
  166. .filter(
  167. ({ published, owner }) =>
  168. published || (userId && (!owner || owner === userId))
  169. )
  170. .map(({ name, id, owner, published }) => (
  171. <Game key={id}>
  172. <h2 className="game-name">
  173. {name} {!published && "(Private)"}
  174. </h2>
  175. <Link to={`/game/${id}/session/`} className="button play">
  176. {t("Play")}
  177. </Link>
  178. {isBeta && userId && (userId === owner || !owner) && (
  179. <div className="extra-actions">
  180. <Link
  181. to={`/game/${id}/edit`}
  182. className="button edit icon-only"
  183. >
  184. <img
  185. src="https://icongr.am/feather/edit.svg?size=16&color=ffffff"
  186. alt={t("Edit")}
  187. />
  188. </Link>
  189. </div>
  190. )}
  191. </Game>
  192. ))}
  193. </GameList>
  194. </GameView>
  195. );
  196. };
  197. export default GameListView;