From 0c98ad8f614f13d0fcbab979565f9a744105530a Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Tue, 29 Oct 2019 15:53:09 +0100 Subject: [PATCH 01/20] Move current container width to separate single variable --- pages/index/Bubbles.tsx | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/pages/index/Bubbles.tsx b/pages/index/Bubbles.tsx index 693d44d..3c7e3f7 100644 --- a/pages/index/Bubbles.tsx +++ b/pages/index/Bubbles.tsx @@ -1,5 +1,6 @@ import React, {useEffect, useRef, useState} from 'react'; import styled from 'styled-components'; +//TODO: add react-konva as lazy import { Layer, Stage } from 'react-konva'; import Bubble, { BubbleConfig } from './Bubble'; import { getRandomInt, helper, Theme } from '../../utils'; @@ -43,31 +44,37 @@ const getBubbleProps = ({ width, height }: Size): BubbleConfig => { const Bubbles: React.FunctionComponent = ({ handleClickBubble, ...props }) => { const containerRef = useRef<{offsetWidth: number}>(null); + //TODO: to utils.js + const [containerWidth, setContainerWidth] = useState(); + useEffect(() => { + const reflow = () => { + if(!containerRef.current) return + setContainerWidth(containerRef.current.offsetWidth) + }; + + reflow() + window.addEventListener('resize', reflow, true); + return () => window.removeEventListener('resize', reflow, true); + }, []); + //TODO: break size & bubbles to separate effects const [bubbles, setBubbles] = useState([]); const [size, setSize] = useState(); - const reflow = () => { - if(!containerRef.current) return - const width = containerRef.current.offsetWidth; + useEffect(() => { + if(!containerWidth) return const breakpointIndex = Theme.breakpoints.findIndex( breakpoint => window.innerWidth < parseInt(breakpoint), ); const height = CONTAINER_HEIGHTS[breakpointIndex] ? CONTAINER_HEIGHTS[breakpointIndex] : CONTAINER_HEIGHTS[CONTAINER_HEIGHTS.length - 1]; - setSize({ width, height }); + setSize({ width: containerWidth, height }); - const bubblesLength = Math.floor((width / 10575) * 10000); + const bubblesLength = Math.floor((containerWidth / 10575) * 10000); setBubbles(helper(0, bubblesLength)) - }; - - useEffect(() => { - if (!size) reflow(); - window.addEventListener('resize', reflow, true); - return () => window.removeEventListener('resize', reflow, true); - }, []); + }, [containerWidth]); return ( From 1a19e506f93394edeca50ccc79887d703ad22fe0 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Mon, 28 Oct 2019 19:38:20 +0100 Subject: [PATCH 02/20] Handle undefined window for blog post --- pages/blog/[id].tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pages/blog/[id].tsx b/pages/blog/[id].tsx index 270d41f..0c99fc0 100644 --- a/pages/blog/[id].tsx +++ b/pages/blog/[id].tsx @@ -90,6 +90,7 @@ const PostContent: React.FunctionComponent = ({ createdAt, updatedAt, cover, + slug, hashtags = [], ...props }) => ( @@ -102,7 +103,7 @@ const PostContent: React.FunctionComponent = ({ openGraph={{ title, description, - url: window.location.href, + url: `https://luck.org.pl/blog/${slug}`, images: [{ url: cover.url }], article: { publishedTime: createdAt, @@ -142,6 +143,7 @@ export const POST_QUERY = gql` isDraft createdAt updatedAt + slug cover { url } From 3dfe9f28e020e431aff34dec342dd001ed8935cd Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Fri, 1 Nov 2019 13:30:09 +0100 Subject: [PATCH 03/20] Created bot-politics page --- components/Typography/DynamicComponent.tsx | 2 + pages/bot-politics/index.tsx | 212 +++++++++++++++++++++ server/server.ts | 7 +- utils/theme.tsx | 9 + 4 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 pages/bot-politics/index.tsx diff --git a/components/Typography/DynamicComponent.tsx b/components/Typography/DynamicComponent.tsx index 1a22ac3..7274610 100644 --- a/components/Typography/DynamicComponent.tsx +++ b/components/Typography/DynamicComponent.tsx @@ -14,9 +14,11 @@ import { size, space, textStyle, + width, } from 'styled-system'; export const StyledDynamicComponent = styled('div')` + ${width} ${space} ${fontSize} ${fontStyle} diff --git a/pages/bot-politics/index.tsx b/pages/bot-politics/index.tsx new file mode 100644 index 0000000..c444433 --- /dev/null +++ b/pages/bot-politics/index.tsx @@ -0,0 +1,212 @@ +import React from 'react'; +import BlogLayout from '../blog/BlogLayout'; +import { Theme } from '../../utils'; +import { NextSeo } from 'next-seo/lib'; +import styled from 'styled-components'; +import {Canon, Flex, MediumText} from '../../components'; +import media from '../../utils/media'; + +const List = styled('ul')` + display: block; + margin-block-start: ${Theme.space.medium}px; + margin-block-end: ${Theme.space.medium}px; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: ${Theme.space.xregular}px; +` + +const Container = styled(Flex)` + background-color: ${Theme.colors.main}; + color: ${Theme.colors.black}; + flex-wrap: wrap; + max-width: 1012px; + margin: 0 auto; + padding: ${Theme.space.small}px ${Theme.space.xregular}px; + + ${media.greaterThan('mobile')` + + `} + + ${media.greaterThan('tablet')` + padding: ${Theme.space.regular}px ${Theme.space.xregular}px; + `} + + ${media.greaterThan('desktop')` + max-width: 1138px; + padding: ${Theme.space.xregular}px ${Theme.space.xregular}px; + `} +`; + +const Text = styled(MediumText).attrs({ + mt: ['small', 'small', 'medium', 'regular'] +})`` + +const Link = styled('a')` + color: ${Theme.colors.darkGray}; + + text-decoration: underline; + text-decoration-color: transparent; + transition: text-decoration-color 0.5s; + + &:hover { + text-decoration-color: ${Theme.colors.darkGray}; + } +` + +const Index = () => { + return ( + <> + + + + Polityka prywatności bota Pan Mikołaj + + Zespół LUCK, jako twórca aplikacji LUCK, ułatwiającej organizowanie + losowań prezentów, w swoich działaniach szanuje Twoją prywatność. Chronimy + prywatność osób korzystających z bota Pan Mikołaj na platformie Google Asystent, + w sposób opisany poniżej. + + + Niniejsza Polityka prywatności pomaga zrozumieć, jakie dane osobowe oraz inne informacje + gromadzimy w celu ulepszenia naszych usług, a jednocześnie informujemy o tym, jak te + dane są wykorzystywane dla ułatwienia korzystania z oferowanych usług przez naszych + użytkowników. + + + „Dane osobowe" oznaczają informacje, które umożliwiają jednoznaczną + identyfikację użytkowników jako indywidualnych osób fizycznych. W ramach + świadczenia usługi, możemy również zbierać inne informacje, których nie + można przypisać żadnej konkretnej osobie, a przez to nie można zidentyfikować + indywidualnych użytkowników. By ulepszać nasze usługi, zbieramy i wykorzystujemy + tylko drugi rodzaj danych. + + +
  • + + Administratorem Twoich danych osobowych jest zespół LUCK zwaną dalej: + „Administratorem”. Z Administratorem danych można skontaktować się + poprzez adres email  + +
  • +
  • + + Administrator wyznaczył inspektora ochrony danych, z którym można + skontaktować się poprzez email info@luck.org.pl. Z Inspektorem ochrony danych można + się kontaktować we wszystkich sprawach dotyczących przetwarzania danych osobowych + oraz korzystania z praw związanych z przetwarzaniem danych. + +
  • +
  • + + Twoje dane osobowe przetwarzane są w związku z komunikacją przy użyciu bota. Twoje + dane mogą być także przetwarzane, jeśli udzieliłeś odpowiedniej zgody, w celu + prowadzenie komunikacji marketingowej, czyli wysyłki wiadomości z materiałami + dotyczącymi aplikacji LUCK. + +
  • +
  • + + Zbieramy i przetwarzamy dane, które otrzymujemy z następujących + źródeł: + +
  • + +
  • + + informacje przekazane nam przez Ciebie poza Google Asystentem (np. w trakcie + kontaktu z zespołem LUCK); + +
  • +
  • + dane przekazane za pomocą bota. +
  • +
    +
  • + + Przetwarzamy za pomocą usługi bota następujące dane osobowe naszych + użytkowników: + +
  • + +
  • + ID rozmowy, +
  • +
  • + płeć, +
  • +
  • + strefa czasowa, +
  • +
  • + preferowany język. +
  • +
    +
  • + + Dane osobowe przetwarzane są na podstawie art. 6 ust. 1 lit. f) RODO, co oznacza że + przetwarzanie jest niezbędne do realizacje prawnie uzasadnionego interesu + administratora danych, polegającego na komunikacji z użytkownikami za pomocą + narzędzia bot, na platformie Google Asystent, oraz prowadzenia marketingu + bezpośredniego do użytkowników bota po wyrażeniu przez Ciebie zgody na + otrzymywanie wiadomości marketingowych od zespołu LUCK, zawierających informacje o + naszych usługach. + +
  • +
  • + Dane osobowe nie będą przetwarzane na podstawie art. 6 ust. 1 lit. b) RODO. +
  • +
  • + + Masz prawo do żądania od Administratora dostępu do treści Twoich danych osobowych + oraz ich sprostowania, usunięcia, ograniczenia przetwarzania oraz ich przenoszenia. + Masz prawo do wniesienia sprzeciwu wobec przetwarzania danych, w sytuacji kiedy + przetwarzania opiera się na prawnie uzasadnionym interesie administratora. Możesz + cofniąć wyrażoną zgodę w dowolnym momencie, bez wpływu na zgodność z prawem + przetwarzania, którego dokonano przed cofnięciem zgody. W przypadku + stwierdzenia, iż przetwarzanie Twoich danych osobowych następuje niezgodnie z RODO i + ustawą o ochronie danych osobowych, masz prawo do wniesienia skargi do Prezesa + Urzędu Ochrony Danych Osobowych. + +
  • +
  • + + Odrębnym Administratorem Twoich danych jest również Google Inc., który + przetwarza Twoja dane niezależnie od celów i podstaw prawnych przetwarzania. + Twoje dane będą przetwarzane przez Google Inc. w zależności od zaakceptowanych przez + Ciebie regulaminów Google i wyrażonych na rzecz Google zgód. + +
  • +
  • + + Twoje dane osobowe będą przetwarzane do czasu skutecznego złożenia sprzeciwu wobec + przetwarzania danych osobowych lub wycofania udzielonej zgody, będącej podstawą + przetwarzania. Dane przetwarzane w celu dochodzenia lub obrony przed roszczeniami + będą przetwarzane przez czas równy okresowi przedawnienia tych roszczeń. + +
  • +
  • + + Nie przekazujemy Twoich danych osobowych poza teren Europejskiego Obszaru + Gospodarczego, za wyjątkiem ponadnarodowego charakteru przepływu danych w ramach + Google Asystenta, stosowanie do zasad określonych przez Google pod adresem: + https://policies.google.com/privacy?hl=pl + +
  • +
  • + + Jednocześnie chcielibyśmy poinformować Cię, że zespół LUCK może zaktualizować + niniejszą Politykę prywatności. O wszystkich zmianach zostaniesz poinformowany + jeżeli zostawisz swój kontakt. Zmiany dotyczące praw użytkownika nigdy nie + będą dokonywane bez uprzedniej prośby o zgodę na ich akceptację. + +
  • +
    +
    +
    + + ); +}; +export default Index; diff --git a/server/server.ts b/server/server.ts index 7934a50..3bb5ec8 100644 --- a/server/server.ts +++ b/server/server.ts @@ -22,7 +22,12 @@ app.prepare().then(() => { const parsedUrl = parse(req.url, true); const { pathname, query } = parsedUrl; - if (pathname === '/posts' || pathname === '/posty') { + console.log(pathname); + if (pathname === '/polityka-prywatnosci-bota-pan-mikolaj') { + app.render(req, res, '/bot-politics', query); + } else if (pathname === '/polityka-prywatnosci-strony') { + app.render(req, res, '/webiste-politics', query); + } else if (pathname === '/posts' || pathname === '/posty') { app.render(req, res, '/blog', query); } else if (pathname === '/robots.txt') { res.statusCode = 200; diff --git a/utils/theme.tsx b/utils/theme.tsx index 3f22cf1..31b1fca 100644 --- a/utils/theme.tsx +++ b/utils/theme.tsx @@ -21,6 +21,7 @@ export const colors = { main: "#f9f9f9", mainContrast: "#d45858", black: "#212121", + darkGray: "#4d4d4d", gray: "#bababa", }; @@ -31,48 +32,56 @@ export const fontFamilies = { export const textStyles = { logoHeading: { tag: "h1", + width: "100%", fontSize: [27, 27, 46, 61], fontWeight: 300, fontFamily: fontFamilies.body }, canon: { tag: "h1", + width: "100%", fontSize: [20, 20, 42, 48], fontWeight: 700, fontFamily: fontFamilies.body }, trafalgar: { tag: "h1", + width: "100%", fontSize: [18, 18, 18, 21], fontWeight: 700, fontFamily: fontFamilies.body }, bodyText: { tag: "p", + width: "100%", fontSize: [16, 16, 18, 21], fontWeight: 400, fontFamily: fontFamilies.body }, mediumText: { tag: "p", + width: "100%", fontSize: [16, 16, 16, 18], fontWeight: 400, fontFamily: fontFamilies.body }, smallText: { tag: "p", + width: "100%", fontSize: [14, 14, 14, 14], fontWeight: 400, fontFamily: fontFamilies.body }, tinySecond: { tag: "p", + width: "100%", fontSize: [10, 10, 12, 14], fontWeight: 400, fontFamily: fontFamilies.body }, tinyText: { tag: "p", + width: "100%", fontSize: [10, 10, 10, 11], fontWeight: 400, fontFamily: fontFamilies.body From d1aab62a60fd23227994e38bce610297bd45d758 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Fri, 1 Nov 2019 13:46:41 +0100 Subject: [PATCH 04/20] Created website-politics page --- pages/webiste-politics/index.tsx | 390 +++++++++++++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 pages/webiste-politics/index.tsx diff --git a/pages/webiste-politics/index.tsx b/pages/webiste-politics/index.tsx new file mode 100644 index 0000000..4f617f2 --- /dev/null +++ b/pages/webiste-politics/index.tsx @@ -0,0 +1,390 @@ +import React from 'react'; +import BlogLayout from '../blog/BlogLayout'; +import { Theme } from '../../utils'; +import { NextSeo } from 'next-seo/lib'; +import styled from 'styled-components'; +import { Canon, Flex, MediumText } from '../../components'; +import media from '../../utils/media'; + +const List = styled('ul')` + display: block; + margin-block-start: ${Theme.space.medium}px; + margin-block-end: ${Theme.space.medium}px; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: ${Theme.space.xregular}px; +`; + +const Container = styled(Flex)` + background-color: ${Theme.colors.main}; + color: ${Theme.colors.black}; + flex-wrap: wrap; + max-width: 1012px; + margin: 0 auto; + padding: ${Theme.space.small}px ${Theme.space.xregular}px; + + ${media.greaterThan('mobile')` + + `} + + ${media.greaterThan('tablet')` + padding: ${Theme.space.regular}px ${Theme.space.xregular}px; + `} + + ${media.greaterThan('desktop')` + max-width: 1138px; + padding: ${Theme.space.xregular}px ${Theme.space.xregular}px; + `} +`; + +const Text = styled(MediumText).attrs({ + mt: ['small', 'small', 'medium', 'regular'], +})``; + +const Link = styled('a')` + color: ${Theme.colors.darkGray}; + + text-decoration: underline; + text-decoration-color: transparent; + transition: text-decoration-color 0.5s; + + &:hover { + text-decoration-color: ${Theme.colors.darkGray}; + } +`; + +const Index = () => { + return ( + <> + + + + Polityka prywatności strony + OŚWIADCZENIE O OCHRONIE DANYCH OSOBOWYCH + § 1 Informacja dotycząca zbierania danych osobowych + + (1) Poniżej informujemy o zbieraniu danych osobowych podczas korzystania z naszej strony + internetowej. Dane osobowe to wszystkie dane, które można odnieść do konkretnej + osoby, np. nazwisko, adres, adres e-mail, zachowanie użytkownika. + + + (2) Podmiotem odpowiedzialnym zgodnie z art. 4 ust. 7 Ogólnego rozporządzenia o + ochronie danych (RODO) jest zespół LUCK,{' '} + info@luck.org.pl. Z naszym pełnomocnikiem do + spraw ochrony danych osobowych można skontaktować się przez e-mail lub tradycyjną pocztą + z dopiskiem „Pełnomocnik ds. ochrony danych osobowych”. + + + (3) W przypadku nawiązania kontaktu z nami przez e-mail lub formularz kontaktowy podane + przez Państwa dane (Państwa adres e-mail, ew. nazwisko, stanowisko i numer telefonu) są + przez nas zapisywane, aby możliwe było udzielenie informacji na Państwa zapytanie. + Uzyskane w ten sposób dane są przez nas usuwane, kiedy ich przechowywanie nie + jest już konieczne, lub ograniczamy ich przetwarzanie, jeśli istnieje ustawowy obowiązek + przechowywania. + + + (4) Jeśli w przypadku poszczególnych funkcji naszej strony sięgamy po + zatrudnionych usługodawców lub chcemy wykorzystywać Państwa dane do celów + reklamowych, w dalszej części zostaną Państwo poinformowani o szczegółach każdej + procedury. Wskazujemy przy tym także określone kryteria okresu przechowywania danych. + + § 2 Przysługujące Państwu prawa + + (1) W odniesieniu do Państwa danych osobowych przysługują Państwu następujące prawa + wobec nas: + + +
  • + prawo do informacji, +
  • +
  • + prawo do poprawiania lub usuwania, +
  • +
  • + prawo do ograniczenia przetwarzania, +
  • +
  • + prawo do sprzeciwu wobec przetwarzania danych osobowych, +
  • +
  • + prawo do przenoszenia danych +
  • +
    + + (2) Ponadto mają Państwo prawo wnieść skargę do organu nadzorczego w związku z + przetwarzaniem przez nas Państwa danych osobowych. + + + § 3 Zbieranie danych osobowych podczas wizyty na naszej stronie internetowej + + + (1) Kiedy strona internetowa jest wykorzystywana jedynie w celu uzyskania informacji, to + znaczy jeśli nie rejestrują się Państwo lub nie przekazują nam Państwo informacji w inny + sposób, zbieramy tylko te dane osobowe, które są przekazywane na nasz + serwer przez Państwa przeglądarkę. Jeśli chcą Państwo przeglądać naszą stronę + internetową, zbieramy następujące dane, które są nam potrzebne ze względów + technicznych, aby wyświetlać naszą stronę internetową oraz zagwarantować jej stabilność + i bezpieczeństwo (podstawę prawną stanowi art. 6 ust. 1 z. 1 lit. f RODO): + + +
  • + adres IP +
  • +
  • + data i godzina zapytania +
  • +
  • + różnica strefy czasowej w stosunku do Greenwich Mean Time (GMT) +
  • +
  • + treść żądania (konkretna strona) +
  • +
  • + status dostępu/ kod odpowiedzi HTTP +
  • +
  • + przesyłana ilość danych +
  • +
  • + strona internetowa, z której pochodzi żądanie +
  • +
  • + przeglądarka +
  • +
  • + system operacyjny i jego interfejs +
  • +
  • + język i wersja oprogramowania przeglądarki. +
  • +
    + + (2) Dodatkowo, oprócz wyżej wymienionych danych, podczas korzystania z naszej + strony internetowej na Państwa komputerze zapisywane są pliki cookies. Pliki cookies to + niewielkie pliki tekstowe, zapisywane w pamięci Państwa komputera i przyporządkowane do + wykorzystywanej przez Państwa przeglądarki. Dzięki nim do organu, który wysyła + plik cookie (w tym przypadku do nas), docierają określone informacje. Pliki cookies nie + mogą uruchamiać żadnych programów ani przenosić wirusów na Państwa + komputer. Dzięki nim oferta internetowa jest bardziej przyjazna dla użytkownika i + efektywna. + + (3) Zastosowanie plików cookies: + + a) Na tej stronie internetowej wykorzystywane są następujące rodzaje plików + cookies, których zakres i sposób działania zostaną objaśnione poniżej: + + +
  • + transient cookies (szczegóły w b) +
  • +
  • + persistent cookies (szczegóły w c). +
  • +
    + + b) Transient cookies to pliki wykorzystywane czasowo, które są automatycznie + usuwane po zamknięciu przeglądarki. Zaliczają się do nich w szczególności sesyjne + pliki cookies. Zapisują one tzw. identyfikator sesji, umożliwiający przyporządkowanie + różnych zapytań przeglądarki do wspólnej sesji. Dzięki temu możliwe jest + ponowne rozpoznanie Państwa komputera, jeśli powrócą Państwo na naszą stronę + internetową. Sesyjne pliki cookies są usuwane po wylogowaniu się lub zamknięciu + przeglądarki. + + + c) Persistent cookies to trwałe pliki usuwane automatycznie po upływie określonego + czasu, który może być różny w zależności od rodzaju pliku. Pliki cookies + można w każdej chwili usunąć, korzystając z ustawień bezpieczeństwa swojej przeglądarki. + + + d) Ustawienia przeglądarki można dowolnie skonfigurować i np. odrzucić przyjmowanie + plików cookies podmiotów trzecich lub wszystkich plików cookies. + Zwracamy jednak uwagę, że wówczas nie będą Państwo mogli w pełni korzystać ze + wszystkich funkcji tej strony internetowej. + + + e) Wykorzystujemy pliki cookies do tego, by móc zidentyfikować Państwa przy + kolejnej wizycie, jeśli posiadają Państwo u nas konto. W przeciwnym razie konieczne + byłoby ponowne logowanie przy każdej wizycie. + + + f) Wykorzystywane pliki flash cookies nie są rejestrowane przez Państwa przeglądarkę, + lecz przez wtyczkę flash. Ponadto wykorzystujemy HTML5 storage objects, które + zapisywane są na Państwa urządzeniu końcowym. Obiekty te zapisują wymagane dane + niezależnie od wykorzystywanej przez Państwa przeglądarki i nie mają automatycznej daty + upływu. Jeśli nie życzą sobie Państwo przetwarzania plików flash cookies, + konieczne jest zainstalowanie odpowiedniego dodatku, np. Adobe-Flash-Killer-Cookie dla + Google Chrome. Można też zapobiec wykorzystywaniu HTML5 storage objects, ustawiając tryb + prywatny w przeglądarce. Ponadto zalecamy regularne ręczne usuwanie plików + cookies i historii przeglądarki. + + § 4 Sprzeciw lub odwołanie zgody na przetwarzanie danych osobowych + + (1) Jeśli wyrazili Państwo zgodę na przetwarzanie danych osobowych, w każdej chwili mogą + ją Państwo odwołać. Odwołanie zgody wpływa na dopuszczalność przetwarzania Państwa + danych osobowych, gdy wyrazili je Państwo w stosunku do nas. + + + (2) Jeśli przetwarzanie przez nas Państwa danych osobowych opiera się na wyważeniu + interesów, mogą Państwo złożyć sprzeciw wobec przetwarzania. Dotyczy to sytuacji, + gdy przetwarzanie danych nie jest wymagane do wykonania umowy z Państwem, co zostało + szczegółowo opisane w poniższym opisie funkcji. W przypadku złożenia takiego + sprzeciwu prosimy o wyjaśnienie powodów, dla których nie powinniśmy + przetwarzać Państwa danych osobowych w realizowany przez nas sposób. W przypadku + uzasadnionego sprzeciwu sprawdzimy stan rzeczy i zaprzestaniemy przetwarzania danych + osobowych lub je dostosujemy, albo przedstawimy Państwu dostateczne, uzasadnione powody, + na podstawie których będziemy kontynuowali przetwarzanie. + + + Oczywiście mogą Państwo również w każdej chwili wyrazić sprzeciw wobec + przetwarzania Państwa danych osobowych do celów reklamowych i do analizy danych. + O swoim sprzeciwie można nas poinformować, korzystając z poniższych danych + kontaktowych:  + + § 5 Google Analytics + + (1) Ta strona internetowa korzysta z Google Analytics, usługi analizowania usług + internetowych, oferowanej przez Google Inc. („Google”). Google Analytics + wykorzystuje tzw. „Cookies”, pliki tekstowe, które są zapisywane na + Państwa komputerze i umożliwiają analizę korzystania przez Państwa ze strony + internetowej. Uzyskane przez plik cookie informacje dotyczące sposobu korzystania przez + Państwa z tej strony internetowej są zwykle przekazywane na serwer Google w USA i tam + zapisywane. W przypadku aktywowania anonimizacji IP na tej stronie internetowej w + obrębie państw członkowskich Unii Europejskiej lub w innych krajach, które są + stronami Porozumienia o Europejskim Obszarze Gospodarczym, Państwa adres IP zostanie + jednak najpierw skrócony przez Google. Tylko w wyjątkowych przypadkach pełny + adres IP jest przekazywany na serwer Google w USA i tam skracany. Na zlecenie + właściciela tej strony internetowej firma Google wykorzystuje te informacje do analizy + Państwa sposobu korzystania ze strony internetowej, sporządzania raportów + dotyczących aktywności na stronie internetowej oraz realizacji dalszych usług związanych + z wykorzystaniem strony internetowej i Internetu na rzecz właściciela strony + internetowej. + + + (2) Adres IP przekazany przez Państwa przeglądarkę w ramach usługi Google Analytics nie + jest zestawiany przez Google z innymi danymi. + + + (3) Można zapobiec zapisywaniu plików cookies przez odpowiednie ustawienia w + oprogramowaniu przeglądarki. Zwracamy jednak uwagę, że w takim przypadku nie będą + Państwo mogli w pełni korzystać ze wszystkich funkcji tej strony internetowej. Ponadto + mogą Państwo zapobiec pobieraniu danych uzyskanych przez plik cookie i odnoszących się + do Państwa sposobu korzystania ze strony internetowej (łącznie z adresem IP) przez + Google, a także przetwarzaniu tych danych przez Google, jeśli pobiorą Państwo i + zainstalują wtyczkę do przeglądarki, dostępną pod następującym linkiem: + tools.google.com/dlpage/gaoptout. + + + (4) Ta strona internetowa korzysta z Google Analytics z rozszerzeniem + „anonymizeIp()”. Oznacza to, że adresy IP są przetwarzane dalej po + skróceniu, aby wykluczyć możliwość odniesienia ich do konkretnych osób. + Jeśli w przypadku danych osobowych zebranych na Państwa temat możliwe jest odniesienie + ich do konkretnej osoby, takie odniesienie jest od razu wykluczane, a dane osobowe są + niezwłocznie usuwane. + + + (5) Korzystamy z Google Analytics w celu analizowania korzystania z naszej strony + internetowej oraz jej regularnego usprawniania. Dzięki uzyskanym statystykom możemy + poprawiać naszą ofertę i uczynić ją bardziej interesującą dla Państwa jako + użytkowników. W odniesieniu do wyjątkowych przypadków, w których + dane osobowe przekazywane są do USA, Google podporządkowuje się porozumieniu EU-US + Privacy Shield, www.privacyshield.gov/EU-US-Framework. Podstawę prawną dla korzystania z + Google Analytics stanowi art. 6 ust. 1 zd. 1 lit. f) RODO. + + + (6) Informacje dostawcy zewnętrznego: Google Dublin, Google Ireland Ltd., Gordon House, + Barrow Street, Dublin 4, Ireland, fax: +353 (1) 436 1001. Warunki korzystania: + www.google.com/analytics/terms/de.html, informacje dotyczące ochrony danych osobowych: + www.google.com/intl/de/analytics/learn/privacy.html, a także oświadczenie o ochronie + danych osobowych: www.google.de/intl/de/policies/privacy. + + § 6 Zastosowanie wtyczek do portali społecznościowych + + (1) Obecnie stosujemy następujące wtyczki do portali społecznościowych: Facebook. + Korzystamy przy tym z tzw. rozwiązania wymagającego dwóch kliknięć. To oznacza, + że kiedy odwiedzą Państwo naszą stronę internetową, początkowo żadne dane osobowe nie są + przekazywane do dostawców wtyczek. Dostawcę wtyczki można rozpoznać po oznaczeniu + w okienku nad jego inicjałem lub logo. Przycisk umożliwia Państwu bezpośrednią + komunikację z dostawcą wtyczki. Tylko jeśli klikną Państwo to oznaczone pole i w ten + sposób je aktywują, dostawca wtyczki otrzyma informację, że otworzyli Państwo + odnośną stronę z naszej oferty internetowej. Ponadto przekazywane są dane + wyszczególnione w § 3 niniejszego oświadczenia. W przypadku Xing, zgodnie z + informacją dostawcy w Niemczech, adres IP jest anonimizowany od razu po pobraniu. W + wyniku aktywowania wtyczki Państwa dane osobowe są więc przekazywane do danego dostawcy + wtyczki i tam (w przypadku dostawców amerykańskich – w USA) zapisywane. + Ponieważ dostawca wtyczki wykorzystuje do zbierania danych przede wszystkim pliki + cookies, przed kliknięciem szarego okienka zalecamy usunięcie wszystkich plików + cookies w ustawieniach bezpieczeństwa przeglądarki. + + + (2) Nie mamy wpływu na pobierane dane i procedury przetwarzania danych. Nie jest nam + również znany pełen zakres zbierania danych, cele przetwarzania, ani okresy + przechowywania. Nie posiadamy również informacji dotyczących usuwania danych + zebranych przez dostawców wtyczek. + + + (3) Dostawca wtyczki zapisuje zebrane na Państwa temat dane jako profile użytkownika i + wykorzystuje je do celów reklamowych, badania rynku oraz do kształtowania swojej + strony internetowej zgodnie z zapotrzebowaniem. Taka analiza jest przeprowadzana przede + wszystkim (również dla niezalogowanych użytkowników) w celu prezentowania + reklam dostosowanych do zapotrzebowania, a także do informowania innych + użytkowników sieci społecznościowej o Państwa aktywności na naszej stronie + internetowej. Przysługuje Państwu prawo do sprzeciwu wobec tworzenia takich + profilów użytkownika, jednak w celu skorzystania z tego prawa muszą Państwo + zwrócić się do danego dostawcy wtyczki. Poprzez wtyczki oferujemy Państwu + możliwość interakcji z sieciami społecznościowymi oraz innymi użytkownikami, dzięki + czemu możemy ulepszać naszą ofertę i czynić ją bardziej interesującą dla Państwa jako + użytkowników. Podstawę prawną dla korzystania z wtyczek stanowi art. 6 ust. 1 zd. + 1 lit. f) RODO. + + + (4) Przekazywanie danych następuje niezależnie od tego, czy mają Państwo konto u + dostawcy wtyczki i czy są na nim zalogowani. Jeśli są Państwo zalogowani u dostawcy + wtyczki, Państwa dane pobrane u nas zostaną przyporządkowane bezpośrednio do Państwa + konta u dostawcy wtyczki. Jeśli wcisną Państwo aktywowany przycisk i na przykład + skorzystają z linku do strony, dostawca wtyczki zapisuje także tę informację na Państwa + koncie użytkownika i publicznie przekazuje ją Państwa znajomym. Zalecamy, by po + zakończeniu korzystania z serwisu społecznościowego zawsze się wylogować, w + szczególności jednak przed aktywowaniem przycisku, ponieważ w ten sposób + można uniknąć przyporządkowania do Państwa profilu przez tego dostawcę wtyczki. + + + (5) Więcej informacji na temat celu i zakresu zbierania i przetwarzania danych przez + właścicieli wtyczek można uzyskać w następujących oświadczeniach o ochronie danych + osobowych poszczególnych dostawców. Otrzymają tam Państwo również + dalsze informacje dotyczące przysługujących Państwu w związku z tym praw oraz możliwości + dokonania ustawień w celu ochrony swojej sfery prywatnej. + + + (6) Adresy poszczególnych dostawców wtyczek i adres URL do ich + wskazówek dotyczących danych osobowych: + + + a) Google Inc., 1600 Amphitheater Parkway, Mountainview, California 94043, USA; + www.google.com/policies/privacy/partners/. Google podporządkowuje się porozumieniu + EU-US-Privacy-Shield, www.privacyshield.gov/EU-US-Framework. + + + b) Twitter, Inc., 1355 Market St, Suite 900, San Francisco, California 94103, USA; + twitter.com/privacy. Twitter podporządkowuje się porozumieniu EU-US-Privacy-Shield, + www.privacyshield.gov/EU-US-Framework. + + c) Xing AG, Gänsemarkt 43, 20354 Hamburg, DE; www.xing.com/privacy. + + d) LinkedIn Corporation, 2029 Stierlin Court, Mountain View, California 94043, USA; + www.linkedin.com/legal/privacy-policy. LinkedIn podporządkowuje się porozumieniu + EU-US-Privacy-Shield, www.privacyshield.gov/EU-US-Framework. + + + e) Facebook Inc. Hacker Way, Menlo Park, California 94025, USA; + developers.facebook.com/docs/plugins/. Facebook podporządkowuje się porozumieniu + EU-US-Privacy-Shield, www.privacyshield.gov/EU-US-Framework. + +
    +
    + + ); +}; +export default Index; From e88ab8dbb890a196979cc05da474b9bdf05969a9 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Tue, 29 Oct 2019 17:54:16 +0100 Subject: [PATCH 05/20] Move current container width to external library --- package.json | 1 + pages/index/Bubbles.tsx | 17 +++-------------- yarn.lock | 5 +++++ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index c235c1e..1c20837 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "dependencies": { "@apollo/react-hooks": "^3.1.0", "@apollo/react-ssr": "^3.1.0", + "@rehooks/component-size": "^1.0.3", "@types/react-no-ssr": "^1.1.1", "apollo-boost": "^0.4.4", "apollo-link-state": "^0.4.2", diff --git a/pages/index/Bubbles.tsx b/pages/index/Bubbles.tsx index 3c7e3f7..92ec69b 100644 --- a/pages/index/Bubbles.tsx +++ b/pages/index/Bubbles.tsx @@ -1,5 +1,6 @@ import React, {useEffect, useRef, useState} from 'react'; import styled from 'styled-components'; +import useComponentSize from '@rehooks/component-size' //TODO: add react-konva as lazy import { Layer, Stage } from 'react-konva'; import Bubble, { BubbleConfig } from './Bubble'; @@ -43,20 +44,8 @@ const getBubbleProps = ({ width, height }: Size): BubbleConfig => { }; const Bubbles: React.FunctionComponent = ({ handleClickBubble, ...props }) => { - const containerRef = useRef<{offsetWidth: number}>(null); - //TODO: to utils.js - const [containerWidth, setContainerWidth] = useState(); - - useEffect(() => { - const reflow = () => { - if(!containerRef.current) return - setContainerWidth(containerRef.current.offsetWidth) - }; - - reflow() - window.addEventListener('resize', reflow, true); - return () => window.removeEventListener('resize', reflow, true); - }, []); + let containerRef = useRef(null); + let {width: containerWidth} = useComponentSize(containerRef); //TODO: break size & bubbles to separate effects const [bubbles, setBubbles] = useState([]); diff --git a/yarn.lock b/yarn.lock index 9d66851..a7b71b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1011,6 +1011,11 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@rehooks/component-size@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rehooks/component-size/-/component-size-1.0.3.tgz#823eabeb42084893d46d43e3a9d1d0e34252b3cb" + integrity sha512-pnYld+8SSF2vXwdLOqBGUyOrv/SjzwLjIUcs/4c1JJgR0q4E9eBtBfuZMD6zUD51fvSehSsbnlQMzotSmPTXPg== + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" From 9de30c5241816eae984c4c10775ebea7bc5638fe Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 07:29:33 +0100 Subject: [PATCH 06/20] Break size & bubbles to separate effects --- pages/index/Bubbles.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pages/index/Bubbles.tsx b/pages/index/Bubbles.tsx index 92ec69b..f1d6649 100644 --- a/pages/index/Bubbles.tsx +++ b/pages/index/Bubbles.tsx @@ -47,12 +47,10 @@ const Bubbles: React.FunctionComponent = ({ handleClickBubble, ... let containerRef = useRef(null); let {width: containerWidth} = useComponentSize(containerRef); - //TODO: break size & bubbles to separate effects - const [bubbles, setBubbles] = useState([]); const [size, setSize] = useState(); - useEffect(() => { if(!containerWidth) return + const breakpointIndex = Theme.breakpoints.findIndex( breakpoint => window.innerWidth < parseInt(breakpoint), ); @@ -60,6 +58,12 @@ const Bubbles: React.FunctionComponent = ({ handleClickBubble, ... ? CONTAINER_HEIGHTS[breakpointIndex] : CONTAINER_HEIGHTS[CONTAINER_HEIGHTS.length - 1]; setSize({ width: containerWidth, height }); + }, [containerWidth]); + + + const [bubbles, setBubbles] = useState([]); + useEffect(() => { + if(!containerWidth) return const bubblesLength = Math.floor((containerWidth / 10575) * 10000); setBubbles(helper(0, bubblesLength)) From 13bd2e91eff533e9a5be9e4bec87ea00ceea5704 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 07:43:02 +0100 Subject: [PATCH 07/20] Moved Bubbles to seperate catalog and break stage layer from bubble list --- pages/index/Bubbles.tsx | 99 ---------------------------- pages/index/{ => Bubbles}/Bubble.tsx | 4 +- pages/index/Bubbles/BubbleList.tsx | 61 +++++++++++++++++ pages/index/Bubbles/index.tsx | 55 ++++++++++++++++ 4 files changed, 118 insertions(+), 101 deletions(-) delete mode 100644 pages/index/Bubbles.tsx rename pages/index/{ => Bubbles}/Bubble.tsx (95%) create mode 100644 pages/index/Bubbles/BubbleList.tsx create mode 100644 pages/index/Bubbles/index.tsx diff --git a/pages/index/Bubbles.tsx b/pages/index/Bubbles.tsx deleted file mode 100644 index f1d6649..0000000 --- a/pages/index/Bubbles.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React, {useEffect, useRef, useState} from 'react'; -import styled from 'styled-components'; -import useComponentSize from '@rehooks/component-size' -//TODO: add react-konva as lazy -import { Layer, Stage } from 'react-konva'; -import Bubble, { BubbleConfig } from './Bubble'; -import { getRandomInt, helper, Theme } from '../../utils'; - -const CONTAINER_HEIGHTS = [200, 250, 300]; - -export interface Size { - width: number; - height: number; -} - -export interface BubblesProps { - handleClickBubble: any; -} - - -export const Container = styled('div')` - position: fixed; - width: 100%; - bottom: ${props => (props.height ? `${-(props.height / 2)}px` : 0)}; - height: ${props => (props.height ? `${props.height}px` : 0)}; - background: transparent; -`; - -const getTimeProp = (width: number) => { - const timeRatio = width; - const timeStep = Math.round(0.5 * width); - const timeMin = timeRatio - timeStep; - const timeMax = timeRatio + timeStep; - return getRandomInt(timeMin, timeMax); -}; - -const getBubbleProps = ({ width, height }: Size): BubbleConfig => { - const radius = getRandomInt(5, Math.round(0.13333333333333333 * height), 5); - const x = getRandomInt(-width / 2, width / 2, 20); - const y = getRandomInt(- height / 5, 0, 20); - const opacity = 0.6; - - return { radius, x, y, opacity, fill: 'white' }; -}; - -const Bubbles: React.FunctionComponent = ({ handleClickBubble, ...props }) => { - let containerRef = useRef(null); - let {width: containerWidth} = useComponentSize(containerRef); - - const [size, setSize] = useState(); - useEffect(() => { - if(!containerWidth) return - - const breakpointIndex = Theme.breakpoints.findIndex( - breakpoint => window.innerWidth < parseInt(breakpoint), - ); - const height = CONTAINER_HEIGHTS[breakpointIndex] - ? CONTAINER_HEIGHTS[breakpointIndex] - : CONTAINER_HEIGHTS[CONTAINER_HEIGHTS.length - 1]; - setSize({ width: containerWidth, height }); - }, [containerWidth]); - - - const [bubbles, setBubbles] = useState([]); - useEffect(() => { - if(!containerWidth) return - - const bubblesLength = Math.floor((containerWidth / 10575) * 10000); - setBubbles(helper(0, bubblesLength)) - }, [containerWidth]); - - return ( - - {size ? ( - - - {bubbles.map(bubbles => { - return ( - - ); - })} - - - ) : ( -

    Loading...

    - )} -
    - ); -}; - -export default Bubbles; diff --git a/pages/index/Bubble.tsx b/pages/index/Bubbles/Bubble.tsx similarity index 95% rename from pages/index/Bubble.tsx rename to pages/index/Bubbles/Bubble.tsx index ac2702a..a7cb43a 100644 --- a/pages/index/Bubble.tsx +++ b/pages/index/Bubbles/Bubble.tsx @@ -1,8 +1,8 @@ import React from 'react'; import Konva from 'konva'; import { Circle } from 'react-konva'; -import { Size } from './Bubbles'; -import { getRandomInt } from '../../utils'; +import { Size } from './BubbleList'; +import { getRandomInt } from '../../../utils'; export interface BubbleConfig { radius: number; diff --git a/pages/index/Bubbles/BubbleList.tsx b/pages/index/Bubbles/BubbleList.tsx new file mode 100644 index 0000000..9e948e5 --- /dev/null +++ b/pages/index/Bubbles/BubbleList.tsx @@ -0,0 +1,61 @@ +import React, { useEffect, useState } from 'react'; +import Bubble, { BubbleConfig } from './Bubble'; +import { getRandomInt, helper} from '../../../utils'; + +export interface Size { + width: number; + height: number; +} + +interface BubbleListProps { + size: Size; + handleClickBubble: any; +} + +const getTimeProp = (width: number) => { + const timeRatio = width; + const timeStep = Math.round(0.5 * width); + const timeMin = timeRatio - timeStep; + const timeMax = timeRatio + timeStep; + return getRandomInt(timeMin, timeMax); +}; + +const getBubbleProps = ({ width, height }: Size): BubbleConfig => { + const radius = getRandomInt(5, Math.round(0.13333333333333333 * height), 5); + const x = getRandomInt(-width / 2, width / 2, 20); + const y = getRandomInt(-height / 5, 0, 20); + const opacity = 0.6; + + return { radius, x, y, opacity, fill: 'white' }; +}; + +const BubbleList: React.FunctionComponent = ({ size, handleClickBubble }) => { + + const [bubbles, setBubbles] = useState([]); + useEffect(() => { + if (!size.width) return; + + const bubblesLength = Math.floor((size.width / 10575) * 10000); + setBubbles(helper(0, bubblesLength)); + }, [size.width]); + + return ( + <> + {bubbles.map(bubbles => { + return ( + + ); + })} + + ); +}; + +export default BubbleList; diff --git a/pages/index/Bubbles/index.tsx b/pages/index/Bubbles/index.tsx new file mode 100644 index 0000000..ac3c4a4 --- /dev/null +++ b/pages/index/Bubbles/index.tsx @@ -0,0 +1,55 @@ +import React, { useEffect, useRef, useState } from 'react'; +import styled from 'styled-components'; +import useComponentSize from '@rehooks/component-size'; +//TODO: add react-konva as lazy +import { Layer, Stage } from 'react-konva'; +import { Theme } from '../../../utils'; +import BubbleList, { Size } from './BubbleList'; + +const CONTAINER_HEIGHTS = [200, 250, 300]; + +interface IndexProps { + handleClickBubble: any; +} + +export const Container = styled('div')` + position: fixed; + width: 100%; + bottom: ${props => (props.height ? `${-(props.height / 2)}px` : 0)}; + height: ${props => (props.height ? `${props.height}px` : 0)}; + background: transparent; +`; + +const Index: React.FunctionComponent = ({ handleClickBubble, ...props }) => { + let containerRef = useRef(null); + let { width } = useComponentSize(containerRef); + + const [size, setSize] = useState(); + useEffect(() => { + if (!width) return; + + const breakpointIndex = Theme.breakpoints.findIndex( + breakpoint => window.innerWidth < parseInt(breakpoint), + ); + const height = CONTAINER_HEIGHTS[breakpointIndex] + ? CONTAINER_HEIGHTS[breakpointIndex] + : CONTAINER_HEIGHTS[CONTAINER_HEIGHTS.length - 1]; + setSize({ width, height }); + }, [width]); + + return ( + + {size ? ( + + + + + + ) : ( +

    Loading...

    + )} +
    + ); +}; + +export default Index; From 721cd2691f4577b5c66d01b0dd92ab82f0ac5d2d Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 10:51:21 +0100 Subject: [PATCH 08/20] Refactor Bubbles from fixed to absolute position --- components/GlobalStyle.tsx | 3 +++ components/Layout.tsx | 2 +- pages/index/Bubbles/index.tsx | 2 +- pages/index/WelcomeSection/index.tsx | 8 ++++++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/components/GlobalStyle.tsx b/components/GlobalStyle.tsx index 7dab902..94af8e0 100644 --- a/components/GlobalStyle.tsx +++ b/components/GlobalStyle.tsx @@ -7,6 +7,9 @@ const GlobalStyle = createGlobalStyle` margin: 0; padding: 0; } + html, body, #__next { + height: 100%; + } html { background-color: ${Theme.colors.main}; } diff --git a/components/Layout.tsx b/components/Layout.tsx index e9e4d72..91dac9c 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -11,7 +11,7 @@ type LayoutProps = { }; const Container = styled(Box)` margin: 0; - height: 100vh; + height: 100%; `; const InnerContainer = styled('div')<{ isFontLoaded: boolean }>` opacity: ${props => (props.isFontLoaded ? 1 : 0)}; diff --git a/pages/index/Bubbles/index.tsx b/pages/index/Bubbles/index.tsx index ac3c4a4..ac52932 100644 --- a/pages/index/Bubbles/index.tsx +++ b/pages/index/Bubbles/index.tsx @@ -13,7 +13,7 @@ interface IndexProps { } export const Container = styled('div')` - position: fixed; + position: absolute; width: 100%; bottom: ${props => (props.height ? `${-(props.height / 2)}px` : 0)}; height: ${props => (props.height ? `${props.height}px` : 0)}; diff --git a/pages/index/WelcomeSection/index.tsx b/pages/index/WelcomeSection/index.tsx index b15ef50..93e6240 100644 --- a/pages/index/WelcomeSection/index.tsx +++ b/pages/index/WelcomeSection/index.tsx @@ -13,13 +13,17 @@ export interface WelcomeSectionProps { const Background = styled('div')` width: 100%; height: 100%; - position: fixed; + position: absolute; background-image: url(static/bg-bubbles.png); z-index: 0; pointer-events: none; `; -const Container = styled('div')``; +const Container = styled('div')` + height: 100%; + overflow: hidden; + position: relative; +`; const WelcomeSection = ({ handleClickBubble, ...pros }: WelcomeSectionProps) => { return ( From 213651b63594db7a9f2d49b97fa7af78c88c4bd0 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 10:37:35 +0100 Subject: [PATCH 09/20] Made Bubbles lazy --- pages/index/WelcomeSection/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pages/index/WelcomeSection/index.tsx b/pages/index/WelcomeSection/index.tsx index 93e6240..0d70376 100644 --- a/pages/index/WelcomeSection/index.tsx +++ b/pages/index/WelcomeSection/index.tsx @@ -1,10 +1,11 @@ import styled from 'styled-components'; import {LogoHeading, Navigation} from '../../../components'; -import Bubbles from '../Bubbles'; import React from 'react'; import Content from './Content'; import Logo from './Logo'; import InputWithButton from './InputWithButton'; +import dynamic from "next/dynamic"; +const Bubbles = dynamic(() => import("../Bubbles"), {ssr: false}); export interface WelcomeSectionProps { handleClickBubble: any; From 3e2460956636a56f879a46cd4bffa470a0aeb4af Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 08:30:16 +0100 Subject: [PATCH 10/20] Moved Font UseEffect to Context --- components/FontProvider.tsx | 30 ++++++++++++++++++++++++++++ components/GlobalStyle.tsx | 8 +++++++- components/Layout.tsx | 13 +++--------- pages/_app.tsx | 13 +++++++----- pages/index/WelcomeSection/index.tsx | 3 ++- utils/fonts.tsx | 15 -------------- utils/index.tsx | 1 - 7 files changed, 50 insertions(+), 33 deletions(-) create mode 100644 components/FontProvider.tsx delete mode 100644 utils/fonts.tsx diff --git a/components/FontProvider.tsx b/components/FontProvider.tsx new file mode 100644 index 0000000..cc7bc95 --- /dev/null +++ b/components/FontProvider.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import {createContext, useState, useEffect} from 'react'; +export const FontContext = createContext(false); + +const FontFaceObserver = require('fontfaceobserver'); + +const Fonts = async () => { + document.documentElement.className += " fonts-loaded"; + + const lato = new FontFaceObserver('Lato'); + + return await lato.load(); +}; + +const FontProvider: React.FunctionComponent = ({ children }) => { + const [isFontLoaded, setIsFontLoaded] = useState(false); + + useEffect(() => { + let isSubscribed = true + Fonts().then(() => isSubscribed ? setIsFontLoaded(true) : null); + return () => {(isSubscribed = false)}; + }, []); + + return ( + + { children } + + ); +}; +export default FontProvider; diff --git a/components/GlobalStyle.tsx b/components/GlobalStyle.tsx index 94af8e0..19a51b3 100644 --- a/components/GlobalStyle.tsx +++ b/components/GlobalStyle.tsx @@ -13,12 +13,18 @@ const GlobalStyle = createGlobalStyle` html { background-color: ${Theme.colors.main}; } + body { margin: 0; padding: 0; width: 100%; color: ${Theme.colors.main}; - font-family: ${Theme.fontFamilies.body} ; + } + + .fonts-loaded { + body { + font-family: ${Theme.fontFamilies.body} ; + } } a { diff --git a/components/Layout.tsx b/components/Layout.tsx index 91dac9c..89c4935 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -2,9 +2,7 @@ import * as React from 'react'; import Head from 'next/head'; import styled from 'styled-components'; import { Box } from './Box'; -import { useState } from 'react'; -import { useEffect } from 'react'; -import Fonts from '../utils/fonts'; +import {FontContext} from "./FontProvider" type LayoutProps = { backgroundColor: string; @@ -19,18 +17,13 @@ const InnerContainer = styled('div')<{ isFontLoaded: boolean }>` `; const Layout: React.FunctionComponent = ({ children, ...props }) => { - const [isFontLoaded, setIsFontLoaded] = useState(false); - useEffect(() => { - let isSubscribed = true - Fonts().then(() => isSubscribed ? setIsFontLoaded(true) : null); - return () => {(isSubscribed = false)}; - }, []); - + const isFontLoaded = React.useContext(FontContext); return ( + {children} diff --git a/pages/_app.tsx b/pages/_app.tsx index 42de10d..fcc0dba 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -9,6 +9,7 @@ import SEO from '../next-seo.config'; // @ts-ignore import withGA from "next-ga"; +import FontProvider from "../components/FontProvider" class MyApp extends App<{analytics: any}> { render() { @@ -16,11 +17,13 @@ class MyApp extends App<{analytics: any}> { return ( - <> - - - - + + <> + + + + + ); diff --git a/pages/index/WelcomeSection/index.tsx b/pages/index/WelcomeSection/index.tsx index 0d70376..e99ac03 100644 --- a/pages/index/WelcomeSection/index.tsx +++ b/pages/index/WelcomeSection/index.tsx @@ -34,7 +34,8 @@ const WelcomeSection = ({ handleClickBubble, ...pros }: WelcomeSectionProps) => - Organizacja Mikołajek nie była nigdy tak łatwa! + Organizacja Mikołajek
    + nie była nigdy tak łatwa!
    { - const link = document.createElement('link'); - link.href = 'https://fonts.googleapis.com/css?family=Lato:300,400,700&subset=latin-ext'; - link.rel = 'stylesheet'; - - document.head.appendChild(link); - - const lato = new FontFaceObserver('Lato'); - - return await lato.load(); -}; - -export default Fonts; diff --git a/utils/index.tsx b/utils/index.tsx index 2a06807..34248f4 100644 --- a/utils/index.tsx +++ b/utils/index.tsx @@ -1,5 +1,4 @@ export * from './apollo'; -export * from './fonts'; export * from './helper'; export * from './media'; export * from './render-ast'; From 0be4bfc6ffeb5a57e19d4b7bbcae18010d129b78 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 10:34:14 +0100 Subject: [PATCH 11/20] Removed container animation --- components/Layout.tsx | 8 +------- pages/index/Bubbles/index.tsx | 2 ++ pages/index/WelcomeSection/Content.tsx | 1 + 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/components/Layout.tsx b/components/Layout.tsx index 89c4935..90edd8a 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import Head from 'next/head'; import styled from 'styled-components'; import { Box } from './Box'; -import {FontContext} from "./FontProvider" type LayoutProps = { backgroundColor: string; @@ -11,13 +10,8 @@ const Container = styled(Box)` margin: 0; height: 100%; `; -const InnerContainer = styled('div')<{ isFontLoaded: boolean }>` - opacity: ${props => (props.isFontLoaded ? 1 : 0)}; - transition: 0.5s; -`; const Layout: React.FunctionComponent = ({ children, ...props }) => { - const isFontLoaded = React.useContext(FontContext); return ( @@ -25,7 +19,7 @@ const Layout: React.FunctionComponent = ({ children, ...props }) => - {children} + {children} ); }; diff --git a/pages/index/Bubbles/index.tsx b/pages/index/Bubbles/index.tsx index ac52932..d5575e6 100644 --- a/pages/index/Bubbles/index.tsx +++ b/pages/index/Bubbles/index.tsx @@ -18,6 +18,8 @@ export const Container = styled('div')` bottom: ${props => (props.height ? `${-(props.height / 2)}px` : 0)}; height: ${props => (props.height ? `${props.height}px` : 0)}; background: transparent; + opacity: ${props => (props.height ? 1 : 0)}; + transition: opacity 0.5s; `; const Index: React.FunctionComponent = ({ handleClickBubble, ...props }) => { diff --git a/pages/index/WelcomeSection/Content.tsx b/pages/index/WelcomeSection/Content.tsx index d524990..e595b8e 100644 --- a/pages/index/WelcomeSection/Content.tsx +++ b/pages/index/WelcomeSection/Content.tsx @@ -4,6 +4,7 @@ import {Flex} from "../../../components" const Content = styled(Flex)` max-width: 374px; + align-content: space-between; margin: 0 auto; padding: 50px 40px 0px 40px; text-align: center; From 909d654f93a429dda365f82527dc6bdabe0e919c Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 12:28:02 +0100 Subject: [PATCH 12/20] Fix container height when input is focused --- components/Layout.tsx | 2 +- pages/index/WelcomeSection/index.tsx | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/components/Layout.tsx b/components/Layout.tsx index 90edd8a..9e871b1 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -8,7 +8,7 @@ type LayoutProps = { }; const Container = styled(Box)` margin: 0; - height: 100%; + min-height: 100%; `; const Layout: React.FunctionComponent = ({ children, ...props }) => { diff --git a/pages/index/WelcomeSection/index.tsx b/pages/index/WelcomeSection/index.tsx index e99ac03..3efc614 100644 --- a/pages/index/WelcomeSection/index.tsx +++ b/pages/index/WelcomeSection/index.tsx @@ -1,6 +1,6 @@ +import React, {useEffect, useState} from 'react'; import styled from 'styled-components'; import {LogoHeading, Navigation} from '../../../components'; -import React from 'react'; import Content from './Content'; import Logo from './Logo'; import InputWithButton from './InputWithButton'; @@ -20,15 +20,20 @@ const Background = styled('div')` pointer-events: none; `; -const Container = styled('div')` - height: 100%; +const Container = styled('div')<{height: number}>` + height: ${props => (props.height ? `${props.height}px` : `100%`)}; overflow: hidden; position: relative; `; const WelcomeSection = ({ handleClickBubble, ...pros }: WelcomeSectionProps) => { + const [height, setHeight] = useState(0) + useEffect(() => { + setHeight(window.innerHeight) + },[]) + return ( - + From e25b216329a9dfc38e5df4a688bb0b96ea3bbba2 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 16:31:58 +0100 Subject: [PATCH 13/20] Added sort blog posts by date:desc --- pages/blog/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/blog/index.tsx b/pages/blog/index.tsx index 01bd57e..60f3659 100644 --- a/pages/blog/index.tsx +++ b/pages/blog/index.tsx @@ -79,7 +79,7 @@ interface StatelessPage

    extends React.FunctionComponent< export const ALL_POSTS_QUERY = gql` query getPosts { - posts { + posts(limit: 999, sort: "date:desc") { _id title content From 0f055190d2688afda6a1a3c4f75f35983692daad Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 16:33:02 +0100 Subject: [PATCH 14/20] Set consistent ratio .69 for mobile cover like in a lists --- pages/blog/[id].tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/blog/[id].tsx b/pages/blog/[id].tsx index 0c99fc0..95363cf 100644 --- a/pages/blog/[id].tsx +++ b/pages/blog/[id].tsx @@ -115,7 +115,7 @@ const PostContent: React.FunctionComponent = ({ }} /> - + From 11b096ee4e225bb478477e6a1fd9eb1bcf763514 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 16:49:11 +0100 Subject: [PATCH 15/20] Refactor politics by separate common components --- components/List.tsx | 11 +++++++++++ components/TextLink.tsx | 15 +++++++++++++++ components/index.tsx | 2 ++ pages/bot-politics/index.tsx | 25 ++----------------------- pages/webiste-politics/index.tsx | 25 ++----------------------- 5 files changed, 32 insertions(+), 46 deletions(-) create mode 100644 components/List.tsx create mode 100644 components/TextLink.tsx diff --git a/components/List.tsx b/components/List.tsx new file mode 100644 index 0000000..f92c57b --- /dev/null +++ b/components/List.tsx @@ -0,0 +1,11 @@ +import styled from "styled-components" +import {Theme} from "../utils" + +export const List = styled('ul')` + display: block; + margin-block-start: ${Theme.space.medium}px; + margin-block-end: ${Theme.space.medium}px; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: ${Theme.space.xregular}px; +` diff --git a/components/TextLink.tsx b/components/TextLink.tsx new file mode 100644 index 0000000..4bf8df4 --- /dev/null +++ b/components/TextLink.tsx @@ -0,0 +1,15 @@ +import styled from "styled-components" +import {Theme} from "../utils" + + +export const TextLink = styled('a')` + color: ${Theme.colors.darkGray}; + + text-decoration: underline; + text-decoration-color: transparent; + transition: text-decoration-color 0.5s; + + &:hover { + text-decoration-color: ${Theme.colors.darkGray}; + } +` diff --git a/components/index.tsx b/components/index.tsx index 2998247..82dd138 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -6,5 +6,7 @@ export * from './Flex'; export * from './GlobalStyle'; export * from './Input'; export * from './Layout'; +export * from './List'; export * from './Navigation'; export * from './RatioContainer'; +export * from './TextLink'; diff --git a/pages/bot-politics/index.tsx b/pages/bot-politics/index.tsx index c444433..b5d2491 100644 --- a/pages/bot-politics/index.tsx +++ b/pages/bot-politics/index.tsx @@ -3,18 +3,9 @@ import BlogLayout from '../blog/BlogLayout'; import { Theme } from '../../utils'; import { NextSeo } from 'next-seo/lib'; import styled from 'styled-components'; -import {Canon, Flex, MediumText} from '../../components'; +import {Canon, Flex, MediumText, List, TextLink} from '../../components'; import media from '../../utils/media'; -const List = styled('ul')` - display: block; - margin-block-start: ${Theme.space.medium}px; - margin-block-end: ${Theme.space.medium}px; - margin-inline-start: 0px; - margin-inline-end: 0px; - padding-inline-start: ${Theme.space.xregular}px; -` - const Container = styled(Flex)` background-color: ${Theme.colors.main}; color: ${Theme.colors.black}; @@ -41,18 +32,6 @@ const Text = styled(MediumText).attrs({ mt: ['small', 'small', 'medium', 'regular'] })`` -const Link = styled('a')` - color: ${Theme.colors.darkGray}; - - text-decoration: underline; - text-decoration-color: transparent; - transition: text-decoration-color 0.5s; - - &:hover { - text-decoration-color: ${Theme.colors.darkGray}; - } -` - const Index = () => { return ( <> @@ -94,7 +73,7 @@ const Index = () => {

  • Administrator wyznaczył inspektora ochrony danych, z którym można - skontaktować się poprzez email info@luck.org.pl. Z Inspektorem ochrony danych można + skontaktować się poprzez email info@luck.org.pl. Z Inspektorem ochrony danych można się kontaktować we wszystkich sprawach dotyczących przetwarzania danych osobowych oraz korzystania z praw związanych z przetwarzaniem danych. diff --git a/pages/webiste-politics/index.tsx b/pages/webiste-politics/index.tsx index 4f617f2..ae2b3de 100644 --- a/pages/webiste-politics/index.tsx +++ b/pages/webiste-politics/index.tsx @@ -3,18 +3,9 @@ import BlogLayout from '../blog/BlogLayout'; import { Theme } from '../../utils'; import { NextSeo } from 'next-seo/lib'; import styled from 'styled-components'; -import { Canon, Flex, MediumText } from '../../components'; +import {Canon, Flex, List, MediumText, TextLink} from '../../components'; import media from '../../utils/media'; -const List = styled('ul')` - display: block; - margin-block-start: ${Theme.space.medium}px; - margin-block-end: ${Theme.space.medium}px; - margin-inline-start: 0px; - margin-inline-end: 0px; - padding-inline-start: ${Theme.space.xregular}px; -`; - const Container = styled(Flex)` background-color: ${Theme.colors.main}; color: ${Theme.colors.black}; @@ -41,18 +32,6 @@ const Text = styled(MediumText).attrs({ mt: ['small', 'small', 'medium', 'regular'], })``; -const Link = styled('a')` - color: ${Theme.colors.darkGray}; - - text-decoration: underline; - text-decoration-color: transparent; - transition: text-decoration-color 0.5s; - - &:hover { - text-decoration-color: ${Theme.colors.darkGray}; - } -`; - const Index = () => { return ( <> @@ -73,7 +52,7 @@ const Index = () => { (2) Podmiotem odpowiedzialnym zgodnie z art. 4 ust. 7 Ogólnego rozporządzenia o ochronie danych (RODO) jest zespół LUCK,{' '} - info@luck.org.pl. Z naszym pełnomocnikiem do + info@luck.org.pl. Z naszym pełnomocnikiem do spraw ochrony danych osobowych można skontaktować się przez e-mail lub tradycyjną pocztą z dopiskiem „Pełnomocnik ds. ochrony danych osobowych”. From 3b233413a297be2a83b15e9d41e11215b69ecbaf Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 16:51:45 +0100 Subject: [PATCH 16/20] Fixed text styles for blog content --- pages/blog/[id].tsx | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/pages/blog/[id].tsx b/pages/blog/[id].tsx index 95363cf..9412eff 100644 --- a/pages/blog/[id].tsx +++ b/pages/blog/[id].tsx @@ -4,7 +4,7 @@ import gql from 'graphql-tag'; import styled from 'styled-components'; import { NextSeo } from 'next-seo/lib'; import RatioContainer from '../../components/RatioContainer'; -import { MediumText, Canon, Flex, TinySecond } from '../../components'; +import { MediumText, Canon, Flex, TinySecond, List } from '../../components'; import BlogLayout from './BlogLayout'; import { mapToPost } from './index'; import { withApollo, getProcessor, Theme, Post, QueryPostsArgs, Hashtag } from '../../utils'; @@ -12,26 +12,16 @@ import media from '../../utils/media'; const ContentImage = styled('img').attrs({alt: ""})` width: 100%; - - padding: ${Theme.space.small}px 0; - - - ${media.greaterThan('mobile')` - - `} - - ${media.greaterThan('tablet')` - padding: ${Theme.space.regular}px 0; - `} - - ${media.greaterThan('desktop')` - padding: ${Theme.space.xregular}px 0; - `} `; +const Text = styled(MediumText).attrs({ + mt: ['small', 'small', 'medium', 'regular'] +})`` + const components = { - p: MediumText, + p: Text, img: ContentImage, + ul: List, }; const processor = getProcessor(components); @@ -58,7 +48,7 @@ const ContentContainer = styled(Flex)` max-width: 664px; margin: 0 auto; flex-direction: column; - padding: ${Theme.space.small}px; + padding: ${Theme.space.medium}px ${Theme.space.small}px; ${media.greaterThan('mobile')` @@ -119,7 +109,7 @@ const PostContent: React.FunctionComponent = ({ - {title} + {title} {processor.processSync(content).contents} {(hashtags as Hashtag[]).map(({ name }, index) => ( From fd771d245d4cde97c70bc18dc98528300afc2258 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sat, 2 Nov 2019 16:53:57 +0100 Subject: [PATCH 17/20] Fix hover on card of list --- components/Card/CardDesktop.tsx | 4 ++-- components/Card/CardMobile.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/Card/CardDesktop.tsx b/components/Card/CardDesktop.tsx index 695d1a4..b068789 100644 --- a/components/Card/CardDesktop.tsx +++ b/components/Card/CardDesktop.tsx @@ -13,14 +13,14 @@ const Container = styled(Box)` overflow: hidden; box-shadow: 0 0 10px rgba(0, 0, 0, 0.25); - h3 { + h2 { text-decoration: underline; text-decoration-color: transparent; transition: text-decoration-color 0.5s, opacity 0.5s; } &:hover { - h3 { + h2 { opacity: 0.8; text-decoration-color: ${Theme.colors.black}; } diff --git a/components/Card/CardMobile.tsx b/components/Card/CardMobile.tsx index 9b03514..4bf612f 100644 --- a/components/Card/CardMobile.tsx +++ b/components/Card/CardMobile.tsx @@ -27,13 +27,13 @@ const InnerContainer = styled(Flex)` cursor: pointer; width: 100%; - h3 { + h2 { text-decoration: underline; text-decoration-color: transparent; transition: text-decoration-color 0.5s, opacity 0.5s; } &:hover { - h3 { + h2 { opacity: 0.8; text-decoration-color: ${Theme.colors.black}; } From 7a3885e934318d50e82d23c2890b2be672bc30e5 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sun, 3 Nov 2019 10:27:11 +0100 Subject: [PATCH 18/20] Create RatioLazyImage & LazyImage and handle covers placeholder --- components/Card/CardDesktop.tsx | 58 ++++++-------- components/Card/CardMobile.tsx | 31 +++----- components/Card/index.tsx | 4 +- components/LazyImage.tsx | 57 ++++++++++++++ components/RatioLazyImage.tsx | 37 +++++++++ components/index.tsx | 2 + package.json | 1 + pages/blog/[id].tsx | 132 +++++++++++++++----------------- pages/blog/index.tsx | 93 ++++++++++++++++------ utils/types.tsx | 42 +++++++++- yarn.lock | 5 ++ 11 files changed, 312 insertions(+), 150 deletions(-) create mode 100644 components/LazyImage.tsx create mode 100644 components/RatioLazyImage.tsx diff --git a/components/Card/CardDesktop.tsx b/components/Card/CardDesktop.tsx index b068789..9f5f5bc 100644 --- a/components/Card/CardDesktop.tsx +++ b/components/Card/CardDesktop.tsx @@ -2,9 +2,10 @@ import React, { Fragment } from 'react'; import styled from 'styled-components'; import Link from 'next/link'; import { TinyText, Trafalgar } from '../Typography'; -import RatioContainer from '../../components/RatioContainer'; import { Box } from '../Box'; -import { Theme, Hashtag, Post } from '../../utils'; +import { Theme, Hashtag } from '../../utils'; +import {ViewPost} from "../../pages/blog" +import {RatioLazyImage} from "../RatioLazyImage" const Container = styled(Box)` position: relative; @@ -31,18 +32,6 @@ const ContentContainer = styled(Box)` padding: ${Theme.space.medium}px; `; -const Image = styled('img')` - width: 100%; - height: auto; - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%); -`; -const ImageContainer = styled(RatioContainer)` - position: relative; -`; - const Hashtags = styled(TinyText)` span { text-transform: uppercase; @@ -53,8 +42,12 @@ const Hashtags = styled(TinyText)` } `; +const Image = styled(RatioLazyImage)` + border-top-left-radius: 14px; + border-top-right-radius: 14px; +`; -export interface CardDesktopProps extends Post { +export interface CardDesktopProps extends ViewPost { display: any; } @@ -63,33 +56,32 @@ const CardDesktop: React.FunctionComponent = ({ description, slug, cover, + coverPlaceholder, hashtags = [], ...props -}) => ( +}) => { + return ( {cover && ( - -

    {cover.url}

    - -
    - - - {(hashtags as Hashtag[]).map(({ name }, index) => ( - #{name} - ))} - - - {title} - - -
    - + + + + {(hashtags as Hashtag[]).map(({ name }, index) => ( + #{name} + ))} + + + {title} + + + +
    )}
    -); +)}; export default CardDesktop; diff --git a/components/Card/CardMobile.tsx b/components/Card/CardMobile.tsx index 4bf612f..11111f4 100644 --- a/components/Card/CardMobile.tsx +++ b/components/Card/CardMobile.tsx @@ -3,9 +3,10 @@ import styled from 'styled-components'; import Link from 'next/link'; import { Flex } from '../Flex'; import { Trafalgar } from '../Typography'; -import RatioContainer from '../../components/RatioContainer'; import { Box } from '../Box'; -import { Theme, Post } from '../../utils'; +import { Theme } from '../../utils'; +import { ViewPost } from '../../pages/blog'; +import { RatioLazyImage } from '../RatioLazyImage'; const Container = styled(Flex)` margin-bottom: ${Theme.space.small}px; @@ -40,25 +41,17 @@ const InnerContainer = styled(Flex)` } `; -const Image = styled('img')` - width: 100%; - height: auto; - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%); -`; - -const ImageContainer = styled(RatioContainer)` - position: relative; -`; - const ContentContainer = styled(Box)` padding: ${Theme.space.medium}px; text-align: center; `; -export interface CardMobileProps extends Post { +const Image = styled(RatioLazyImage)` + border-top-left-radius: 14px; + border-top-right-radius: 14px; +`; + +export interface CardMobileProps extends ViewPost { display: any; } @@ -67,6 +60,7 @@ const CardMobile: React.FunctionComponent = ({ description, slug, cover, + coverPlaceholder, ...props }) => ( @@ -75,10 +69,7 @@ const CardMobile: React.FunctionComponent = ({ - -

    {cover.url}

    - -
    + {title} diff --git a/components/Card/index.tsx b/components/Card/index.tsx index b003dda..dcb6aa4 100644 --- a/components/Card/index.tsx +++ b/components/Card/index.tsx @@ -1,9 +1,9 @@ import React, { Fragment } from 'react'; import CardMobile from './CardMobile'; import CardDesktop from "./CardDesktop" -import { Post } from '../../utils'; +import {ViewPost} from "../../pages/blog" -const Card: React.FunctionComponent = ({ ...props }) => ( +const Card: React.FunctionComponent = ({ ...props }) => ( diff --git a/components/LazyImage.tsx b/components/LazyImage.tsx new file mode 100644 index 0000000..32ee11c --- /dev/null +++ b/components/LazyImage.tsx @@ -0,0 +1,57 @@ +import React, {useEffect, useRef, useState} from "react" +import useVisibilitySensor from "@rooks/use-visibility-sensor"; + +export const LazyImage: React.FunctionComponent = ({children, src, placeholderSrc, onImageError}) => { + const rootNode = useRef(null); + const { isVisible } = useVisibilitySensor(rootNode, { + intervalCheck: false, + scrollCheck: true, + resizeCheck: true + }); + + if (!children || typeof children !== "function") { + throw new Error( + `LazyImage requires a function as its only child` + ); + } + + const [imageElement, setImageElement] = useState(null); + const [imageSrc, setImageSrc] = useState(placeholderSrc); + const [loading, setLoading] = useState(true); + + const onLoad = () => { + setImageSrc(src) + setLoading(false) + }; + + const onError = (error: any) => { + if (onImageError) { + onImageError(error); + } + }; + + const loadImage = () => { + if (imageElement) { + imageElement.onload = null; + imageElement.onerror = null; + } + const image = new Image(); + setImageElement(() => { + image.onload = onLoad; + image.onerror = onError; + image.src = src; + + return image + }); + } + + useEffect(() => { + if(loading && isVisible) loadImage() + }, [isVisible]) + + return ( +
    + {children(imageSrc, loading, isVisible)} +
    + ) +} diff --git a/components/RatioLazyImage.tsx b/components/RatioLazyImage.tsx new file mode 100644 index 0000000..a2cbad2 --- /dev/null +++ b/components/RatioLazyImage.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import styled from 'styled-components'; +import RatioContainer from './RatioContainer'; +import {LazyImage} from "./LazyImage" + +interface RatioLazyImageProps { + display?: string | string []; + ratio: string | string []; + url: string; + placeholderUrl: string; +} + +const LazyImageContainer = styled(RatioContainer)` + position: relative; +`; + +const Image = styled('img')<{ loading: any }>` + width: 100%; + height: auto; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + filter: ${props => (props.loading ? 'blur(10px)' : 'blur(0)')}; + opacity: ${props => (props.loading ? 0.8 : 1)}; + transition: filter 0.5s, opacity 0.5s; +`; + +export const RatioLazyImage: React.FunctionComponent = ({ url, placeholderUrl, ...props }) => { + return ( + + + {(src: any, loading: boolean) => {loading.toString()}} + + + ); +}; diff --git a/components/index.tsx b/components/index.tsx index 82dd138..92d60c8 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -6,7 +6,9 @@ export * from './Flex'; export * from './GlobalStyle'; export * from './Input'; export * from './Layout'; +export * from './LazyImage'; export * from './List'; export * from './Navigation'; export * from './RatioContainer'; +export * from './RatioLazyImage'; export * from './TextLink'; diff --git a/package.json b/package.json index 1c20837..56355c5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@apollo/react-hooks": "^3.1.0", "@apollo/react-ssr": "^3.1.0", "@rehooks/component-size": "^1.0.3", + "@rooks/use-visibility-sensor": "^3.4.0", "@types/react-no-ssr": "^1.1.1", "apollo-boost": "^0.4.4", "apollo-link-state": "^0.4.2", diff --git a/pages/blog/[id].tsx b/pages/blog/[id].tsx index 9412eff..9707b24 100644 --- a/pages/blog/[id].tsx +++ b/pages/blog/[id].tsx @@ -3,20 +3,19 @@ import { useQuery } from '@apollo/react-hooks'; import gql from 'graphql-tag'; import styled from 'styled-components'; import { NextSeo } from 'next-seo/lib'; -import RatioContainer from '../../components/RatioContainer'; -import { MediumText, Canon, Flex, TinySecond, List } from '../../components'; +import { MediumText, Canon, Flex, TinySecond, List, RatioLazyImage } from '../../components'; import BlogLayout from './BlogLayout'; -import { mapToPost } from './index'; +import { mapToViewPosts, POST_FRAGMENT, ViewPost } from './index'; import { withApollo, getProcessor, Theme, Post, QueryPostsArgs, Hashtag } from '../../utils'; import media from '../../utils/media'; -const ContentImage = styled('img').attrs({alt: ""})` +const ContentImage = styled('img').attrs({ alt: '' })` width: 100%; `; const Text = styled(MediumText).attrs({ - mt: ['small', 'small', 'medium', 'regular'] -})`` + mt: ['small', 'small', 'medium', 'regular'], +})``; const components = { p: Text, @@ -32,18 +31,6 @@ const Container = styled(Flex)` margin: 0 auto; `; -const Image = styled('img')` - width: 100%; - height: auto; - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%); -`; -const ImageContainer = styled(RatioContainer)` - position: relative; -`; - const ContentContainer = styled(Flex)` max-width: 664px; margin: 0 auto; @@ -73,73 +60,69 @@ const HashtagsText = styled(TinySecond)` } `; -const PostContent: React.FunctionComponent = ({ +const PostContent: React.FunctionComponent = ({ title, description, content, createdAt, updatedAt, cover, + coverPlaceholder, + wideCover, + wideCoverPlaceholder, slug, hashtags = [], ...props }) => ( <> - {cover && ( - <> - name), - }, - }} - /> - - - - - - {title} - {processor.processSync(content).contents} - - {(hashtags as Hashtag[]).map(({ name }, index) => ( - #{name} - ))} - - - - - )} + name), + }, + }} + /> + + + + + {title} + {processor.processSync(content).contents} + + {(hashtags as Hashtag[]).map(({ name }, index) => ( + #{name} + ))} + + + ); export const POST_QUERY = gql` + ${POST_FRAGMENT} query getPosts($where: JSON) { posts(where: $where) { - title - description - content - slug - isDraft - createdAt - updatedAt - slug - cover { - url - } - hashtags { - name - } + ...Post } } `; @@ -150,16 +133,21 @@ interface StatelessPage

    extends React.Func const Index: StatelessPage = ({ slug, cmsUrl }) => { const where = { slug }; - const { loading, error, data } = useQuery<{ posts: Post[] }, QueryPostsArgs>(POST_QUERY, { - variables: { where }, - }); + const { loading, error, data = { posts: [] } } = useQuery<{ posts: Post[] }, QueryPostsArgs>( + POST_QUERY, + { + variables: { where }, + }, + ); + + const viewPost = mapToViewPosts(data.posts, cmsUrl)[0]; if (error) return

    Error loading users.
    ; if (loading) return
    Loading
    ; return ( - {data && data.posts[0] && } + {viewPost && } ); }; diff --git a/pages/blog/index.tsx b/pages/blog/index.tsx index 60f3659..ce6b1b6 100644 --- a/pages/blog/index.tsx +++ b/pages/blog/index.tsx @@ -1,6 +1,6 @@ import React, { Fragment } from 'react'; import { NextSeo } from 'next-seo'; -import { withApollo, Theme, Post } from '../../utils'; +import { withApollo, Theme, Post, UploadFile } from '../../utils'; import { useQuery } from '@apollo/react-hooks'; import gql from 'graphql-tag'; import styled from 'styled-components'; @@ -77,32 +77,83 @@ interface StatelessPage

    extends React.FunctionComponent< getInitialProps?: (ctx: any) => Promise

    ; } +export const POST_FRAGMENT = gql` + fragment Post on Post { + _id + title + description + content + slug + isDraft + createdAt + updatedAt + slug + cover { + url + } + coverPlaceholder { + url + } + wideCover { + url + } + wideCoverPlaceholder { + url + } + hashtags { + name + } + } +`; + export const ALL_POSTS_QUERY = gql` + ${POST_FRAGMENT} query getPosts { posts(limit: 999, sort: "date:desc") { - _id - title - content - description - slug - isDraft - cover { - url - } - hashtags { - name - } + ...Post } } `; -export const mapToPost = ({ cover, ...post }: Post, cmsUrl: string): Post => { - cover = cover && { ...cover, url: `${cmsUrl}${cover.url}` }; - return { cover, ...post }; +export interface ViewPost extends Post { + cover: UploadFile; + coverPlaceholder: UploadFile; + wideCover: UploadFile; + wideCoverPlaceholder: UploadFile; +} + +const isSomeCoverUndefined = ({ + cover, + coverPlaceholder, + wideCover, + wideCoverPlaceholder, +}: Post): boolean => { + return ![cover, coverPlaceholder, wideCover, wideCoverPlaceholder].some(c => !c); +}; + +const mapCoverUrls = ( + { cover, coverPlaceholder, wideCover, wideCoverPlaceholder, ...post }: ViewPost, + cmsUrl: string, +): ViewPost => { + const mapCoverUrl = (c: any) => ({ ...c, url: `${cmsUrl}${c.url}` }); + cover = mapCoverUrl(cover); + coverPlaceholder = mapCoverUrl(coverPlaceholder); + wideCover = mapCoverUrl(wideCover); + wideCoverPlaceholder = mapCoverUrl(wideCoverPlaceholder); + return { cover, coverPlaceholder, wideCover, wideCoverPlaceholder, ...post }; }; +export const mapToViewPosts = (posts: Post[], cmsUrl: string):ViewPost[] => { + return posts + .filter(({ isDraft }) => !isDraft) + .filter(isSomeCoverUndefined) + .map(post => mapCoverUrls(post as ViewPost, cmsUrl)) +} + const Index: StatelessPage = ({ cmsUrl }) => { - const { loading, error, data } = useQuery<{ posts: Post[] }>(ALL_POSTS_QUERY); + const { loading, error, data = { posts: [] } } = useQuery<{ posts: Post[] }>(ALL_POSTS_QUERY); + + const viewPosts = mapToViewPosts(data.posts, cmsUrl); if (error) return

    Error loading users.
    ; if (loading) return
    Loading
    ; @@ -115,11 +166,9 @@ const Index: StatelessPage = ({ cmsUrl }) => { /> - {data && - data.posts - .filter(({ isDraft }) => !isDraft) - .map(post => mapToPost(post, cmsUrl)) - .map((post, index) => )} + {viewPosts.map((post, index) => ( + + ))} diff --git a/utils/types.tsx b/utils/types.tsx index b4c452c..430f0b5 100644 --- a/utils/types.tsx +++ b/utils/types.tsx @@ -118,7 +118,11 @@ export type EditPostInput = { slug?: Maybe, isDraft?: Maybe, cover?: Maybe, + coverPlaceholder?: Maybe, + wideCover?: Maybe, + wideCoverPlaceholder?: Maybe, description?: Maybe, + date?: Maybe, hashtags?: Maybe>>, }; @@ -235,7 +239,7 @@ export type InputId = { -export type Morph = UsersPermissionsMe | UsersPermissionsMeRole | Hashtag | CreateHashtagPayload | UpdateHashtagPayload | DeleteHashtagPayload | HashtagConnection | HashtagAggregator | HashtagGroupBy | HashtagConnectionName | HashtagConnection_Id | HashtagConnectionId | HashtagConnectionCreatedAt | HashtagConnectionUpdatedAt | Post | CreatePostPayload | UpdatePostPayload | DeletePostPayload | PostConnection | PostAggregator | PostGroupBy | PostConnectionTitle | PostConnectionContent | PostConnectionSlug | PostConnectionIsDraft | PostConnectionCover | PostConnectionDescription | PostConnection_Id | PostConnectionId | PostConnectionCreatedAt | PostConnectionUpdatedAt | UploadFile | UploadFileConnection | UploadFileAggregator | UploadFileGroupBy | UploadFileConnectionName | UploadFileConnectionHash | UploadFileConnectionSha256 | UploadFileConnectionExt | UploadFileConnectionMime | UploadFileConnectionSize | UploadFileConnectionUrl | UploadFileConnectionProvider | UploadFileConnectionPublic_Id | UploadFileConnection_Id | UploadFileConnectionId | UploadFileConnectionCreatedAt | UploadFileConnectionUpdatedAt | UsersPermissionsPermission | UsersPermissionsRole | CreateRolePayload | UpdateRolePayload | DeleteRolePayload | UsersPermissionsRoleConnection | UsersPermissionsRoleAggregator | UsersPermissionsRoleGroupBy | UsersPermissionsRoleConnectionName | UsersPermissionsRoleConnectionDescription | UsersPermissionsRoleConnectionType | UsersPermissionsRoleConnection_Id | UsersPermissionsRoleConnectionId | UsersPermissionsRoleConnectionCreatedAt | UsersPermissionsRoleConnectionUpdatedAt | UsersPermissionsUser | CreateUserPayload | UpdateUserPayload | DeleteUserPayload | UsersPermissionsUserConnection | UsersPermissionsUserAggregator | UsersPermissionsUserGroupBy | UsersPermissionsUserConnectionUsername | UsersPermissionsUserConnectionEmail | UsersPermissionsUserConnectionProvider | UsersPermissionsUserConnectionConfirmed | UsersPermissionsUserConnectionBlocked | UsersPermissionsUserConnectionRole | UsersPermissionsUserConnection_Id | UsersPermissionsUserConnectionId | UsersPermissionsUserConnectionCreatedAt | UsersPermissionsUserConnectionUpdatedAt; +export type Morph = UsersPermissionsMe | UsersPermissionsMeRole | Hashtag | CreateHashtagPayload | UpdateHashtagPayload | DeleteHashtagPayload | HashtagConnection | HashtagAggregator | HashtagGroupBy | HashtagConnectionName | HashtagConnection_Id | HashtagConnectionId | HashtagConnectionCreatedAt | HashtagConnectionUpdatedAt | Post | CreatePostPayload | UpdatePostPayload | DeletePostPayload | PostConnection | PostAggregator | PostGroupBy | PostConnectionTitle | PostConnectionContent | PostConnectionSlug | PostConnectionIsDraft | PostConnectionCover | PostConnectionCoverPlaceholder | PostConnectionWideCover | PostConnectionWideCoverPlaceholder | PostConnectionDescription | PostConnectionDate | PostConnection_Id | PostConnectionId | PostConnectionCreatedAt | PostConnectionUpdatedAt | UploadFile | UploadFileConnection | UploadFileAggregator | UploadFileGroupBy | UploadFileConnectionName | UploadFileConnectionHash | UploadFileConnectionSha256 | UploadFileConnectionExt | UploadFileConnectionMime | UploadFileConnectionSize | UploadFileConnectionUrl | UploadFileConnectionProvider | UploadFileConnectionPublic_Id | UploadFileConnection_Id | UploadFileConnectionId | UploadFileConnectionCreatedAt | UploadFileConnectionUpdatedAt | UsersPermissionsPermission | UsersPermissionsRole | CreateRolePayload | UpdateRolePayload | DeleteRolePayload | UsersPermissionsRoleConnection | UsersPermissionsRoleAggregator | UsersPermissionsRoleGroupBy | UsersPermissionsRoleConnectionName | UsersPermissionsRoleConnectionDescription | UsersPermissionsRoleConnectionType | UsersPermissionsRoleConnection_Id | UsersPermissionsRoleConnectionId | UsersPermissionsRoleConnectionCreatedAt | UsersPermissionsRoleConnectionUpdatedAt | UsersPermissionsUser | CreateUserPayload | UpdateUserPayload | DeleteUserPayload | UsersPermissionsUserConnection | UsersPermissionsUserAggregator | UsersPermissionsUserGroupBy | UsersPermissionsUserConnectionUsername | UsersPermissionsUserConnectionEmail | UsersPermissionsUserConnectionProvider | UsersPermissionsUserConnectionConfirmed | UsersPermissionsUserConnectionBlocked | UsersPermissionsUserConnectionRole | UsersPermissionsUserConnection_Id | UsersPermissionsUserConnectionId | UsersPermissionsUserConnectionCreatedAt | UsersPermissionsUserConnectionUpdatedAt; export type Mutation = { __typename?: 'Mutation', @@ -346,7 +350,11 @@ export type Post = { slug?: Maybe, isDraft?: Maybe, cover?: Maybe, + coverPlaceholder?: Maybe, + wideCover?: Maybe, + wideCoverPlaceholder?: Maybe, description: Scalars['String'], + date: Scalars['DateTime'], hashtags?: Maybe>>, _id: Scalars['ID'], id: Scalars['ID'], @@ -393,12 +401,24 @@ export type PostConnectionCover = { connection?: Maybe, }; +export type PostConnectionCoverPlaceholder = { + __typename?: 'PostConnectionCoverPlaceholder', + key?: Maybe, + connection?: Maybe, +}; + export type PostConnectionCreatedAt = { __typename?: 'PostConnectionCreatedAt', key?: Maybe, connection?: Maybe, }; +export type PostConnectionDate = { + __typename?: 'PostConnectionDate', + key?: Maybe, + connection?: Maybe, +}; + export type PostConnectionDescription = { __typename?: 'PostConnectionDescription', key?: Maybe, @@ -435,6 +455,18 @@ export type PostConnectionUpdatedAt = { connection?: Maybe, }; +export type PostConnectionWideCover = { + __typename?: 'PostConnectionWideCover', + key?: Maybe, + connection?: Maybe, +}; + +export type PostConnectionWideCoverPlaceholder = { + __typename?: 'PostConnectionWideCoverPlaceholder', + key?: Maybe, + connection?: Maybe, +}; + export type PostGroupBy = { __typename?: 'PostGroupBy', title?: Maybe>>, @@ -442,7 +474,11 @@ export type PostGroupBy = { slug?: Maybe>>, isDraft?: Maybe>>, cover?: Maybe>>, + coverPlaceholder?: Maybe>>, + wideCover?: Maybe>>, + wideCoverPlaceholder?: Maybe>>, description?: Maybe>>, + date?: Maybe>>, _id?: Maybe>>, id?: Maybe>>, createdAt?: Maybe>>, @@ -455,7 +491,11 @@ export type PostInput = { slug?: Maybe, isDraft?: Maybe, cover?: Maybe, + coverPlaceholder?: Maybe, + wideCover?: Maybe, + wideCoverPlaceholder?: Maybe, description: Scalars['String'], + date: Scalars['DateTime'], hashtags?: Maybe>>, }; diff --git a/yarn.lock b/yarn.lock index a7b71b8..0449013 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1016,6 +1016,11 @@ resolved "https://registry.yarnpkg.com/@rehooks/component-size/-/component-size-1.0.3.tgz#823eabeb42084893d46d43e3a9d1d0e34252b3cb" integrity sha512-pnYld+8SSF2vXwdLOqBGUyOrv/SjzwLjIUcs/4c1JJgR0q4E9eBtBfuZMD6zUD51fvSehSsbnlQMzotSmPTXPg== +"@rooks/use-visibility-sensor@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@rooks/use-visibility-sensor/-/use-visibility-sensor-3.4.0.tgz#76b0523129913a4ec09ef1661807a141d1cdf83e" + integrity sha512-bjDt27TCvRn3DBRT9rm8Is2MeMK8kBJX9NJEszOYxCZnbfHNQu2Nmgt+fobyhLyRnwP5j4mchDxeL9dIazJlog== + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" From bbde2b278566347a4e766b23cca4063d9b6c2be7 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sun, 3 Nov 2019 13:02:00 +0100 Subject: [PATCH 19/20] Added Header style for blog content --- pages/blog/[id].tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pages/blog/[id].tsx b/pages/blog/[id].tsx index 9707b24..c728496 100644 --- a/pages/blog/[id].tsx +++ b/pages/blog/[id].tsx @@ -3,7 +3,7 @@ import { useQuery } from '@apollo/react-hooks'; import gql from 'graphql-tag'; import styled from 'styled-components'; import { NextSeo } from 'next-seo/lib'; -import { MediumText, Canon, Flex, TinySecond, List, RatioLazyImage } from '../../components'; +import {MediumText, Canon, Flex, TinySecond, List, RatioLazyImage, Trafalgar} from '../../components'; import BlogLayout from './BlogLayout'; import { mapToViewPosts, POST_FRAGMENT, ViewPost } from './index'; import { withApollo, getProcessor, Theme, Post, QueryPostsArgs, Hashtag } from '../../utils'; @@ -17,8 +17,13 @@ const Text = styled(MediumText).attrs({ mt: ['small', 'small', 'medium', 'regular'], })``; +const Header = styled(Trafalgar).attrs({ + mt: ['small', 'small', 'medium', 'regular'], +})``; + const components = { p: Text, + h2: Header, img: ContentImage, ul: List, }; From 50759e2fa2d516b51f346e605686f32473547129 Mon Sep 17 00:00:00 2001 From: Daniel Karski Date: Sun, 3 Nov 2019 13:28:16 +0100 Subject: [PATCH 20/20] Handle drafts post for test development --- .env.develop | 3 ++- .travis.yml | 10 +++++++--- Dockerfile | 2 ++ pages/blog/[id].tsx | 11 +++++++---- pages/blog/index.tsx | 19 ++++++++++++------- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/.env.develop b/.env.develop index 48c4ef0..ae83b7e 100644 --- a/.env.develop +++ b/.env.develop @@ -1,2 +1,3 @@ -APPOLO_CLIENT_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkODI0OTUzYjViZmY4MjZlODNmZmU1MSIsImlzQWRtaW4iOnRydWUsImlhdCI6MTU3MTQ5Mzc2MywiZXhwIjoxNTc0MDg1NzYzfQ.4waARlkToB3Rq2JE0cEmyBmsaHfJyUwwlaK3wdrPUqE +APPOLO_CLIENT_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYmViZTNkNDM3OGVhNTExZDE4MGIzZSIsImlzQWRtaW4iOnRydWUsImlhdCI6MTU3Mjc4MTY1NiwiZXhwIjoxNTc1MzczNjU2fQ.--S0L3AC0nJqskUp49_6_yYFs6oCmEyNhRCPsvnt68A CLIENT_URL=http://localhost:1337 +SHOULD_SHOW_DRAFT=true diff --git a/.travis.yml b/.travis.yml index 8260878..22741cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,15 +17,19 @@ jobs: services: - docker script: - - docker build --build-arg TRACKING_ID=$TRACKING_ID --build-arg CLIENT_URL=$CLIENT_URL --build-arg APPOLO_CLIENT_TOKEN=$APPOLO_CLIENT_TOKEN . -t $IMAGE_NAME + - docker build --build-arg SHOULD_SHOW_DRAFT=$SHOULD_SHOW_DRAFT --build-arg TRACKING_ID=$TRACKING_ID --build-arg CLIENT_URL=$CLIENT_URL --build-arg APPOLO_CLIENT_TOKEN=$APPOLO_CLIENT_TOKEN . -t $IMAGE_NAME - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - docker push $IMAGE_NAME if: branch = develop - env: IMAGE_NAME=$DEV_IMAGE_NAME + env: + - IMAGE_NAME=$DEV_IMAGE_NAME + - SHOULD_SHOW_DRAFT=true - <<: *build if: branch = master OR tag =~ ^v.*$ - env: IMAGE_NAME=$PROD_IMAGE_NAME + env: + - IMAGE_NAME=$PROD_IMAGE_NAME + - SHOULD_SHOW_DRAFT=false - &deploy diff --git a/Dockerfile b/Dockerfile index 28681ee..c110425 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,9 +6,11 @@ COPY . . ARG APPOLO_CLIENT_TOKEN ARG CLIENT_URL ARG TRACKING_ID +ARG SHOULD_SHOW_DRAFT ENV APPOLO_CLIENT_TOKEN=$APPOLO_CLIENT_TOKEN ENV CLIENT_URL=$CLIENT_URL ENV TRACKING_ID=$TRACKING_ID +ENV SHOULD_SHOW_DRAFT=$SHOULD_SHOW_DRAFT RUN yarn run build ENV PORT 3000 EXPOSE 3000 diff --git a/pages/blog/[id].tsx b/pages/blog/[id].tsx index c728496..a896610 100644 --- a/pages/blog/[id].tsx +++ b/pages/blog/[id].tsx @@ -132,11 +132,11 @@ export const POST_QUERY = gql` } `; -interface StatelessPage

    extends React.FunctionComponent

    { +interface StatelessPage

    extends React.FunctionComponent

    { getInitialProps?: (ctx: any) => Promise

    ; } -const Index: StatelessPage = ({ slug, cmsUrl }) => { +const Index: StatelessPage = ({ slug, cmsUrl, shouldShowDraft }) => { const where = { slug }; const { loading, error, data = { posts: [] } } = useQuery<{ posts: Post[] }, QueryPostsArgs>( POST_QUERY, @@ -145,7 +145,7 @@ const Index: StatelessPage = ({ slug, cmsUrl }) => { }, ); - const viewPost = mapToViewPosts(data.posts, cmsUrl)[0]; + const viewPost = mapToViewPosts(data.posts, cmsUrl, shouldShowDraft)[0]; if (error) return

    Error loading users.
    ; if (loading) return
    Loading
    ; @@ -158,7 +158,10 @@ const Index: StatelessPage = ({ slug, cmsUrl }) => { }; Index.getInitialProps = async function({ query }) { - return { slug: query.id, cmsUrl: process.env.CLIENT_URL ? process.env.CLIENT_URL : '' }; + return { + slug: query.id, cmsUrl: process.env.CLIENT_URL ? process.env.CLIENT_URL : '', + shouldShowDraft: process.env.SHOULD_SHOW_DRAFT === "true" + }; }; export default withApollo(Index, { diff --git a/pages/blog/index.tsx b/pages/blog/index.tsx index ce6b1b6..9465d6f 100644 --- a/pages/blog/index.tsx +++ b/pages/blog/index.tsx @@ -73,7 +73,7 @@ const CardsContainer: React.FunctionComponent = ({ children, ...props }) => ); }; -interface StatelessPage

    extends React.FunctionComponent

    { +interface StatelessPage

    extends React.FunctionComponent

    { getInitialProps?: (ctx: any) => Promise

    ; } @@ -143,17 +143,19 @@ const mapCoverUrls = ( return { cover, coverPlaceholder, wideCover, wideCoverPlaceholder, ...post }; }; -export const mapToViewPosts = (posts: Post[], cmsUrl: string):ViewPost[] => { - return posts - .filter(({ isDraft }) => !isDraft) +export const mapToViewPosts = (posts: Post[], cmsUrl: string, shouldShowDraft: boolean):ViewPost[] => { + const viewPost = posts .filter(isSomeCoverUndefined) .map(post => mapCoverUrls(post as ViewPost, cmsUrl)) + + return shouldShowDraft ? viewPost : viewPost.filter(({ isDraft }) => !isDraft); } -const Index: StatelessPage = ({ cmsUrl }) => { +const Index: StatelessPage = ({ cmsUrl, shouldShowDraft }) => { const { loading, error, data = { posts: [] } } = useQuery<{ posts: Post[] }>(ALL_POSTS_QUERY); - const viewPosts = mapToViewPosts(data.posts, cmsUrl); + const viewPosts = mapToViewPosts(data.posts, cmsUrl, shouldShowDraft); + console.log(shouldShowDraft, viewPosts[0] && viewPosts[0].isDraft) if (error) return

    Error loading users.
    ; if (loading) return
    Loading
    ; @@ -176,7 +178,10 @@ const Index: StatelessPage = ({ cmsUrl }) => { }; Index.getInitialProps = async function() { - return { cmsUrl: process.env.CLIENT_URL ? process.env.CLIENT_URL : '' }; + return { + cmsUrl: process.env.CLIENT_URL ? process.env.CLIENT_URL : '', + shouldShowDraft: process.env.SHOULD_SHOW_DRAFT === "true" + }; }; export default withApollo(Index, {