From 5a9967db119bdad32bc48f297a3db6eec4ee2c2f Mon Sep 17 00:00:00 2001 From: xaralis <filip.varecha@fragaria.cz> Date: Tue, 15 Dec 2020 14:02:36 +0100 Subject: [PATCH] feat: favicons, page desc, support removing likes/dislikes --- public/index.html | 23 ++++++++---- src/App.jsx | 2 +- src/actions/posts.js | 62 +++++++++++++++++++++++++++++++ src/components/Footer.jsx | 2 +- src/components/Navbar.jsx | 2 +- src/components/Thumbs.jsx | 12 +++--- src/components/posts/Post.jsx | 2 +- src/containers/PostsContainer.jsx | 32 ++++++++++++++-- 8 files changed, 117 insertions(+), 20 deletions(-) diff --git a/public/index.html b/public/index.html index 5d168a8..88f032b 100644 --- a/public/index.html +++ b/public/index.html @@ -2,14 +2,21 @@ <html lang="en"> <head> <meta charset="utf-8" /> - <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> + <!-- Favicons --> + <link rel="apple-touch-icon" href="%REACT_APP_STYLEGUIDE_URL%images/favicons/favicon-196x196.png"> + <link rel="icon" type="image/png" href="%REACT_APP_STYLEGUIDE_URL%images/favicons/favicon-196x196.png" sizes="196x196"> + <meta name="application-name" content="CF2021"> + <meta name="msapplication-TileColor" content="#000000"> + <meta name="msapplication-TileImage" content="%REACT_APP_STYLEGUIDE_URL%images/favicons/mstile-144x144.png"> + <meta name="msapplication-square70x70logo" content="%REACT_APP_STYLEGUIDE_URL%images/favicons/mstile-70x70.png"> + <meta name="msapplication-square150x150logo" content="%REACT_APP_STYLEGUIDE_URL%images/favicons/mstile-150x150.png"> + <meta name="msapplication-wide310x150logo" content="%REACT_APP_STYLEGUIDE_URL%images/favicons/mstile-310x150.png"> + <meta name="msapplication-square310x310logo" content="%REACT_APP_STYLEGUIDE_URL%images/favicons/mstile-310x310.png"> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> - <meta - name="description" - content="Web site created using create-react-app" - /> - <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> + <meta property="og:url" content="https://cf2021.pirati.cz/" /> + <meta property="og:type" content="website" /> + <meta property="og:title" content="CF 2021" /> <!-- manifest.json provides metadata used when your web app is installed on a user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ @@ -24,8 +31,8 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - <link rel="stylesheet" href="%REACT_APP_STYLEGUIDE_URL%/css/styles.css" /> - <title>React App</title> + <link rel="stylesheet" href="%REACT_APP_STYLEGUIDE_URL%css/styles.css" /> + <title>CF 2021 | Pirátská strana</title> </head> <body class="h-screen"> <noscript>You need to enable JavaScript to run this app.</noscript> diff --git a/src/App.jsx b/src/App.jsx index f608dce..d972113 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -41,7 +41,7 @@ const LoadingComponent = ( <div className="h-screen w-screen flex justify-center items-center"> <div className="text-center"> <img - src={`${process.env.REACT_APP_STYLEGUIDE_URL}/images/logo-round-black.svg`} + src={`${process.env.REACT_APP_STYLEGUIDE_URL}images/logo-round-black.svg`} className="w-20 mb-2 inline-block" alt="Pirátská strana" /> diff --git a/src/actions/posts.js b/src/actions/posts.js index f196e0c..6d15bd7 100644 --- a/src/actions/posts.js +++ b/src/actions/posts.js @@ -34,6 +34,37 @@ export const like = createAsyncAction( } ); +export const removeLike = createAsyncAction( + /** + * @param {CF2021.Post} post + */ + async (post) => { + return successResult(post); + }, + { + shortCircuitHook: ({ args }) => { + if (args.ranking.myVote !== "like") { + return errorResult(); + } + + return false; + }, + postActionHook: ({ result }) => { + if (!result.error) { + PostStore.update((state) => { + state.items[result.payload.id].ranking.likes -= 1; + state.items[result.payload.id].ranking.score -= 1; + state.items[result.payload.id].ranking.myVote = "none"; + + if (state.filters.sort === "byScore") { + updateWindowPosts(state); + } + }); + } + }, + } +); + export const dislike = createAsyncAction( /** * @param {CF2021.Post} post @@ -65,6 +96,37 @@ export const dislike = createAsyncAction( } ); +export const removeDislike = createAsyncAction( + /** + * @param {CF2021.Post} post + */ + async (post) => { + return successResult(post); + }, + { + shortCircuitHook: ({ args }) => { + if (args.ranking.myVote !== "dislike") { + return errorResult(); + } + + return false; + }, + postActionHook: ({ result }) => { + if (!result.error) { + PostStore.update((state) => { + state.items[result.payload.id].ranking.dislikes -= 1; + state.items[result.payload.id].ranking.score += 1; + state.items[result.payload.id].ranking.myVote = "none"; + + if (state.filters.sort === "byScore") { + updateWindowPosts(state); + } + }); + } + }, + } +); + /** * Add new discussion post. */ diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index 8034cf3..f911e02 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -6,7 +6,7 @@ const Footer = () => { <div className="footer__main py-4 lg:py-16 container container--default"> <section className="footer__brand"> <img - src="https://www.va-fighters.com/pirati/krajska-sablona/dist/assets/img/logo.svg" + src={`${process.env.REACT_APP_STYLEGUIDE_URL}images/logo-full-white.svg`} alt="" className="w-32 md:w-40 pb-6" /> diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index 1a908b2..f7557e3 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -24,7 +24,7 @@ const Navbar = () => { <div className="navbar__brand my-4 flex items-center lg:pr-8 lg:my-0"> <NavLink to="/"> <img - src={`${process.env.REACT_APP_STYLEGUIDE_URL}/images/logo-round-white.svg`} + src={`${process.env.REACT_APP_STYLEGUIDE_URL}images/logo-round-white.svg`} className="w-8" alt="Pirátská strana" /> diff --git a/src/components/Thumbs.jsx b/src/components/Thumbs.jsx index c267eee..33f81d8 100644 --- a/src/components/Thumbs.jsx +++ b/src/components/Thumbs.jsx @@ -1,15 +1,16 @@ import React from "react"; import classNames from "classnames"; -const Thumbs = ({ likes, dislikes, onLike, onDislike, canThumb }) => { +const Thumbs = ({ likes, dislikes, onLike, onDislike, myVote }) => { return ( <div> <div className="space-x-2 text-sm flex items-center"> <button className={classNames("text-blue-300 flex items-center space-x-1", { - "cursor-not-allowed": !canThumb, + "cursor-pointer": myVote === "none" || myVote === "like", + "cursor-default": myVote === "dislike", })} - disabled={!canThumb} + disabled={myVote === "dislike"} onClick={onLike} > <span className="font-bold">{likes}</span> @@ -17,9 +18,10 @@ const Thumbs = ({ likes, dislikes, onLike, onDislike, canThumb }) => { </button> <button className={classNames("text-red-600 flex items-center space-x-1", { - "cursor-not-allowed": !canThumb, + "cursor-pointer": myVote === "none" || myVote === "dislike", + "cursor-default": myVote === "like", })} - disabled={!canThumb} + disabled={myVote === "like"} onClick={onDislike} > <i className="ico--thumbs-down transform -scale-x-1"></i> diff --git a/src/components/posts/Post.jsx b/src/components/posts/Post.jsx index 3a76326..88c6f53 100644 --- a/src/components/posts/Post.jsx +++ b/src/components/posts/Post.jsx @@ -153,7 +153,7 @@ const Post = ({ dislikes={ranking.dislikes} onLike={onLike} onDislike={onDislike} - canThumb={ranking.myVote === "none"} + myVote={ranking.myVote} /> {displayActions && ( <DropdownMenu right> diff --git a/src/containers/PostsContainer.jsx b/src/containers/PostsContainer.jsx index ca4187e..f1e7fea 100644 --- a/src/containers/PostsContainer.jsx +++ b/src/containers/PostsContainer.jsx @@ -1,7 +1,7 @@ import React from "react"; import pick from "lodash/pick"; -import { dislike, like } from "actions/posts"; +import { dislike, like, removeDislike, removeLike } from "actions/posts"; import PostList from "components/posts/PostList"; import { PostStore } from "stores"; @@ -16,6 +16,32 @@ const PostsContainer = ({ className }) => { // const onLike = (post) => like.run(); // const onDislike = (post) => console.log("dislike", post); + /** + * + * @param {CF2021.Post} post + */ + const onLike = (post) => { + if (post.ranking.myVote === "none") { + return like.run(post); + } + if (post.ranking.myVote === "like") { + return removeLike.run(post); + } + }; + + /** + * + * @param {CF2021.Post} post + */ + const onDislike = (post) => { + if (post.ranking.myVote === "none") { + return dislike.run(post); + } + if (post.ranking.myVote === "dislike") { + return removeDislike.run(post); + } + }; + const sliceStart = (window.page - 1) * window.perPage; const sliceEnd = window.page * window.perPage; @@ -40,8 +66,8 @@ const PostsContainer = ({ className }) => { items={window.items .slice(sliceStart, sliceEnd) .map((postId) => items[postId])} - onLike={like.run} - onDislike={dislike.run} + onLike={onLike} + onDislike={onDislike} className={className} dimArchived={!showingArchivedOnly} displayActions={true} -- GitLab