diff --git a/.github/screenshots/screenshot1.png b/.github/screenshots/screenshot1.png
index 0188da6..e94cc11 100644
Binary files a/.github/screenshots/screenshot1.png and b/.github/screenshots/screenshot1.png differ
diff --git a/.github/screenshots/screenshot2.png b/.github/screenshots/screenshot2.png
index 4c8ef5a..8b4e41f 100644
Binary files a/.github/screenshots/screenshot2.png and b/.github/screenshots/screenshot2.png differ
diff --git a/README.md b/README.md
index b49085f..d319a48 100644
--- a/README.md
+++ b/README.md
@@ -9,21 +9,24 @@
## Background
-This app uses the official media.ccc.de/graphql endpoint. It is built using React, Swiper, Mantine, React TV Player and ❤️ from Leipzig.
+This app uses the official media.ccc.de/graphql endpoint. It is built using React, Embla, Mantine, React TV Player and ❤️ from Leipzig.
## Features
- Support for conferences lazy loading.
+- Preview of the events on scroll.
- Support for language change.
## Roadmap
-- ☐ Switch to Swiper virtual slides to optimise the DOM
-- ☐ Add Search functionnality
+- ☐ Add Search functionnality.
+- ☐ Add Watchlist functionnality.
+- ☐ Add configuration page.
+- ☐ Add substitles functionnality.
## Contributions
-Either you found a bug, optimisation strategy or want something implemented, go ahead and hack your way into the code, PRs are welcome.
+Either you found a bug, optimisation strategy or want something implemented, go ahead and hack your way into the code, PRs are welcome 🌱.
## Development
@@ -31,7 +34,7 @@ Either you found a bug, optimisation strategy or want something implemented, go
```sh
yarm install
-# Serve development build on http://127.0.0.1:3000
+# Serve development build on http://127.0.0.1:3333
yarn start
# Production build (dumped into dist/)
diff --git a/assets/appinfo.json b/assets/appinfo.json
index 255d74a..da32533 100644
--- a/assets/appinfo.json
+++ b/assets/appinfo.json
@@ -1,6 +1,6 @@
{
"id": "org.webosbrew.cccbib",
- "version": "0.1.0",
+ "version": "1.0.0",
"vendor": "volkovmqx",
"type": "web",
"main": "index.html",
@@ -9,5 +9,6 @@
"appDescription": "media.ccc.de library application (EXPERIMENTAL)",
"icon": "icon80.png",
"splashBackground": "splash.png",
- "largeIcon": "icon130.png"
+ "largeIcon": "icon130.png",
+ "disableBackHistoryAPI": true
}
diff --git a/package.json b/package.json
index f6ab999..6ef21de 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "org.webosbrew.cccbib",
- "version": "0.1.0",
+ "version": "1.0.0",
"description": "media.ccc.de library application (EXPERIMENTAL)",
"main": "index.js",
"scripts": {
@@ -37,12 +37,12 @@
"@mantine/hooks": "^7.4.2",
"@tabler/icons-react": "^2.46.0",
"core-js": "^3.20.1",
+ "embla-carousel-react": "^8.0.0-rc20",
"graphql": "^16.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.2.1",
"react-tv-player": "^1.1.4",
- "regenerator-runtime": "^0.14.1",
- "swiper": "^11.0.5"
+ "regenerator-runtime": "^0.14.1"
}
}
diff --git a/src/App.js b/src/App.js
index ec6f172..31290c7 100644
--- a/src/App.js
+++ b/src/App.js
@@ -6,13 +6,11 @@ import {
} from 'react-router-dom';
import MainView from './views/Main';
-import EventView from './views/Event';
const App = () => (
} />
- } />
);
diff --git a/src/components/EventCarousel.js b/src/components/EventCarousel.js
new file mode 100644
index 0000000..2732f87
--- /dev/null
+++ b/src/components/EventCarousel.js
@@ -0,0 +1,35 @@
+import React, { useEffect, useState } from 'react';
+import useEmblaCarousel from 'embla-carousel-react'
+import { Title } from '@mantine/core';
+
+export function EventCarousel({ eventApis, eventRefs, events, activeEvent, ci, conferenceTitle }) {
+ const [emblaRef, emblaApi] = useEmblaCarousel({align: 'start', startIndex: activeEvent || 0});
+ useEffect(() => {
+ if (emblaRef && emblaApi) {
+ eventRefs.current[ci] = emblaRef;
+ eventApis.current[ci] = emblaApi;
+ }
+ }, [emblaRef, emblaApi]);
+
+ return (
+ <>
+
{conferenceTitle}
+
+
+
+ {events.lectures.nodes.map((e, ei) => (
+
+
+
+ ))}
+
+
+
+ >
+ )
+ }
\ No newline at end of file
diff --git a/src/components/Home.js b/src/components/Home.js
new file mode 100644
index 0000000..8bc47d3
--- /dev/null
+++ b/src/components/Home.js
@@ -0,0 +1,103 @@
+import React, { useState, useRef, useEffect } from 'react';
+import { GET_RECENT_CONFERENCES } from '../data';
+import { Center, Loader, Container, Box } from '@mantine/core';
+import { useWindowEvent, useListState } from '@mantine/hooks';
+import { useQuery } from '@apollo/client';
+import { Preview } from '../components/Preview';
+import { Player } from '../components/Player';
+import { handleArrowDown, handleArrowUp, handleArrowLeft, handleArrowRight } from '../helpers/helpers';
+import { EventCarousel } from '../components/EventCarousel';
+
+import '../styles.css';
+
+export const Home = () => {
+ const [playerIsOpen, setPlayerIsOpen] = useState(false)
+ const [activeSlice, setActiveSlice] = useState(0)
+ const [dataSlice, dataSliceHandlers] = useListState([]);
+ const [isLoading, setIsLoading] = useState(false)
+ const [activeEvents, setActiveEvents] = useState({ 0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 });
+
+ const { loading, error, data, fetchMore } = useQuery(GET_RECENT_CONFERENCES, {
+ variables: { offset: 0 },
+ });
+
+ useEffect(() => {
+ if (!data) return
+ if (data.conferencesRecent) {
+ if (activeSlice == 0) {
+ dataSliceHandlers.setState(data.conferencesRecent)
+ } else {
+ dataSliceHandlers.setState(data.conferencesRecent.slice(activeSlice, activeSlice + 6))
+ }
+ setIsLoading(false)
+ //setActiveSlice(activeSlice + 1)
+ }
+ }, [data])
+
+ const eventRefs = useRef([]);
+ const eventApis = useRef([]);
+
+
+ useWindowEvent('keydown', (e) => {
+ if (!data) return
+ if ((e.key === 'Escape' || e.key === 'Backspace' || e.keyCode == '461') && playerIsOpen) {
+ // close the Player
+ e.preventDefault()
+ setPlayerIsOpen(false)
+
+ } else if (e.key === 'Enter' && !playerIsOpen) {
+ // go to the Event page
+ setPlayerIsOpen(true)
+ } else if (e.key === 'ArrowRight' && !playerIsOpen) {
+ handleArrowRight(eventApis, activeEvents, activeSlice, setActiveEvents)
+ } else if (e.key === 'ArrowLeft' && !playerIsOpen) {
+ handleArrowLeft(eventApis, activeEvents, activeSlice, setActiveEvents)
+ } else if (e.key === 'ArrowDown' && !playerIsOpen) {
+
+ handleArrowDown(setActiveSlice, dataSlice, dataSliceHandlers, data, setIsLoading, fetchMore)
+ } else if (e.key === 'ArrowUp' && !playerIsOpen) {
+ handleArrowUp(setActiveSlice, dataSliceHandlers, data)
+ }
+ })
+ if (loading || dataSlice.length == 0) return ;
+ if (error) return Error : {error.message}
;
+ if (playerIsOpen) return
+ return (
+
+
+
+
+
+
+ {dataSlice.map((c, ci) => (
+
+
+
+ ))}
+ {isLoading && (
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/src/components/ItemCard.js b/src/components/ItemCard.js
deleted file mode 100644
index a45f745..0000000
--- a/src/components/ItemCard.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import React from 'react';
-import { IconEye, IconPlayerPlay, IconTimeDuration0 } from '@tabler/icons-react';
-import { Card, Text, Group, Center, rem, useMantineTheme } from '@mantine/core';
-import classes from './ItemCard.module.css';
-import { Link } from 'react-router-dom';
-
-const formatSeconds = s => [parseInt(s / 60 / 60), parseInt(s / 60 % 60), parseInt(s % 60)].join(':').replace(/\b(\d)\b/g, '0$1');
-
-export function ItemCard({title, image, persons, duration, viewCount, active, guid}) {
- const theme = useMantineTheme();
- return (
-
-
-
- {active && (
-
-
-
- )}
-
-
-
- {title}
-
-
-
-
- {persons.map((p) => p + " ")}
-
-
-
-
-
-
- {viewCount}
-
-
-
-
-
- {formatSeconds(duration)}
-
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/ItemCard.module.css b/src/components/ItemCard.module.css
deleted file mode 100644
index b03d6d6..0000000
--- a/src/components/ItemCard.module.css
+++ /dev/null
@@ -1,55 +0,0 @@
-.card {
- height: 400px;
- width: 300px;
- margin: 20px 5px 20px 5px;
- background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
-}
-.active {
- box-shadow: rgb(255, 255, 255) 0px 0px 0px 5px ;
-}
-
-.image {
- background-position: center;
- background-size: cover;
-}
-.activeplay {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- color: var(--mantine-color-white);
- font-size: 50px;
- z-index: 1;
-}
-
-.overlay {
- position: absolute;
- top: 20%;
- left: 0;
- right: 0;
- bottom: 0;
- background-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 90%);
-}
-
-.content {
- height: 100%;
- position: relative;
- display: flex;
- flex-direction: column;
- justify-content: flex-end;
- z-index: 1;
-}
-
-.title {
- color: var(--mantine-color-white);
- margin-bottom: 5px;
-}
-
-.bodyText {
- color: var(--mantine-color-dark-2);
- margin-left: 7px;
-}
-
-.author {
- color: var(--mantine-color-dark-2);
-}
diff --git a/src/components/Logo.js b/src/components/Logo.js
new file mode 100644
index 0000000..87754c4
--- /dev/null
+++ b/src/components/Logo.js
@@ -0,0 +1,10 @@
+import React from 'react';
+
+export const Logo = () => {
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/Player.js b/src/components/Player.js
new file mode 100644
index 0000000..669877f
--- /dev/null
+++ b/src/components/Player.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import { TVPlayer } from "react-tv-player";
+import { faGlobe, faClockRotateLeft } from "@fortawesome/free-solid-svg-icons";
+import { useLocalStorage } from "@mantine/hooks";
+import { getVideo } from '../helpers/helpers';
+
+export function Player({ event }) {
+ const [language, setLanguage] = useLocalStorage({
+ key: 'language',
+ defaultValue: 'deu',
+ });
+ const [recording, foundLanguage] = getVideo(event.videos)
+
+ const customButtons = [
+ { action: "skipback", align: "center", faIcon: faClockRotateLeft },
+ { action: "playpause", align: "center" },
+ { action: "skipforward", align: "center", faIcon: faClockRotateLeft },
+ { action: "mute", align: "right" },
+ {
+ action: "custom",
+ align: "left",
+ label: "Switch to" + (language === "deu" ? " English" : " German"),
+ faIcon: faGlobe,
+ onPress: () => {
+ setLanguage(language === "deu" ? "eng" : "deu");
+ },
+ },
+ ];
+
+ if (!foundLanguage) {
+ // deactivate the language switch button
+ customButtons[4].disabled = true;
+ }
+ if (recording) {
+ return (
+
+ );
+ } else {
+ return 404
;
+ }
+}
diff --git a/src/components/Preview.js b/src/components/Preview.js
new file mode 100644
index 0000000..69d4667
--- /dev/null
+++ b/src/components/Preview.js
@@ -0,0 +1,63 @@
+import React from 'react';
+import { getVideo } from '../helpers/helpers';
+import { Box, Title, Group, Center, Text, Space } from '@mantine/core';
+import { IconEye, IconClock, IconUser } from '@tabler/icons-react';
+import { formatSeconds } from '../helpers/helpers';
+
+import { Logo } from './Logo'
+export function Preview({ event, conferenceTitle }) {
+ const [recording] = getVideo(event.videos)
+
+ return (
+
+
+
+
+
{event.title}
+
+
+
+
+ {event.viewCount}
+
+
+
+
+
+ {formatSeconds(event.duration)}
+
+
+
+
+
+ {event.persons.join(', ')}
+
+
+
+ {event.description}
+
+
+
+
+ {recording && recording.url && (
+
+ )}
+ {!recording && (
+
+ No recording available
+
+ )}
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/data.js b/src/data.js
index edee669..8269449 100644
--- a/src/data.js
+++ b/src/data.js
@@ -3,7 +3,7 @@ import { gql } from '@apollo/client';
export const GET_RECENT_CONFERENCES = gql`
query conferencesRecent($offset: Int) {
- conferencesRecent(offset: $offset, first: 3) {
+ conferencesRecent(offset: $offset, first: 6) {
id
title
slug
@@ -12,14 +12,16 @@ export const GET_RECENT_CONFERENCES = gql`
scheduleUrl
updatedAt
eventLastReleasedAt
- lectures {
+ lectures {
nodes {
guid
title
persons
duration
+ description
viewCount
images {
+ thumbUrl
posterUrl
}
videos {
diff --git a/src/helpers/helpers.js b/src/helpers/helpers.js
new file mode 100644
index 0000000..f41efc5
--- /dev/null
+++ b/src/helpers/helpers.js
@@ -0,0 +1,88 @@
+import { useLocalStorage } from "@mantine/hooks";
+
+
+export function getVideo(videos) {
+ const [language] = useLocalStorage({
+ key: 'language',
+ defaultValue: 'deu',
+ });
+ let recording = videos.find(
+ (s) => (s.language === language && s.mimeType === "video/mp4" && s.url)
+ );
+ let foundLanguage = true;
+ // check if another language is available
+ let otherLanguage = language === "deu" ? "eng" : "deu";
+ const otherLanguageAvailable = videos.find(
+ (s) => (s.language === otherLanguage && s.mimeType === "video/mp4" && s.url)
+ );
+ // video in language not found, try to find any video
+ if (!recording) {
+ recording = videos.find(
+ (s) => s.mimeType === "video/mp4" && s.url
+ );
+ foundLanguage = false;
+ }
+ return [recording, foundLanguage];
+}
+export const formatSeconds = s => [parseInt(s / 60 / 60), parseInt(s / 60 % 60), parseInt(s % 60)].join(':').replace(/\b(\d)\b/g, '0$1');
+
+export const handleArrowRight = (eventApis, activeEvents, activeSlice, setActiveEvents) => {
+ eventApis.current[0].scrollNext()
+ if (eventApis.current[0].canScrollNext()) {
+ const newActiveEvents = {...activeEvents, [activeSlice]: eventApis.current[0].selectedScrollSnap()}
+ setActiveEvents(newActiveEvents)
+ } else {
+ const eventSlides = eventApis.current[0].slidesInView()
+ const newActiveEvents = {...activeEvents, [activeSlice]: ((activeEvents[activeSlice] || 0) + 1) % eventSlides.length}
+ setActiveEvents(newActiveEvents)
+ }
+}
+
+export const handleArrowLeft = (eventApis, activeEvents, activeSlice, setActiveEvents) => {
+ eventApis.current[0].scrollPrev()
+ if (eventApis.current[0].canScrollPrev()) {
+ const newActiveEvents = {...activeEvents, [activeSlice]: eventApis.current[0].selectedScrollSnap()}
+ setActiveEvents(newActiveEvents)
+ } else {
+ if((activeEvents[activeSlice] || 0) == 0) {
+ console.log("cannot scroll prev -- open the sidebar")
+ } else {
+ const newActiveEvent = activeEvents[activeSlice] - 1
+ const newActiveEvents = {...activeEvents, [activeSlice]: newActiveEvent}
+ setActiveEvents(newActiveEvents)
+ }
+
+ }
+}
+export const handleArrowDown = (setActiveSlice, dataSlice, dataSliceHandlers, data, setIsLoading, fetchMore) => {
+ setActiveSlice((activeSlice) => {
+ if (dataSlice.length >= 2) {
+ dataSliceHandlers.shift()
+ if (dataSlice.length - 2 == 1) {
+ setIsLoading(true)
+ fetchMore({
+ variables: {
+ offset: data.conferencesRecent.length,
+ },
+ })
+ }
+
+ return activeSlice + 1
+ }
+ return activeSlice
+ })
+
+}
+
+export const handleArrowUp = (setActiveSlice, dataSliceHandlers, data) => {
+ setActiveSlice((activeSlice) => {
+ if (activeSlice - 1 >= 0) {
+ dataSliceHandlers.prepend(data.conferencesRecent[activeSlice - 1])
+ return activeSlice - 1
+
+ }
+ return activeSlice
+ })
+
+}
+
diff --git a/src/index.js b/src/index.js
index cc0928d..e2bec0d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -3,10 +3,13 @@ import 'core-js/stable';
import React from 'react';
import '@mantine/core/styles.css';
-import { createTheme, MantineProvider } from '@mantine/core';
+
+import { MantineProvider, createTheme } from '@mantine/core';
import { createRoot } from 'react-dom/client';
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
+const theme = createTheme();
+
const cache = new InMemoryCache({
typePolicies: {
Query: {
@@ -43,14 +46,11 @@ const client = new ApolloClient({
import App from './App';
-const theme = createTheme({
- /** Put your mantine theme override here */
-});
const container = document.getElementById('app');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(
-
+
diff --git a/src/styles.css b/src/styles.css
index eff658e..f60652f 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -1,19 +1,363 @@
-#app {
- height: 100%;
+.theme-dark {
+ --brand-primary: rgb(138, 180, 248);
+ --brand-secondary: rgb(193, 168, 226);
+ --brand-alternative: rgb(136, 186, 191);
+ --background-site: rgb(0, 0, 0);
+ --background-code: rgb(12, 12, 12);
+ --text-body: rgb(222, 222, 222);
+ --text-comment: rgb(170, 170, 170);
+ --text-high-contrast: rgb(230, 230, 230);
+ --text-medium-contrast: rgb(202, 202, 202);
+ --text-low-contrast: rgb(170, 170, 170);
+ --detail-high-contrast: rgb(101, 101, 101);
+ --detail-medium-contrast: rgb(25, 25, 25);
+ --detail-low-contrast: rgb(21, 21, 21);
+ --admonition-note: rgb(138, 180, 248);
+ --admonition-warning: rgb(253, 186, 116);
+ --admonition-danger: rgb(220, 38, 38);
+ --brand-primary-rgb-value: 138, 180, 248;
+ --brand-secondary-rgb-value: 193, 168, 226;
+ --brand-alternative-rgb-value: 136, 186, 191;
+ --background-site-rgb-value: 0, 0, 0;
+ --background-code-rgb-value: 12, 12, 12;
+ --text-body-rgb-value: 222, 222, 222;
+ --text-comment-rgb-value: 170, 170, 170;
+ --text-high-contrast-rgb-value: 230, 230, 230;
+ --text-medium-contrast-rgb-value: 202, 202, 202;
+ --text-low-contrast-rgb-value: 170, 170, 170;
+ --detail-high-contrast-rgb-value: 101, 101, 101;
+ --detail-medium-contrast-rgb-value: 25, 25, 25;
+ --detail-low-contrast-rgb-value: 21, 21, 21;
+ --admonition-note-rgb-value: 138, 180, 248;
+ --admonition-warning-rgb-value: 253, 186, 116;
+ --admonition-danger-rgb-value: 220, 38, 38;
+}
+html {
+ box-sizing: border-box;
+ line-height: 1.15;
+ -webkit-text-size-adjust: 100%;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+*,
+*::before,
+*::after {
+ box-sizing: inherit;
+}
+html,
+body,
+p,
+ol,
+ul,
+li,
+dl,
+dt,
+dd,
+blockquote,
+figure,
+fieldset,
+legend,
+textarea,
+pre,
+iframe,
+hr,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ margin: 0;
+ padding: 0;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size: 100%;
+ font-weight: 400;
+}
+ul {
+ list-style: none;
+}
+:root {
+ -moz-tab-size: 4;
+ tab-size: 4;
+}
+hr {
+ height: 0;
+}
+abbr[title] {
+ text-decoration: underline dotted;
+}
+b,
+strong {
+ font-weight: bolder;
+}
+code,
+kbd,
+samp,
+pre {
+ font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier,
+ monospace;
+ font-size: 1em;
+}
+small {
+ font-size: 80%;
+}
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+sub {
+ bottom: -0.25em;
+ top: -0.5em;
+}
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit;
+ font-size: 100%;
+ line-height: 1.15;
+ margin: 0;
+}
+button,
+select {
+ text-transform: none;
+}
+button,
+[type='button'],
+[type='reset'],
+[type='submit'] {
+ -webkit-appearance: none;
+ appearance: none;
+}
+button::-moz-focus-inner,
+[type='button']::-moz-focus-inner,
+[type='reset']::-moz-focus-inner,
+[type='submit']::-moz-focus-inner {
+ border-style: none;
+ padding: 0;
+}
+button:-moz-focusring,
+[type='button']:-moz-focusring,
+[type='reset']:-moz-focusring,
+[type='submit']:-moz-focusring {
+ outline: 1px dotted ButtonText;
+}
+img,
+embed,
+iframe,
+object,
+audio,
+video {
+ height: auto;
+ max-width: 100%;
+}
+html {
+ background-color: #000;
+ font-size: 62.5%;
+}
+body {
+ background-color: #000;
+ color: #fff;
+ font-size: 1.6rem;
+ line-height: 1.65;
+ padding: 0!important;
+ margin: 0!important;
+}
+html {
+ font-family: 'system-ui', -apple-system, BlinkMacSystemFont, 'Segoe UI',
+ Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
+ 'Segoe UI Symbol';
+ letter-spacing: -0.02rem;
+}
+@supports (font-variation-settings: normal) {
+ html {
+ font-family: 'Inter var', 'system-ui', -apple-system, BlinkMacSystemFont,
+ 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji',
+ 'Segoe UI Emoji', 'Segoe UI Symbol';
}
-
- .swiper {
- width: 100%;
- height: 100%;
- }
-
- .swiper-slide {
- height: auto;
- }
- .swiper-event-slide {
- width: 310px;
- }
- .conference {
- margin-top: 10px;
- margin-bottom: 10px;
- }
\ No newline at end of file
+}
+
+.loaderContainer {
+ transform: translate(-50%, -50%);
+ position: absolute;
+ top: 50%;
+ left: 50%;
+}
+.skeleton {
+ width: 400px;
+ height: 225px;
+ border-radius: 1rem;
+ position: relative;
+ background-color: #525252;
+}
+.embla {
+ --slide-spacing: 1rem;
+ --slide-size: 400px;
+ --slide-height: 225px;
+ padding: 0;
+ margin-bottom: 20px;
+}
+.embla_vertical {
+ --slide-spacing: 0;
+ --slide-size: 225px;
+ --slide-height: 225px;
+ padding: 0;
+}
+.embla__viewport {
+ overflow: hidden;
+}
+.embla__container {
+ backface-visibility: hidden;
+ display: flex;
+}
+
+.embla__container_vertical {
+ flex-direction: column;
+ height: calc(100vh - 450px);
+}
+.embla__slide {
+ flex: 0 0 var(--slide-size);
+ min-width: 0;
+ padding-left: var(--slide-spacing);
+ position: relative;
+
+
+}
+.embla__slide__img {
+ display: block;
+ height: var(--slide-height);
+ width: 100%;
+ object-fit: cover;
+ opacity: 0.5;
+ border-radius: 1rem;
+ border: 3px solid #fff;
+}
+.embla__slide__number {
+ width: 4.6rem;
+ height: 4.6rem;
+ z-index: 1;
+ position: absolute;
+ top: 0.6rem;
+ right: 0.6rem;
+ border-radius: 50%;
+ background-color: rgba(var(--background-site-rgb-value), 0.85);
+ line-height: 4.6rem;
+ font-weight: 900;
+ text-align: center;
+ pointer-events: none;
+}
+.embla__slide__number > span {
+ color: #fff;
+ background-image: linear-gradient(
+ 45deg,
+ var(--brand-primary),
+ var(--brand-secondary)
+ );
+ background-clip: text;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ font-size: 1.6rem;
+ display: block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+.previewContainer {
+ position: relative;
+}
+.active .embla__slide__img {
+ opacity: 1;
+ border: 10px solid #AAF40D;
+
+}
+
+.preview {
+ width: 880px;
+ height: 495px;
+ z-index: 999;
+ position: absolute;
+ right: 0;
+}
+
+.container {
+ background-color: #000;
+ width: 100%;
+ position: fixed;
+ z-index: 99;
+ top: 450px;
+}
+
+.previewOverlay {
+ display: block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: linear-gradient(90deg, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 90%),
+ linear-gradient(0deg, rgba(0, 0, 0, 1) 1%, rgba(0, 0, 0, 0) 40%);
+ z-index: 9999;
+}
+.title {
+ display: block;
+ position: absolute;
+ top: 40px;
+ left: 20px;
+ z-index: 99999;
+ width: 1200px;
+}
+.preview .player {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+}
+.conferenceTitle {
+ position: absolute;
+ top: 360px;
+}
+.noRecording {
+ position: absolute;
+ top: 150px;
+ left: 260px;
+ font-size: 48px;
+ z-index: 99999;
+}
+
+.actionButton {
+ width: 50px;
+ height: 50px;
+ margin: 20px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #fff;
+}
+.tv-player>* {
+ z-index: 9999;
+}
+.tv-player {
+ position: initial;
+}
+.mantine-AppShell-navbar {
+ width: 110px;
+ height: 100vh;
+ padding: 20px;
+ display: flex;
+ flex-direction: row;
+ background-color: #000;
+}
+[data-testid="skipforward"] svg {
+ transform: scaleX(-1)
+}
\ No newline at end of file
diff --git a/src/views/Event.js b/src/views/Event.js
deleted file mode 100644
index befdca3..0000000
--- a/src/views/Event.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import React, { useEffect } from 'react';
-import { useParams } from 'react-router';
-import { useQuery } from '@apollo/client';
-import { GET_LECTURE } from '../data';
-import { TVPlayer } from "react-tv-player";
-import { faGlobe } from "@fortawesome/free-solid-svg-icons";
-import { useLocalStorage } from "@mantine/hooks";
-import { Center, Loader } from "@mantine/core";
-
-export default function Event({}) {
- let params = useParams();
- const [language, setLanguage] = useLocalStorage({
- key: 'language',
- defaultValue: 'deu',
- });
-
- const { loading, error, data } = useQuery(GET_LECTURE, {
- variables: { id: params.id },
- });
- const customButtons = [
- { action: "skipback", align: "center" },
- { action: "playpause", align: "center" },
- { action: "skipforward", align: "center" },
- { action: "mute", align: "right" },
- {
- action: "custom",
- align: "left",
- label: "Switch to" + (language === "deu" ? " English" : " German"),
- faIcon: faGlobe,
- onPress: () => {
- setLanguage(language === "deu" ? "eng" : "deu");
- },
- },
- ];
-
-
- if (loading) return ;
-
- if (error) return Error : {error.message}
;
-
-
- let recording = data.lecture.videos.find(
- (s) => (s.language === language && s.mimeType === "video/mp4" && s.url)
- );
- // check if another language is available
- let otherLanguage = language === "deu" ? "eng" : "deu";
- const otherLanguageAvailable = data.lecture.videos.find(
- (s) => (s.language === otherLanguage && s.mimeType === "video/mp4" && s.url)
- );
- if( !otherLanguageAvailable ) {
- // deactivate the language switch button
- customButtons[4].disabled = true;
- }
- // video in language not found, try to find any video
- if (!recording) {
- recording = data.lecture.videos.find(
- (s) => s.mimeType === "video/mp4" && s.url
- );
- // deactivate the language switch button
- customButtons[4].disabled = true;
- }
- if (recording) {
- return (
-
- );
- } else {
- return 404
;
- }
-}
diff --git a/src/views/Main.js b/src/views/Main.js
index 7d28d06..59a9e9e 100644
--- a/src/views/Main.js
+++ b/src/views/Main.js
@@ -1,139 +1,33 @@
-import React, { useState } from 'react';
-// Import Swiper React components
-import { Swiper, SwiperSlide } from 'swiper/react';
-import { GET_RECENT_CONFERENCES } from '../data';
-import { Container, Group, Image, Box, Center, Loader, Text } from '@mantine/core';
-import { useWindowEvent, useListState, } from '@mantine/hooks';
-import { ItemCard } from '../components/ItemCard';
-import { useQuery } from '@apollo/client';
-import { useNavigate } from "react-router-dom";
-
-
-// Import Swiper styles
-import 'swiper/css';
-
-
-import '../styles.css';
+import React from 'react';
+import { Home } from '../components/Home';
+import { AppShell, Stack, ActionIcon } from '@mantine/core';
+import { IconHeart, IconInfoCircle, IconSearch, IconSettings } from '@tabler/icons-react';
export default function App() {
- const [isLoadingMore, setIsLoadingMore] = useState(false);
- const [initialLoaded, setInitialLoaded] = useState(false);
- const navigate = useNavigate();
- const [activeLecture, setActiveLecture] = useState(0);
- const [activeConference, setActiveConference] = useState(0);
- const { loading, error, data, fetchMore } = useQuery(GET_RECENT_CONFERENCES, {
- variables: { offset: 0, first: 3 },
- });
-
- const [conferencesSliderRef, setConferencesSliderRef] = useState(null);
- const [events, eventHandlers] = useListState([]);
-
-
- useWindowEvent('keydown', (e) => {
- if (!data) return
- if (e.key === 'Enter') {
- // go to the Event page
- navigate(`/event/${data.conferencesRecent[activeConference].lectures.nodes[activeLecture].guid}`)
-
- } else if (e.key === 'ArrowRight') {
- if (data.conferencesRecent[activeConference].lectures.nodes[activeLecture + 1]) {
- setActiveLecture(activeLecture + 1)
- events[activeConference].slideNext()
- }
- } else if (e.key === 'ArrowLeft') {
- if (data.conferencesRecent[activeConference].lectures.nodes[activeLecture - 1]) {
- setActiveLecture(activeLecture - 1)
- events[activeConference].slidePrev()
- }
- } else if (e.key === 'ArrowDown') {
- e.preventDefault()
- // load more if there is no more data
- if (!data.conferencesRecent[activeConference + 2]) {
- setIsLoadingMore(true)
- fetchMore({
- variables: {
- offset: data.conferencesRecent.length,
- }
- }).then(() => {
- setIsLoadingMore(false)
- });
- }
- else if (data.conferencesRecent[activeConference + 1] && events[activeConference + 1]) {
- setActiveLecture(0)
- setActiveConference(activeConference + 1)
- events[activeConference].slideTo(0, 0)
- conferencesSliderRef.slideNext()
- }
- } else if (e.key === 'ArrowUp') {
- e.preventDefault()
- if (data.conferencesRecent[activeConference - 1] && events[activeConference - 1]) {
- setActiveLecture(0)
- setActiveConference(activeConference - 1)
- events[activeConference].slideTo(0, 0)
- conferencesSliderRef.slidePrev()
- }
- }
- })
- console.log(loading)
- if (loading) return ;
- if (error) return Error : {error.message}
;
return (
-
- CCCBIB {isLoadingMore && }
- {
- if(initialLoaded) {
- setActiveLecture(0)
- setActiveConference(activeConference + 1)
- events[activeConference].slideTo(0, 0)
- conferencesSliderRef.slideNext()
- }
- else {
- setInitialLoaded(true)
- }
- }
- }
- >
- {data.conferencesRecent.map((c, ci) => (
-
-
-
-
- {c.title}
-
- eventHandlers.insert(ci, el)}
-
- >
- {c.lectures.nodes.map((l, li) => (
-
-
-
- ))}
-
-
-
- ))}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
-}
+}
\ No newline at end of file
diff --git a/tools/gen-manifest.js b/tools/gen-manifest.js
index c0fb7b4..09cf578 100644
--- a/tools/gen-manifest.js
+++ b/tools/gen-manifest.js
@@ -22,7 +22,6 @@ fs.writeFileSync(
iconUri:
'https://raw.githubusercontent.com/volkovmqx/cccbib/main/assets/icon130.png',
sourceUrl: 'https://github.com/volkovmqx/cccbib',
- rootRequired: true,
ipkUrl: ipkfile,
ipkHash: {
sha256: ipkhash,
diff --git a/webpack.config.js b/webpack.config.js
index 543a19a..c6447e5 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -47,6 +47,6 @@ module.exports = {
],
devServer: {
static: path.resolve(__dirname, './dist'),
- port: 3000,
+ port: 3333,
},
};
diff --git a/yarn.lock b/yarn.lock
index 2acc6ce..520ea7e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2081,7 +2081,7 @@ chrome-trace-event@^1.0.2:
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
-classnames@^2.3.2:
+classnames@^2.2.5, classnames@^2.3.2:
version "2.5.1"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
@@ -2456,6 +2456,11 @@ depd@~1.1.2:
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
+desandro-matches-selector@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/desandro-matches-selector/-/desandro-matches-selector-2.0.2.tgz#717beed4dc13e7d8f3762f707a6d58a6774218e1"
+ integrity sha512-+1q0nXhdzg1IpIJdMKalUwvvskeKnYyEe3shPRwedNcWtnhEKT3ZxvFjzywHDeGcKViIxTCAoOYQWP1qD7VNyg==
+
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
@@ -2524,6 +2529,29 @@ elfy@1.0.0:
dependencies:
endian-reader "^0.3.0"
+embla-carousel-class-names@^8.0.0-rc20:
+ version "8.0.0-rc20"
+ resolved "https://registry.yarnpkg.com/embla-carousel-class-names/-/embla-carousel-class-names-8.0.0-rc20.tgz#d9ee764405e79df1885590617135042ba84e9cd6"
+ integrity sha512-UfhlpyeSgecbLp0tsBIKmjRmv56kpkN/B1e9OtzP3ReH41fDDbvPK7L0L9ETPpJslIAZoTaSKSVU016/ajaxrg==
+
+embla-carousel-react@^8.0.0-rc20:
+ version "8.0.0-rc20"
+ resolved "https://registry.yarnpkg.com/embla-carousel-react/-/embla-carousel-react-8.0.0-rc20.tgz#0756fde4791b0f727dd90383c3cf798c17cdb539"
+ integrity sha512-02xhtl/qd5VQtzRbG3jQKVXy/YzP4J3nxQcJhz7cIY73nK3aPwxoZL+Fjk0VdS5eUIWowRBH5qIv3nVNsqeYZQ==
+ dependencies:
+ embla-carousel "8.0.0-rc20"
+ embla-carousel-reactive-utils "8.0.0-rc20"
+
+embla-carousel-reactive-utils@8.0.0-rc20:
+ version "8.0.0-rc20"
+ resolved "https://registry.yarnpkg.com/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.0.0-rc20.tgz#606c2708bdb95c4c3391a3a6cd01d3c7b81e36f3"
+ integrity sha512-fE7IeSS8HqwDnTDMP8eo0i4pcYQAemmJq53zCLXnp3Yj/p5+IpB1nC7aKQjd2ug1dGOSwwNRFaPI3shlAVVW/A==
+
+embla-carousel@8.0.0-rc20:
+ version "8.0.0-rc20"
+ resolved "https://registry.yarnpkg.com/embla-carousel/-/embla-carousel-8.0.0-rc20.tgz#6763e9db8db11a64d49f42ecdb15936a694fed7e"
+ integrity sha512-fhzhbIAcsjSpUsg5jWsg0+zVyJhY5x2SPXtuS4MPAWQWoVQpvkcbX9r0FvPBn6emTbgNFRtAcWczstJy2msdUw==
+
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@@ -2561,6 +2589,11 @@ enhanced-resolve@^5.15.0:
graceful-fs "^4.2.4"
tapable "^2.2.0"
+enquire.js@^2.1.6:
+ version "2.1.6"
+ resolved "https://registry.yarnpkg.com/enquire.js/-/enquire.js-2.1.6.tgz#3e8780c9b8b835084c3f60e166dbc3c2a3c89814"
+ integrity sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==
+
envinfo@^7.7.3:
version "7.11.0"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f"
@@ -2621,6 +2654,11 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
+ev-emitter@^1.0.0, ev-emitter@^1.0.1, ev-emitter@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ev-emitter/-/ev-emitter-1.1.1.tgz#8f18b0ce5c76a5d18017f71c0a795c65b9138f2a"
+ integrity sha512-ipiDYhdQSCZ4hSbX4rMW+XzNKMD1prg/sTvoVmSLkuQ1MVlwjJQQA+sW8tMYR3BLUr9KjodFV4pvzunvRhd33Q==
+
eventemitter3@^4.0.0:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
@@ -2884,11 +2922,30 @@ first-chunk-stream@^1.0.0:
resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e"
integrity sha512-ArRi5axuv66gEsyl3UuK80CzW7t56hem73YGNYxNWTGNKFJUadSb9Gu9SHijYEUi8ulQMf1bJomYNwSCPHhtTQ==
+fizzy-ui-utils@^2.0.7:
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/fizzy-ui-utils/-/fizzy-ui-utils-2.0.7.tgz#7df45dcc4eb374a08b65d39bb9a4beedf7330505"
+ integrity sha512-CZXDVXQ1If3/r8s0T+v+qVeMshhfcuq0rqIFgJnrtd+Bu8GmDmqMjntjUePypVtjHXKJ6V4sw9zeyox34n9aCg==
+ dependencies:
+ desandro-matches-selector "^2.0.0"
+
flat@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
+flickity@2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/flickity/-/flickity-2.3.0.tgz#6763eb4d9cfeee6eedd96d042a21fff4ad439944"
+ integrity sha512-x4cJBVywsaCWmId3I6wvBYJtWk3gcr+gz8UJQ48P57W5G7ER5OUgc3GUK0rtTrbMy/HYB9wL6u+I7EC4qrLO8g==
+ dependencies:
+ desandro-matches-selector "^2.0.0"
+ ev-emitter "^1.1.1"
+ fizzy-ui-utils "^2.0.7"
+ get-size "^2.0.3"
+ unidragger "^2.4.0"
+ unipointer "^2.4.0"
+
follow-redirects@^1.0.0:
version "1.15.5"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020"
@@ -2996,6 +3053,11 @@ get-nonce@^1.0.0:
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
+get-size@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/get-size/-/get-size-2.0.3.tgz#54a1d0256b20ea7ac646516756202769941ad2ef"
+ integrity sha512-lXNzT/h/dTjTxRbm9BXb+SGxxzkm97h/PCIKtlN/CBCxxmkkIVV21udumMS93MuVTDX583gqc94v3RjuHmI+2Q==
+
get-stream@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
@@ -3295,6 +3357,13 @@ ignore@^5.1.9:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78"
integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==
+imagesloaded@^4.1.4:
+ version "4.1.4"
+ resolved "https://registry.yarnpkg.com/imagesloaded/-/imagesloaded-4.1.4.tgz#1376efcd162bb768c34c3727ac89cc04051f3cc7"
+ integrity sha512-ltiBVcYpc/TYTF5nolkMNsnREHW+ICvfQ3Yla2Sgr71YFwQ86bDwV9hgpFhFtrGPuwEx5+LqOHIrdXBdoWwwsA==
+ dependencies:
+ ev-emitter "^1.0.0"
+
import-local@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
@@ -3548,6 +3617,13 @@ json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
+json2mq@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a"
+ integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==
+ dependencies:
+ string-convert "^0.2.0"
+
json5@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
@@ -4268,6 +4344,14 @@ react-fast-compare@^3.0.1:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49"
integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==
+react-flickity-component@^4.0.7:
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/react-flickity-component/-/react-flickity-component-4.0.7.tgz#e57987988f2bd0df83177fc7545a13ca2b252bad"
+ integrity sha512-Gn+MqLWeW2znG0sPAaaShckXKW1oXVR/RMnJd9EWUuZH33vxRVc7Q1gwpLGDKs0Aiq0D1/cmb8ltepEgDmu7Rw==
+ dependencies:
+ imagesloaded "^4.1.4"
+ prop-types "^15.7.2"
+
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -4325,6 +4409,17 @@ react-router@6.21.3:
dependencies:
"@remix-run/router" "1.14.2"
+react-slick@^0.29.0:
+ version "0.29.0"
+ resolved "https://registry.yarnpkg.com/react-slick/-/react-slick-0.29.0.tgz#0bed5ea42bf75a23d40c0259b828ed27627b51bb"
+ integrity sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==
+ dependencies:
+ classnames "^2.2.5"
+ enquire.js "^2.1.6"
+ json2mq "^0.2.0"
+ lodash.debounce "^4.0.8"
+ resize-observer-polyfill "^1.5.0"
+
react-style-singleton@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4"
@@ -4491,6 +4586,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
+resize-observer-polyfill@^1.5.0:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
+ integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
+
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@@ -4925,6 +5025,11 @@ streamsearch@~0.1.2:
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
integrity sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==
+string-convert@^0.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97"
+ integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==
+
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -5258,6 +5363,20 @@ unicode-property-aliases-ecmascript@^2.0.0:
resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd"
integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==
+unidragger@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/unidragger/-/unidragger-2.4.0.tgz#4cd7e564317af0ef42632d5984a82d4ae6314d8d"
+ integrity sha512-MueZK2oXuGE6OAlGKIrSXK2zCq+8yb1QUZgqyTDCSJzvwYL0g2Llrad+TtoQTYxtFnNyxxSw0IMnKNIgEMia1w==
+ dependencies:
+ unipointer "^2.4.0"
+
+unipointer@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/unipointer/-/unipointer-2.4.0.tgz#ac7316aff6170ff87a4b008e55e842fb4bf13181"
+ integrity sha512-VjzDLPjGK7aYpQKH7bnDZS8X4axF5AFU/LQi+NQe1oyEHfaz6lWKhaQ7n4o7vJ1iJ4i2T0quCIfrQM139p05Sw==
+ dependencies:
+ ev-emitter "^1.0.1"
+
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"