diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 1d2ce354..9f2b6958 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -1,4 +1,8 @@ +import c from "classnames"; +import { useState } from "react"; + import { MWQuery } from "@/backend/metadata/types/mw"; +import { Flare } from "@/components/utils/Flare"; import { Icon, Icons } from "./Icon"; import { TextInputControl } from "./text-inputs/TextInputControl"; @@ -11,6 +15,8 @@ export interface SearchBarProps { } export function SearchBarInput(props: SearchBarProps) { + const [focused, setFocused] = useState(false); + function setSearch(value: string) { props.onChange( { @@ -22,18 +28,42 @@ export function SearchBarInput(props: SearchBarProps) { } return ( -
-
- -
- - setSearch(val)} - value={props.value.searchQuery} - className="w-full flex-1 bg-transparent px-4 py-4 pl-12 text-white placeholder-denim-700 focus:outline-none sm:py-4 sm:pr-2" - placeholder={props.placeholder} + + -
+ + +
+ +
+ + { + setFocused(false); + props.onUnFocus(); + }} + onFocus={() => setFocused(true)} + onChange={(val) => setSearch(val)} + value={props.value.searchQuery} + className="text-search-text w-full flex-1 bg-transparent px-4 py-4 pl-12 placeholder-search-placeholder focus:outline-none sm:py-4 sm:pr-2" + placeholder={props.placeholder} + /> +
+ ); } diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx index 24f975fb..05b17a01 100644 --- a/src/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -22,6 +22,15 @@ function FooterLink(props: { ); } +function Dmca() { + const { t } = useTranslation(); + return ( + + {t("footer.links.dmca")} + + ); +} + export function Footer() { const { t } = useTranslation(); @@ -33,29 +42,27 @@ export function Footer() {

{t("footer.tagline")}

-
- - {t("footer.links.github")} - - - {t("footer.links.github")} - -

{t("footer.legal.disclaimer")}

{t("footer.legal.disclaimerText")}

-
- - {t("footer.links.dmca")} - +
+
+ + {t("footer.links.github")} + + + {t("footer.links.discord")} + +
+
+
+ +
); diff --git a/src/components/layout/Navigation.tsx b/src/components/layout/Navigation.tsx index a9a3e0c1..266735ce 100644 --- a/src/components/layout/Navigation.tsx +++ b/src/components/layout/Navigation.tsx @@ -3,6 +3,7 @@ import { Link } from "react-router-dom"; import { IconPatch } from "@/components/buttons/IconPatch"; import { Icons } from "@/components/Icon"; +import { Lightbar } from "@/components/utils/Lightbar"; import { useBannerSize } from "@/hooks/useBanner"; import { conf } from "@/setup/config"; import SettingsModal from "@/views/SettingsModal"; @@ -18,60 +19,67 @@ export function Navigation(props: NavigationProps) { const bannerHeight = useBannerSize(); const [showModal, setShowModal] = useState(false); return ( -
-
-
-
-
-
-
- - - -
- {props.children} -
-
- { - setShowModal(true); - }} - /> - - - - - - + <> +
+
+
- setShowModal(false)} /> -
+
+
+
+
+
+
+
+ + + +
+ {props.children} +
+
+ { + setShowModal(true); + }} + /> + + + + + + +
+
+ setShowModal(false)} /> +
+ ); } diff --git a/src/components/text-inputs/TextInputControl.tsx b/src/components/text-inputs/TextInputControl.tsx index a6d18994..c3b42616 100644 --- a/src/components/text-inputs/TextInputControl.tsx +++ b/src/components/text-inputs/TextInputControl.tsx @@ -1,6 +1,7 @@ export interface TextInputControlPropsNoLabel { onChange?: (data: string) => void; onUnFocus?: () => void; + onFocus?: () => void; value?: string; placeholder?: string; className?: string; @@ -17,6 +18,7 @@ export function TextInputControl({ label, className, placeholder, + onFocus, }: TextInputControlProps) { const input = ( onChange && onChange(e.target.value)} value={value} onBlur={() => onUnFocus && onUnFocus()} + onFocus={() => onFocus?.()} /> ); diff --git a/src/components/utils/Flare.css b/src/components/utils/Flare.css new file mode 100644 index 00000000..1c17cda8 --- /dev/null +++ b/src/components/utils/Flare.css @@ -0,0 +1,7 @@ +.flare-enabled .flare-light { + opacity: 1 !important; +} + +.hover\:flare-enabled:hover .flare-light { + opacity: 1 !important; +} diff --git a/src/components/utils/Flare.tsx b/src/components/utils/Flare.tsx index 680c3812..86ea0bfe 100644 --- a/src/components/utils/Flare.tsx +++ b/src/components/utils/Flare.tsx @@ -1,5 +1,6 @@ import c from "classnames"; -import { useEffect, useRef } from "react"; +import { ReactNode, useEffect, useRef } from "react"; +import "./Flare.css"; export interface FlareProps { className?: string; @@ -12,7 +13,15 @@ export interface FlareProps { const SIZE_DEFAULT = 200; const CSS_VAR_DEFAULT = "--colors-global-accentA"; -export function Flare(props: FlareProps) { +function Base(props: { className?: string; children?: ReactNode }) { + return
{props.children}
; +} + +function Child(props: { className?: string; children?: ReactNode }) { + return
{props.children}
; +} + +function Light(props: FlareProps) { const outerRef = useRef(null); const size = props.flareSize ?? SIZE_DEFAULT; const cssVar = props.cssColorVar ?? CSS_VAR_DEFAULT; @@ -20,13 +29,14 @@ export function Flare(props: FlareProps) { useEffect(() => { function mouseMove(e: MouseEvent) { if (!outerRef.current) return; + const rect = outerRef.current.getBoundingClientRect(); outerRef.current.style.setProperty( "--bg-x", - `${(e.clientX - size / 2).toFixed(0)}px` + `${(e.clientX - rect.left - size / 2).toFixed(0)}px` ); outerRef.current.style.setProperty( "--bg-y", - `${(e.clientY - size / 2).toFixed(0)}px` + `${(e.clientY - rect.top - size / 2).toFixed(0)}px` ); } document.addEventListener("mousemove", mouseMove); @@ -38,10 +48,10 @@ export function Flare(props: FlareProps) {
); } + +export const Flare = { + Base, + Light, + Child, +}; diff --git a/src/components/utils/Lightbar.css b/src/components/utils/Lightbar.css new file mode 100644 index 00000000..9cf845d4 --- /dev/null +++ b/src/components/utils/Lightbar.css @@ -0,0 +1,22 @@ +.lightbar { + position: absolute; + left: -25vw; + top: 0; + width: 150vw; + height: 800px; + pointer-events: none; + user-select: none; + --top: theme('colors.background.main'); + --bottom: theme('colors.lightBar.light'); + --first: conic-gradient(from 90deg at 80% 50%,var(--top),var(--bottom)); + --second: conic-gradient(from 270deg at 20% 50%,var(--bottom),var(--top)); + mask-image: radial-gradient(100% 50% at center center, black, transparent); + background-image: var(--first), var(--second); + background-position-x: 1%, 99%; + background-position-y: 0%, 0%; + background-size: 50% 100%, 50% 100%; + opacity: 1; + transform: rotate(180deg) translateZ(0px) translateY(400px); + transform-origin: center center; + background-repeat: no-repeat; +} diff --git a/src/components/utils/Lightbar.tsx b/src/components/utils/Lightbar.tsx new file mode 100644 index 00000000..604f7cba --- /dev/null +++ b/src/components/utils/Lightbar.tsx @@ -0,0 +1,9 @@ +import "./Lightbar.css"; + +export function Lightbar(props: { className?: string }) { + return ( +
+
+
+ ); +} diff --git a/src/setup/App.tsx b/src/setup/App.tsx index 6bb6b957..b3a49685 100644 --- a/src/setup/App.tsx +++ b/src/setup/App.tsx @@ -17,7 +17,6 @@ import { SettingsProvider } from "@/state/settings"; import { WatchedContextProvider } from "@/state/watched"; import { MediaView } from "@/views/media/MediaView"; import { NotFoundPage } from "@/views/notfound/NotFoundView"; -import { V2MigrationView } from "@/views/other/v2Migration"; import { SearchView } from "@/views/search/SearchView"; function LegacyUrlView({ children }: { children: ReactElement }) { @@ -62,7 +61,6 @@ function App() { {/* functional routes */} - diff --git a/src/setup/index.css b/src/setup/index.css index 259aaa61..168ce2ea 100644 --- a/src/setup/index.css +++ b/src/setup/index.css @@ -4,9 +4,10 @@ html, body { - @apply bg-denim-100 font-open-sans text-denim-700 overflow-x-hidden; + @apply bg-background-main font-open-sans text-denim-700 overflow-x-hidden; min-height: 100vh; min-height: 100dvh; + position: relative; } html[data-full], @@ -198,4 +199,4 @@ input[type=range].styled-slider.slider-progress::-ms-fill-lower { ::-webkit-scrollbar { /* For some reason the styles don't get applied without the width */ width: 13px; -} \ No newline at end of file +} diff --git a/src/setup/locales/en/translation.json b/src/setup/locales/en/translation.json index f78adc46..e11e67de 100644 --- a/src/setup/locales/en/translation.json +++ b/src/setup/locales/en/translation.json @@ -10,7 +10,7 @@ "headingTitle": "Search results", "bookmarks": "Bookmarks", "continueWatching": "Continue Watching", - "title": "What do you want to watch?", + "title": "What to watch tonight?", "placeholder": "What do you want to watch?" }, "media": { @@ -136,7 +136,8 @@ "tagline": "Watch your favorite shows and movies with this open source streaming app.", "links": { "github": "GitHub", - "dmca": "DMCA" + "dmca": "DMCA", + "discord": "Discord" }, "legal": { "disclaimer": "Disclaimer", diff --git a/src/views/HomePage.tsx b/src/views/HomePage.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/views/SearchPart.tsx b/src/views/SearchPart.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/views/other/v2Migration.tsx b/src/views/other/v2Migration.tsx deleted file mode 100644 index d0b05e42..00000000 --- a/src/views/other/v2Migration.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import pako from "pako"; -import { useEffect, useState } from "react"; - -import { MWMediaType } from "@/backend/metadata/types/mw"; -import { conf } from "@/setup/config"; - -function fromBinary(str: string): Uint8Array { - const result = new Uint8Array(str.length); - [...str].forEach((char, i) => { - result[i] = char.charCodeAt(0); - }); - return result; -} - -export function importV2Data({ data, time }: { data: any; time: Date }) { - const savedTime = localStorage.getItem("mw-migration-date"); - if (savedTime) { - if (new Date(savedTime) >= time) { - // has already migrated this or something newer, skip - return false; - } - } - - // restore migration data - if (data.bookmarks) - localStorage.setItem("mw-bookmarks", JSON.stringify(data.bookmarks)); - if (data.videoProgress) - localStorage.setItem("video-progress", JSON.stringify(data.videoProgress)); - - localStorage.setItem("mw-migration-date", time.toISOString()); - - return true; -} - -export function EmbedMigration() { - let hasReceivedMigrationData = false; - - const onMessage = (e: any) => { - const data = e.data; - if (data && data.isMigrationData && !hasReceivedMigrationData) { - hasReceivedMigrationData = true; - const didImport = importV2Data({ - data: data.data, - time: data.date, - }); - if (didImport) window.location.reload(); - } - }; - - useEffect(() => { - window.addEventListener("message", onMessage); - - return () => { - window.removeEventListener("message", onMessage); - }; - }); - - return