From 162038fceb4eee9a156df6163c0530c7bc2d2049 Mon Sep 17 00:00:00 2001 From: Elisei Nicolae Date: Sat, 16 Apr 2022 23:04:10 +0300 Subject: [PATCH] Elisei/improve UI (#7) * truncate text * update gitignore. * Start the radio when switch from a station to other one. * update UI - fix cut off 'g' - fix height of header * make the play button & volume icon a bit smaller * Header: truncate text & fix style. * made responsive. * minor fix responsive --- .gitignore | 3 + TODO.md | 62 ++++++++--------- frontend/.idea/.gitignore | 5 ++ .../Container/Container.module.scss | 4 +- frontend/components/Footer/Footer.module.scss | 8 +-- .../StationCategories.module.scss | 5 ++ .../StationHeader/StationHeader.module.scss | 19 +++++- .../StationHeader/StationHeader.tsx | 26 +++++--- .../StationInformation.module.scss | 14 +++- .../StationInformation/StationInformation.tsx | 11 ++-- .../StationPlayer/StationPlayer.module.scss | 66 ++++++++++++++----- .../StationPlayer/StationPlayer.tsx | 8 +-- .../components/StationPlayer/plyr.module.scss | 19 +++--- .../components/Stations/Stations.module.scss | 25 ++++++- frontend/hooks/useWindowSize.tsx | 30 +++++++++ frontend/pages/index.tsx | 4 +- frontend/public/circle_matrix_mobile.svg | 3 + frontend/styles/globals.scss | 2 + 18 files changed, 223 insertions(+), 91 deletions(-) create mode 100644 frontend/.idea/.gitignore create mode 100644 frontend/hooks/useWindowSize.tsx create mode 100644 frontend/public/circle_matrix_mobile.svg diff --git a/.gitignore b/.gitignore index 09bf71c..c2dcc46 100644 --- a/.gitignore +++ b/.gitignore @@ -387,3 +387,6 @@ data nginx/nginx.conf /frontend/node_modules hls-streaming/docker-compose.yml +frontend/.idea/frontend.iml +frontend/.idea/modules.xml +frontend/.idea/vcs.xml diff --git a/TODO.md b/TODO.md index dd75c6a..bbb3ea2 100644 --- a/TODO.md +++ b/TODO.md @@ -1,35 +1,35 @@ Frontend: -- make the play button & volume icon a bit smaller && make the volume slider width constant -- implement random station functionality -- when selecting a station, the player should start automatically -- implement add to favorite functionality -- implement stations groups -- sort all stations to left -- make the stations thumbnail a bit smaller -- add current played song as head meta (maybe Google will pick them and display in search results) -- create a page for each station (and simulate a page redirect when clicking on a station) -- create a sitemap with all the stations -- create a subdomain for each station (just the player + the latest articles) -- allow the user to add a shortcut on desktop for the app -- create am embeddable player -- add SEO meta for all the reviews -- make the website mobile responsive -- refresh the station metadata every 5 seconds -- implement add to favorite each station -- optimize the website for maximum performance on web.dev -- implement HLS player on website -- populate station description from API -- allow the user to leave a review (/api/v1/review) -- create a pop-up so that the user can share Radio Crestin with their friends -- enable server side rendering and push the website to a very fast CDN -- link each station to the correct Facebook page by using the link from API -- improve the website audio player to allow the user to select which stream should be played for that station (hls/proxy/original) - - when a stream is failing, fall-back to the next stream automatically -- when a station is playing, send this signal to backend (send station_id every 15 seconds) -- add facebook page as SEO meta field -- SEO meta field - current playing -- send the listened station every 1 minute (/api/v1/listen) -- sum up to station listeners the radio_crestin_listeners value when the user is listening using the HLS or proxy +- [X] make the play button & volume icon a bit smaller && make the volume slider width constant +- [ ] implement random station functionality +- [X] when selecting a station, the player should start automatically +- [ ] implement add to favorite functionality +- [ ] implement stations groups +- [ ] sort all stations to left +- [ ] make the stations thumbnail a bit smaller +- [ ] add current played song as head meta (maybe Google will pick them and display in search results) +- [ ] create a page for each station (and simulate a page redirect when clicking on a station) +- [ ] create a sitemap with all the stations +- [ ] create a subdomain for each station (just the player + the latest articles) +- [ ] allow the user to add a shortcut on desktop for the app +- [ ] create am embeddable player +- [ ] add SEO meta for all the reviews +- [ ] make the website mobile responsive +- [ ] refresh the station metadata every 5 seconds +- [ ] implement add to favorite each station +- [ ] optimize the website for maximum performance on web.dev +- [ ] implement HLS player on website +- [ ] populate station description from API +- [ ] allow the user to leave a review (/api/v1/review) +- [ ] create a pop-up so that the user can share Radio Crestin with their friends +- [ ] enable server side rendering and push the website to a very fast CDN +- [ ] link each station to the correct Facebook page by using the link from API +- [ ] improve the website audio player to allow the user to select which stream should be played for that station (hls/proxy/original) +- [ ] when a stream is failing, fall-back to the next stream automatically +- [ ] when a station is playing, send this signal to backend (send station_id every 15 seconds) +- [ ] add facebook page as SEO meta field +- [ ] SEO meta field - current playing +- [ ] send the listened station every 1 minute (/api/v1/listen) +- [ ] sum up to station listeners the radio_crestin_listeners value when the user is listening using the HLS or proxy Backend: - incorporate multiple audio sources for each station (hls, proxy, original stream) diff --git a/frontend/.idea/.gitignore b/frontend/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/frontend/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/frontend/components/Container/Container.module.scss b/frontend/components/Container/Container.module.scss index 52e59ac..e54de8c 100644 --- a/frontend/components/Container/Container.module.scss +++ b/frontend/components/Container/Container.module.scss @@ -6,9 +6,11 @@ width: 90%; margin: 0 auto; - @include mixinHelper.onTablet { width: 100%; margin: 0; } + @include mixinHelper.onMobile{ + background: #ffd8af; + } } diff --git a/frontend/components/Footer/Footer.module.scss b/frontend/components/Footer/Footer.module.scss index ecf107e..dbc20e6 100644 --- a/frontend/components/Footer/Footer.module.scss +++ b/frontend/components/Footer/Footer.module.scss @@ -7,14 +7,12 @@ font-weight: 500; font-size: 18px; color: #646464; - margin-top: 150px; - margin-bottom: 82px; + padding-top: 150px; + padding-bottom: 82px; flex-wrap: wrap; @include mixinHelper.onMobile{ - margin-top: 50px; - margin-bottom: 30px; - padding: 0 30px; + padding: 50px 30px 30px; gap: 10px 20px; } } diff --git a/frontend/components/StationCategories/StationCategories.module.scss b/frontend/components/StationCategories/StationCategories.module.scss index e0078b2..c255ee8 100644 --- a/frontend/components/StationCategories/StationCategories.module.scss +++ b/frontend/components/StationCategories/StationCategories.module.scss @@ -9,6 +9,7 @@ overflow-x: scroll; justify-content: start; padding-left: 30px; + padding-right: 30px; padding-bottom: 20px; } @@ -29,6 +30,10 @@ padding: 5px 10px; } + @include mixinHelper.onMobile{ + background: white; + } + &:first-child{ background: #F9AC59;; } diff --git a/frontend/components/StationHeader/StationHeader.module.scss b/frontend/components/StationHeader/StationHeader.module.scss index 0f9cfd0..94b2a92 100644 --- a/frontend/components/StationHeader/StationHeader.module.scss +++ b/frontend/components/StationHeader/StationHeader.module.scss @@ -5,7 +5,6 @@ flex-direction: column; margin: 2rem auto; padding: 36px; - min-height: 520px; background: #FFD9AF; border-radius: 25px; box-shadow: 1px 2px 18px 0 #0000001A; @@ -17,6 +16,10 @@ border-radius: 0; } + @include mixinHelper.onMobile{ + box-shadow: none; + } + .row { display: flex; width: 100%; @@ -25,13 +28,15 @@ .currentStation { display: flex; - gap: 0 30px; height: 100%; z-index: 0; - margin-top: 50px; + gap: 50px; + padding: 0 50px; @include mixinHelper.onTablet{ flex-direction: column-reverse; + padding: 0; + gap: 0; } } } @@ -43,6 +48,14 @@ left: 0; z-index: -1; border-bottom-left-radius: 25px; + + @include mixinHelper.onTablet{ + width: 700px; + } + + @include mixinHelper.onMobile{ + width: 340px; + } } diff --git a/frontend/components/StationHeader/StationHeader.tsx b/frontend/components/StationHeader/StationHeader.tsx index 970a429..a9aa361 100644 --- a/frontend/components/StationHeader/StationHeader.tsx +++ b/frontend/components/StationHeader/StationHeader.tsx @@ -5,9 +5,10 @@ import RandomStationButton from "@/components/RandomStationButton/RandomStationB import StationInformation from "@/components/StationInformation/StationInformation"; import styles from "./StationHeader.module.scss"; import dynamic from "next/dynamic"; -import { useStations } from "../../hooks/stations"; import Circle_matrix_desktop from "@/public/circle_matrix_desktop.svg"; +import Circle_matrix_mobile from "@/public/circle_matrix_mobile.svg"; import { Station } from "../../types"; +import useWindowSize from "../../hooks/useWindowSize"; export const StationPlayer = dynamic( () => import("components/StationPlayer/StationPlayer"), @@ -18,7 +19,8 @@ export const StationPlayer = dynamic( export default function StationHeader(station: Station) { const [showChild, setShowChild] = useState(false); - const [started, setStarted] = useState(false); + const [started, setStarted] = useState(true); + const window = useWindowSize(); useEffect(() => { setShowChild(true); @@ -32,7 +34,7 @@ export default function StationHeader(station: Station) {
- + {window.width > 767 && }
setStarted(false)} /> + + + + fullSymbol + - {"fullSymbol"}
); diff --git a/frontend/components/StationInformation/StationInformation.module.scss b/frontend/components/StationInformation/StationInformation.module.scss index cc0c4a8..126d178 100644 --- a/frontend/components/StationInformation/StationInformation.module.scss +++ b/frontend/components/StationInformation/StationInformation.module.scss @@ -1,9 +1,16 @@ @use 'styles/mixinHelper'; +@mixin truncateText { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + .container { display: flex; flex-direction: column; - max-width: 60%; + justify-content: center; + max-width: 45%; @include mixinHelper.onTablet { max-width: unset; @@ -13,9 +20,14 @@ .station { &_Name { + @include truncateText; font-style: normal; font-weight: 700; font-size: 50px; + + @include mixinHelper.onMobile{ + font-size: 30px; + } } &_RatingContainer { diff --git a/frontend/components/StationInformation/StationInformation.tsx b/frontend/components/StationInformation/StationInformation.tsx index a43bf11..0387878 100644 --- a/frontend/components/StationInformation/StationInformation.tsx +++ b/frontend/components/StationInformation/StationInformation.tsx @@ -61,12 +61,11 @@ export default function StationInformation(props: any) { /> - {/*@ts-ignore*/} - {NumberOfListeners && ( -

- {NumberOfListeners} persoane ascultă împreună cu tine acest radio -

- )} +

+ {NumberOfListeners} persoane ascultă împreună cu tine acest radio +

”Nu numai că trebuie să ne ferim să producem dezbinare, ci trebuie să devenim agenți ai păcii, străduindu-ne să reconciliem părțile aflate în diff --git a/frontend/components/StationPlayer/StationPlayer.module.scss b/frontend/components/StationPlayer/StationPlayer.module.scss index 3ce7770..d34c2ab 100644 --- a/frontend/components/StationPlayer/StationPlayer.module.scss +++ b/frontend/components/StationPlayer/StationPlayer.module.scss @@ -1,13 +1,18 @@ @use 'styles/globalVariables'; @use 'styles/mixinHelper'; -.containerPlayer { - display: flex; +@mixin truncateText { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + max-width: 425px; +} + +.contentHeaderLeft { color: black; border-radius: 15px; - margin-left: 50px; margin-right: auto; - min-width: 450px; + width: 50%; gap: 20px; @include mixinHelper.onSmallDesktop{ @@ -20,15 +25,26 @@ padding: 18px 0; width: unset; } + @include mixinHelper.onMobile{ + min-width: 0; + } - .descriptionSong { + .container { position: relative; display: flex; flex-direction: column; align-self: flex-end; + height: 100%; + justify-content: end; @include mixinHelper.onTablet { flex-direction: row; + justify-content: unset; + gap: 0 50px; + } + + @include mixinHelper.onMobile{ + flex-direction: column; } .songImage { @@ -37,22 +53,38 @@ height: 200px; border-radius: 30px; filter: drop-shadow(2px 2px 5px rgba(0, 0, 0, 0.25)); - } - .songName { - margin: 15px 0 5px; - font-size: 2.3em; - font-weight: 800; + @include mixinHelper.onMobile{ + margin: 0 auto; + } } - .artist { - font-size: 1.5em; - font-weight: 300; - margin-top: 10px; - } + .descriptionSong { + display: flex; + flex-direction: column; + height: 205px; + + @include mixinHelper.onMobile{ + height: 250px; + } + + .songName { + @include truncateText; + height: 50px; + margin: 15px 0 5px; + font-size: 2.3em; + font-weight: 800; + } + + .artist { + @include truncateText; + font-size: 1.5em; + font-weight: 300; + } - .containerPlyr { - margin-top: 45px; + .containerPlyr { + margin-top: auto; + } } } } diff --git a/frontend/components/StationPlayer/StationPlayer.tsx b/frontend/components/StationPlayer/StationPlayer.tsx index 4136a70..e1ff5b0 100644 --- a/frontend/components/StationPlayer/StationPlayer.tsx +++ b/frontend/components/StationPlayer/StationPlayer.tsx @@ -107,18 +107,16 @@ export default function StationPlayer(props: { // TODO: make the player mobile responsive // TODO: normalize the sound volume (100% - 10db, 70% - 7db, etc) - console.log(station); - return ( <> -

-
+
+
Image station -
+

{station.now_playing?.song?.name}

diff --git a/frontend/components/StationPlayer/plyr.module.scss b/frontend/components/StationPlayer/plyr.module.scss index c242d4c..4d8b80c 100644 --- a/frontend/components/StationPlayer/plyr.module.scss +++ b/frontend/components/StationPlayer/plyr.module.scss @@ -20,14 +20,14 @@ border-radius: 38px !important; background: black !important; color: #FFB05A !important; - width: 68px; - height: 68px; + width: 55px; + height: 55px; transition-duration: 0s; margin-right: 0; } &:first-child[aria-label="Pause"] { - padding: 22px !important; + padding: 17px !important; svg { width: 19px !important; @@ -40,18 +40,21 @@ } svg { - width: 25px; - height: 25px; + width: 20px; + height: 20px; + margin-left: 2px; } } .plyr__volume { - width: 100%; + width: 60%; max-width: unset !important; + margin-right: auto; input[type=range] { cursor: pointer; color: black !important; + width: 150px; &::-moz-range-thumb { background: black; @@ -65,8 +68,8 @@ } svg{ - width: 40px; - height: 40px; + width: 34px; + height: 34px; margin: 0 15px; } } diff --git a/frontend/components/Stations/Stations.module.scss b/frontend/components/Stations/Stations.module.scss index 0f55457..dbd9aff 100644 --- a/frontend/components/Stations/Stations.module.scss +++ b/frontend/components/Stations/Stations.module.scss @@ -7,18 +7,33 @@ flex-flow: wrap; gap: 30px; width: 100%; + + @include mixinHelper.onMobile{ + gap: 15px; + padding: 0 20px; + } } .station { - width: 250px; + width: 200px; height: 100%; cursor: pointer; + @include mixinHelper.onMobile{ + width: 150px; + } + &_Image { - width: 250px; - height: 250px; + width: 200px; + height: 200px; border-radius: 41px; + background: linear-gradient(180.37deg, rgba(0, 0, 0, 0) 11.16%, rgba(0, 0, 0, 0.7138) 91.38%); filter: drop-shadow(2px 2px 5px rgba(0, 0, 0, 0.25)); + + @include mixinHelper.onMobile{ + width: 150px; + height: 150px; + } } .is_Offline { @@ -32,5 +47,9 @@ font-weight: 500; font-size: 23px; text-align: center; + + @include mixinHelper.onMobile{ + font-size: 18px; + } } } diff --git a/frontend/hooks/useWindowSize.tsx b/frontend/hooks/useWindowSize.tsx new file mode 100644 index 0000000..066d1cf --- /dev/null +++ b/frontend/hooks/useWindowSize.tsx @@ -0,0 +1,30 @@ +// @ts-nocheck +import { useState, useEffect } from "react"; +// Hook +const useWindowSize = () => { + // Initialize state with undefined width/height so server and client renders match + // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/ + const [windowSize, setWindowSize] = useState({ + width: 0, + height: 0, + }); + useEffect(() => { + // Handler to call on window resize + function handleResize() { + // Set window width/height to state + setWindowSize({ + width: window.innerWidth, + height: window.innerHeight, + }); + } + // Add event listener + window.addEventListener("resize", handleResize); + // Call handler right away so state gets updated with initial window size + handleResize(); + // Remove event listener on cleanup + return () => window.removeEventListener("resize", handleResize); + }, []); // Empty array ensures that effect is only run on mount + return windowSize; +}; + +export default useWindowSize; diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index eeaeac9..5aca067 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -24,10 +24,9 @@ export default function Home(initialProps: { }); const [selectedStationId, selectStationId] = useLocalStorageState( - -1, + 3, "SELECTED_STATION_ID", ); - const [started, setStarted] = useState(false); const selectedStation = stations.find(s => s.id === selectedStationId); const onStationSelect = (station: Station) => { @@ -36,7 +35,6 @@ export default function Home(initialProps: { station, selectedStationId !== station.id, ); - setStarted(selectedStationId !== station.id ? true : !started); selectStationId(station.id); }; diff --git a/frontend/public/circle_matrix_mobile.svg b/frontend/public/circle_matrix_mobile.svg new file mode 100644 index 0000000..d971f98 --- /dev/null +++ b/frontend/public/circle_matrix_mobile.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/styles/globals.scss b/frontend/styles/globals.scss index 89b6118..7212e7f 100644 --- a/frontend/styles/globals.scss +++ b/frontend/styles/globals.scss @@ -18,7 +18,9 @@ time, mark, audio, video { margin: 0; padding: 0; border: 0; + box-sizing: border-box; vertical-align: baseline; + line-height: initial; } article, aside, details, figcaption, figure,