mirror of
https://github.com/recloudstream/website.git
synced 2024-08-15 03:18:45 +00:00
improve tv support
This commit is contained in:
parent
006cff303e
commit
711823a824
9 changed files with 126 additions and 51 deletions
|
@ -1 +1,15 @@
|
||||||
|
import { init } from '@noriginmedia/norigin-spatial-navigation';
|
||||||
|
|
||||||
require("prism-themes/themes/prism-dracula.min.css")
|
require("prism-themes/themes/prism-dracula.min.css")
|
||||||
|
|
||||||
|
if (window !== undefined) {
|
||||||
|
init({
|
||||||
|
visualDebug: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// window.addEventListener("keypress", (evt) => {
|
||||||
|
// if (evt.keyCode === 461) {
|
||||||
|
// window.history.go(-1)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
20
package-lock.json
generated
20
package-lock.json
generated
|
@ -8,6 +8,7 @@
|
||||||
"name": "recloudstream",
|
"name": "recloudstream",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@noriginmedia/norigin-spatial-navigation": "^1.0.5",
|
||||||
"daisyui": "^2.24.0",
|
"daisyui": "^2.24.0",
|
||||||
"gatsby": "^4.21.1",
|
"gatsby": "^4.21.1",
|
||||||
"gatsby-plugin-html-attributes": "^1.0.5",
|
"gatsby-plugin-html-attributes": "^1.0.5",
|
||||||
|
@ -2917,6 +2918,17 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@noriginmedia/norigin-spatial-navigation": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noriginmedia/norigin-spatial-navigation/-/norigin-spatial-navigation-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-sLdigBBihZUtlexumYRrPkqg8P8SaSp+ZsLDEfDXdySP+/NK+uGCkRl7vi/GboTfDghdKt88KhwOa8sjvTuu5Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.21"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@parcel/bundler-default": {
|
"node_modules/@parcel/bundler-default": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.6.2.tgz",
|
||||||
|
@ -22844,6 +22856,14 @@
|
||||||
"fastq": "^1.6.0"
|
"fastq": "^1.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@noriginmedia/norigin-spatial-navigation": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noriginmedia/norigin-spatial-navigation/-/norigin-spatial-navigation-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-sLdigBBihZUtlexumYRrPkqg8P8SaSp+ZsLDEfDXdySP+/NK+uGCkRl7vi/GboTfDghdKt88KhwOa8sjvTuu5Q==",
|
||||||
|
"requires": {
|
||||||
|
"lodash": "^4.17.21"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@parcel/bundler-default": {
|
"@parcel/bundler-default": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.6.2.tgz",
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"clean": "gatsby clean"
|
"clean": "gatsby clean"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@noriginmedia/norigin-spatial-navigation": "^1.0.5",
|
||||||
"daisyui": "^2.24.0",
|
"daisyui": "^2.24.0",
|
||||||
"gatsby": "^4.21.1",
|
"gatsby": "^4.21.1",
|
||||||
"gatsby-plugin-html-attributes": "^1.0.5",
|
"gatsby-plugin-html-attributes": "^1.0.5",
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
import CompatBtn from "../compatbtn";
|
||||||
|
|
||||||
const RepoCard = ({ url, isFirst }) => {
|
const RepoCard = ({ url, isFirst }) => {
|
||||||
const [data, setData] = useState(null)
|
const [data, setData] = useState(null)
|
||||||
const firstButton = useRef(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isFirst) return;
|
|
||||||
console.log({firstButton})
|
|
||||||
firstButton.current?.focus()
|
|
||||||
}, [firstButton])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch(url)
|
fetch(url)
|
||||||
|
@ -30,21 +25,26 @@ const RepoCard = ({ url, isFirst }) => {
|
||||||
</p>
|
</p>
|
||||||
<div className="card-actions justify-end">
|
<div className="card-actions justify-end">
|
||||||
<div className="btn-group">
|
<div className="btn-group">
|
||||||
<button ref={firstButton} className="btn btn-primary" onClick={() => {
|
<CompatBtn
|
||||||
window.open(`cloudstreamrepo://${url.replace(/^https?:\/\//, "")}`)
|
autoFocus={isFirst}
|
||||||
}}>Install</button>
|
group={true}
|
||||||
<button className="btn" onClick={() => {
|
className="btn-primary"
|
||||||
if (navigator.clipboard) {
|
href={`cloudstreamrepo://${url.replace(/^https?:\/\//, "")}`}
|
||||||
navigator.clipboard.writeText(url);
|
target="_blank"
|
||||||
} else {
|
>Install</CompatBtn>
|
||||||
var tempInput = document.createElement("input");
|
<CompatBtn group={true}
|
||||||
tempInput.value = url;
|
onClick={() => {
|
||||||
document.body.appendChild(tempInput);
|
if (navigator.clipboard) {
|
||||||
tempInput.select();
|
navigator.clipboard.writeText(url);
|
||||||
document.execCommand("copy");
|
} else {
|
||||||
document.body.removeChild(tempInput);
|
var tempInput = document.createElement("input");
|
||||||
}
|
tempInput.value = url;
|
||||||
}}>Copy URL</button>
|
document.body.appendChild(tempInput);
|
||||||
|
tempInput.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
document.body.removeChild(tempInput);
|
||||||
|
}
|
||||||
|
}}>Copy URL</CompatBtn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
47
src/components/compatbtn.jsx
Normal file
47
src/components/compatbtn.jsx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useFocusable } from '@noriginmedia/norigin-spatial-navigation';
|
||||||
|
import { Link, navigate } from "gatsby";
|
||||||
|
|
||||||
|
|
||||||
|
function CompatBtn({autoFocus, href, onClick, group, target, children, className, ...props}) {
|
||||||
|
const { ref, focused, focusSelf} = useFocusable();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!autoFocus) return;
|
||||||
|
focusSelf()
|
||||||
|
}, [focusSelf])
|
||||||
|
|
||||||
|
if (onClick) href = "#!"
|
||||||
|
|
||||||
|
if (focused && ref.current) {
|
||||||
|
ref.current?.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window) {
|
||||||
|
window.addEventListener("keyup", (ev) => {
|
||||||
|
if (!focused) return;
|
||||||
|
if (document.activeElement !== ref.current) return;
|
||||||
|
if (ev.key === "Enter" || ev.key === " ") {
|
||||||
|
ref.current?.click()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target !== "_blank" && !group && !onClick) {
|
||||||
|
return <Link to={href} className={`btn ${className||""}`} ref={ref} {...props}>{children}</Link>
|
||||||
|
} else if (!group && !onClick) {
|
||||||
|
return <a href={href} target="_blank" className={`btn ${className||""}`} ref={ref} {...props}>{children}</a>
|
||||||
|
} else {
|
||||||
|
return <button className={`btn ${className||""}`} ref={ref} {...props} onClick={() => {
|
||||||
|
if (onClick) {
|
||||||
|
onClick()
|
||||||
|
} else if (target === "_blank") {
|
||||||
|
window.open(href)
|
||||||
|
} else {
|
||||||
|
navigate(href)
|
||||||
|
}
|
||||||
|
}}>{children}</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CompatBtn
|
|
@ -1,11 +1,12 @@
|
||||||
import React from "react"
|
import React from "react"
|
||||||
|
import { Link } from "gatsby"
|
||||||
|
|
||||||
const Button = ({url, children, name}) => (
|
const Button = ({url, children, name}) => (
|
||||||
<span className="tooltip tooltip-bottom before:text-xs before:content-[attr(data-tip)]" data-tip={name}>
|
<span className="tooltip tooltip-bottom before:text-xs before:content-[attr(data-tip)]" data-tip={name}>
|
||||||
<div className="flex-none items-center">
|
<div className="flex-none items-center">
|
||||||
<a className="btn btn-ghost drawer-button btn-square text-xl" href={url || "#!"}>
|
<Link className="btn btn-ghost drawer-button btn-square text-xl" to={url || "#!"}>
|
||||||
{children}
|
{children}
|
||||||
</a>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,24 +1,18 @@
|
||||||
import React, {useRef, useEffect} from "react"
|
import React from "react"
|
||||||
import Layout from "../components/layout"
|
import Layout from "../components/layout"
|
||||||
import Hero from "../components/hero"
|
import Hero from "../components/hero"
|
||||||
|
|
||||||
import bgImage from "../media/phones.png"
|
import bgImage from "../media/phones.png"
|
||||||
import { Link } from "gatsby"
|
import CompatBtn from "../components/compatbtn"
|
||||||
|
|
||||||
const NotFoundPage = () => {
|
const NotFoundPage = () => {
|
||||||
|
|
||||||
const firstBtn = useRef(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
firstBtn.current.focus()
|
|
||||||
}, [firstBtn])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Hero bg={bgImage}>
|
<Hero bg={bgImage}>
|
||||||
<h1 className="mb-5 text-5xl font-bold">Not found</h1>
|
<h1 className="mb-5 text-5xl font-bold">Not found</h1>
|
||||||
<p className="mb-5 text-lg">Sorry 😔. We couldn’t find what you were looking for.</p>
|
<p className="mb-5 text-lg">Sorry 😔. We couldn’t find what you were looking for.</p>
|
||||||
<Link ref={firstBtn} className="btn btn-primary" to="/">Home</Link>
|
<CompatBtn autoFocus={true} className="btn-primary" href="/">Home</CompatBtn>
|
||||||
</Hero>
|
</Hero>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,26 +1,21 @@
|
||||||
import React, {useEffect, useRef} from "react"
|
import React from "react"
|
||||||
|
|
||||||
import Layout from "../components/layout"
|
import Layout from "../components/layout"
|
||||||
import Hero from "../components/hero"
|
import Hero from "../components/hero"
|
||||||
|
import CompatBtn from "../components/compatbtn"
|
||||||
|
|
||||||
import bgImage from "../media/phones.png"
|
import bgImage from "../media/phones.png"
|
||||||
import { Link } from "gatsby"
|
import { Link } from "gatsby"
|
||||||
|
|
||||||
const IndexPage = () => {
|
const IndexPage = () => {
|
||||||
const firstBtn = useRef(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
firstBtn.current.focus()
|
|
||||||
}, [firstBtn])
|
|
||||||
|
|
||||||
return <Layout>
|
return <Layout>
|
||||||
<Hero bg={bgImage}>
|
<Hero bg={bgImage}>
|
||||||
<h1 className="mb-5 text-5xl font-bold">Hello there</h1>
|
<h1 className="mb-5 text-5xl font-bold">Hello there</h1>
|
||||||
<p className="mb-5 text-lg">Cloudstream is an Android app for streaming and downloading Movies, TV-Series and Anime.</p>
|
<p className="mb-5 text-lg">Cloudstream is an Android app for streaming and downloading Movies, TV-Series and Anime.</p>
|
||||||
<div className="flex justify-center w-full mb-5">
|
<div className="flex justify-center w-full mb-5">
|
||||||
<Link ref={firstBtn} className="btn btn-primary" to="/install">Install</Link>
|
<CompatBtn autoFocus={true} className="btn-primary" href="/install">Install</CompatBtn>
|
||||||
<div className="divider divider-horizontal" />
|
<div className="divider divider-horizontal" />
|
||||||
<Link className="btn btn-primary" to="/repos">Repositories</Link>
|
<CompatBtn className="btn-primary" href="/repos">Repositories</CompatBtn>
|
||||||
</div>
|
</div>
|
||||||
<Link to="/docs" className="link">Documentation</Link>
|
<Link to="/docs" className="link">Documentation</Link>
|
||||||
</Hero>
|
</Hero>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react"
|
||||||
|
|
||||||
import Layout from "../components/layout"
|
import Layout from "../components/layout"
|
||||||
import Hero from "../components/hero"
|
import Hero from "../components/hero"
|
||||||
|
import CompatBtn from "../components/compatbtn"
|
||||||
|
|
||||||
import bgImage from "../media/phones.png"
|
import bgImage from "../media/phones.png"
|
||||||
|
|
||||||
|
@ -54,14 +55,16 @@ const InstallPage = () => {
|
||||||
<h1 className="mb-5 text-5xl font-bold">Installation</h1>
|
<h1 className="mb-5 text-5xl font-bold">Installation</h1>
|
||||||
{(data != null) &&
|
{(data != null) &&
|
||||||
<div className="flex flex-col items-center gap-3">{
|
<div className="flex flex-col items-center gap-3">{
|
||||||
data.btns.map(it => {
|
data.btns.map((it, idx) => {
|
||||||
return <div className="btn-group" key={JSON.stringify(it)}>
|
return <div className="btn-group" key={JSON.stringify(it)}>
|
||||||
<button className={"btn " + (it.pre?'btn-secondary':'btn-primary')} onClick={() => {
|
<CompatBtn group={true} autoFocus={idx === 0}
|
||||||
window.open(it.apk.browser_download_url)
|
className={it.pre?'btn-secondary':'btn-primary'}
|
||||||
}}>Download {it.tag}</button>
|
href={it.apk.browser_download_url}
|
||||||
<button className="btn"onClick={() => {
|
target="_blank">Download {it.tag}</CompatBtn>
|
||||||
window.open(it.url)
|
<CompatBtn
|
||||||
}}>Release notes</button>
|
group={true}
|
||||||
|
href={it.url}
|
||||||
|
target="_blank">Release notes</CompatBtn>
|
||||||
</div>
|
</div>
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue