Merge pull request #505 from movie-web/more-v4-stuff

More v4 stuff
This commit is contained in:
William Oldham 2023-12-02 11:59:43 +00:00 committed by GitHub
commit 8b498ef036
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 46 additions and 13 deletions

View File

@ -16,6 +16,7 @@
"title": "Login to your account", "title": "Login to your account",
"description": "Please enter your passphrase to login to your account", "description": "Please enter your passphrase to login to your account",
"validationError": "Invalid or incomplete passphrase", "validationError": "Invalid or incomplete passphrase",
"deviceLengthError": "Please enter a device name",
"submit": "Login", "submit": "Login",
"passphraseLabel": "12-Word passphrase", "passphraseLabel": "12-Word passphrase",
"passphrasePlaceholder": "Passphrase" "passphrasePlaceholder": "Passphrase"

View File

@ -21,7 +21,7 @@ export function Dropdown(props: DropdownProps) {
<Listbox value={props.selectedItem} onChange={props.setSelectedItem}> <Listbox value={props.selectedItem} onChange={props.setSelectedItem}>
{() => ( {() => (
<> <>
<Listbox.Button className="relative w-full rounded-lg bg-dropdown-background py-3 pl-3 pr-10 text-left text-white shadow-md focus:outline-none tabbable cursor-pointer"> <Listbox.Button className="relative w-full rounded-lg bg-dropdown-background hover:bg-dropdown-hoverBackground py-3 pl-3 pr-10 text-left text-white shadow-md focus:outline-none tabbable cursor-pointer">
<span className="flex gap-4 items-center truncate"> <span className="flex gap-4 items-center truncate">
{props.selectedItem.leftIcon {props.selectedItem.leftIcon
? props.selectedItem.leftIcon ? props.selectedItem.leftIcon

View File

@ -4,6 +4,7 @@ import { useHistory } from "react-router-dom";
import { Icon, Icons } from "@/components/Icon"; import { Icon, Icons } from "@/components/Icon";
import { BrandPill } from "@/components/layout/BrandPill"; import { BrandPill } from "@/components/layout/BrandPill";
import { WideContainer } from "@/components/layout/WideContainer"; import { WideContainer } from "@/components/layout/WideContainer";
import { shouldHaveDmcaPage } from "@/pages/Dmca";
import { conf } from "@/setup/config"; import { conf } from "@/setup/config";
function FooterLink(props: { function FooterLink(props: {
@ -30,6 +31,8 @@ function Dmca() {
const { t } = useTranslation(); const { t } = useTranslation();
const history = useHistory(); const history = useHistory();
if (!shouldHaveDmcaPage()) return null;
return ( return (
<FooterLink icon={Icons.DRAGON} onClick={() => history.push("/dmca")}> <FooterLink icon={Icons.DRAGON} onClick={() => history.push("/dmca")}>
{t("footer.links.dmca")} {t("footer.links.dmca")}

View File

@ -130,10 +130,12 @@ export function CaptionsView({ id }: { id: string }) {
[selectLanguage, setCurrentlyDownloading] [selectLanguage, setCurrentlyDownloading]
); );
const content = subtitleList.map((v) => { const content = subtitleList.map((v, i) => {
return ( return (
<CaptionOption <CaptionOption
key={v.language} // key must use index to prevent url collisions
// eslint-disable-next-line react/no-array-index-key
key={`${i}-${v.url}`}
countryCode={v.language} countryCode={v.language}
selected={lang === v.language} selected={lang === v.language}
loading={v.language === currentlyDownloading && downloadReq.loading} loading={v.language === currentlyDownloading && downloadReq.loading}

View File

@ -1,5 +1,4 @@
export function handleBuffered(time: number, buffered: TimeRanges): number { export function handleBuffered(time: number, buffered: TimeRanges): number {
// TODO normalize the buffer sections into one section. they can be stitched together
for (let i = 0; i < buffered.length; i += 1) { for (let i = 0; i < buffered.length; i += 1) {
if (buffered.start(buffered.length - 1 - i) < time) { if (buffered.start(buffered.length - 1 - i) < time) {
return buffered.end(buffered.length - 1 - i); return buffered.end(buffered.length - 1 - i);

View File

@ -4,10 +4,14 @@ import { Icon, Icons } from "@/components/Icon";
import { ThinContainer } from "@/components/layout/ThinContainer"; import { ThinContainer } from "@/components/layout/ThinContainer";
import { Heading1, Paragraph } from "@/components/utils/Text"; import { Heading1, Paragraph } from "@/components/utils/Text";
import { PageTitle } from "@/pages/parts/util/PageTitle"; import { PageTitle } from "@/pages/parts/util/PageTitle";
import { conf } from "@/setup/config";
import { SubPageLayout } from "./layouts/SubPageLayout"; import { SubPageLayout } from "./layouts/SubPageLayout";
// TODO make email a constant export function shouldHaveDmcaPage() {
return !!conf().DMCA_EMAIL;
}
export function DmcaPage() { export function DmcaPage() {
const { t } = useTranslation(); const { t } = useTranslation();
@ -19,7 +23,7 @@ export function DmcaPage() {
<Paragraph>{t("screens.dmca.text")}</Paragraph> <Paragraph>{t("screens.dmca.text")}</Paragraph>
<Paragraph className="flex space-x-3 items-center"> <Paragraph className="flex space-x-3 items-center">
<Icon icon={Icons.MAIL} /> <Icon icon={Icons.MAIL} />
<span>dmca@movie-web.app</span> <span>{conf().DMCA_EMAIL ?? ""}</span>
</Paragraph> </Paragraph>
</ThinContainer> </ThinContainer>
</SubPageLayout> </SubPageLayout>

View File

@ -32,11 +32,18 @@ export function AccountCreatePart(props: AccountCreatePartProps) {
const [colorB, setColorB] = useState("#2E65CF"); const [colorB, setColorB] = useState("#2E65CF");
const [userIcon, setUserIcon] = useState<UserIcons>(UserIcons.USER); const [userIcon, setUserIcon] = useState<UserIcons>(UserIcons.USER);
const { t } = useTranslation(); const { t } = useTranslation();
// TODO validate device and account before next step const [hasDeviceError, setHasDeviceError] = useState(false);
const nextStep = useCallback(() => { const nextStep = useCallback(() => {
setHasDeviceError(false);
const validatedDevice = device.trim();
if (validatedDevice.length === 0) {
setHasDeviceError(true);
return;
}
props.onNext?.({ props.onNext?.({
device, device: validatedDevice,
profile: { profile: {
colorA, colorA,
colorB, colorB,
@ -75,6 +82,11 @@ export function AccountCreatePart(props: AccountCreatePartProps) {
value={userIcon} value={userIcon}
onInput={setUserIcon} onInput={setUserIcon}
/> />
{hasDeviceError ? (
<p className="text-authentication-errorText">
{t("auth.login.deviceLengthError")}
</p>
) : null}
</div> </div>
<LargeCardButtons> <LargeCardButtons>
<Button theme="purple" onClick={() => nextStep()}> <Button theme="purple" onClick={() => nextStep()}>

View File

@ -29,14 +29,17 @@ export function LoginFormPart(props: LoginFormPartProps) {
const [result, execute] = useAsyncFn( const [result, execute] = useAsyncFn(
async (inputMnemonic: string, inputdevice: string) => { async (inputMnemonic: string, inputdevice: string) => {
// TODO verify valid device input
if (!verifyValidMnemonic(inputMnemonic)) if (!verifyValidMnemonic(inputMnemonic))
throw new Error(t("auth.login.validationError") ?? undefined); throw new Error(t("auth.login.validationError") ?? undefined);
const validatedDevice = inputdevice.trim();
if (validatedDevice.length === 0)
throw new Error(t("auth.login.deviceLengthError") ?? undefined);
const account = await login({ const account = await login({
mnemonic: inputMnemonic, mnemonic: inputMnemonic,
userData: { userData: {
device: inputdevice, device: validatedDevice,
}, },
}); });
@ -46,7 +49,7 @@ export function LoginFormPart(props: LoginFormPartProps) {
props.onLogin?.(); props.onLogin?.();
}, },
[props, login, restore] [props, login, restore, t]
); );
return ( return (

View File

@ -14,7 +14,7 @@ import { useOnlineListener } from "@/hooks/usePing";
import { AboutPage } from "@/pages/About"; import { AboutPage } from "@/pages/About";
import { AdminPage } from "@/pages/admin/AdminPage"; import { AdminPage } from "@/pages/admin/AdminPage";
import VideoTesterView from "@/pages/developer/VideoTesterView"; import VideoTesterView from "@/pages/developer/VideoTesterView";
import { DmcaPage } from "@/pages/Dmca"; import { DmcaPage, shouldHaveDmcaPage } from "@/pages/Dmca";
import { NotFoundPage } from "@/pages/errors/NotFoundPage"; import { NotFoundPage } from "@/pages/errors/NotFoundPage";
import { HomePage } from "@/pages/HomePage"; import { HomePage } from "@/pages/HomePage";
import { LoginPage } from "@/pages/Login"; import { LoginPage } from "@/pages/Login";
@ -93,7 +93,10 @@ function App() {
<Route exact path="/register" component={RegisterPage} /> <Route exact path="/register" component={RegisterPage} />
<Route exact path="/login" component={LoginPage} /> <Route exact path="/login" component={LoginPage} />
<Route exact path="/faq" component={AboutPage} /> <Route exact path="/faq" component={AboutPage} />
<Route exact path="/dmca" component={DmcaPage} />
{shouldHaveDmcaPage() ? (
<Route exact path="/dmca" component={DmcaPage} />
) : null}
{/* Settings page */} {/* Settings page */}
<Route exact path="/settings" component={SettingsPage} /> <Route exact path="/settings" component={SettingsPage} />

View File

@ -10,6 +10,7 @@ interface Config {
GITHUB_LINK: string; GITHUB_LINK: string;
DONATION_LINK: string; DONATION_LINK: string;
DISCORD_LINK: string; DISCORD_LINK: string;
DMCA_EMAIL: string;
TMDB_READ_API_KEY: string; TMDB_READ_API_KEY: string;
CORS_PROXY_URL: string; CORS_PROXY_URL: string;
NORMAL_ROUTER: boolean; NORMAL_ROUTER: boolean;
@ -22,6 +23,7 @@ export interface RuntimeConfig {
GITHUB_LINK: string; GITHUB_LINK: string;
DONATION_LINK: string; DONATION_LINK: string;
DISCORD_LINK: string; DISCORD_LINK: string;
DMCA_EMAIL: string | null;
TMDB_READ_API_KEY: string; TMDB_READ_API_KEY: string;
NORMAL_ROUTER: boolean; NORMAL_ROUTER: boolean;
PROXY_URLS: string[]; PROXY_URLS: string[];
@ -35,6 +37,7 @@ const env: Record<keyof Config, undefined | string> = {
GITHUB_LINK: undefined, GITHUB_LINK: undefined,
DONATION_LINK: undefined, DONATION_LINK: undefined,
DISCORD_LINK: undefined, DISCORD_LINK: undefined,
DMCA_EMAIL: import.meta.env.VITE_DMCA_EMAIL,
CORS_PROXY_URL: import.meta.env.VITE_CORS_PROXY_URL, CORS_PROXY_URL: import.meta.env.VITE_CORS_PROXY_URL,
NORMAL_ROUTER: import.meta.env.VITE_NORMAL_ROUTER, NORMAL_ROUTER: import.meta.env.VITE_NORMAL_ROUTER,
BACKEND_URL: import.meta.env.VITE_BACKEND_URL, BACKEND_URL: import.meta.env.VITE_BACKEND_URL,
@ -54,11 +57,13 @@ function getKey(key: keyof Config, defaultString?: string): string {
} }
export function conf(): RuntimeConfig { export function conf(): RuntimeConfig {
const dmcaEmail = getKey("DMCA_EMAIL");
return { return {
APP_VERSION, APP_VERSION,
GITHUB_LINK, GITHUB_LINK,
DONATION_LINK, DONATION_LINK,
DISCORD_LINK, DISCORD_LINK,
DMCA_EMAIL: dmcaEmail.length > 0 ? dmcaEmail : null,
BACKEND_URL: getKey("BACKEND_URL"), BACKEND_URL: getKey("BACKEND_URL"),
TMDB_READ_API_KEY: getKey("TMDB_READ_API_KEY"), TMDB_READ_API_KEY: getKey("TMDB_READ_API_KEY"),
PROXY_URLS: getKey("CORS_PROXY_URL") PROXY_URLS: getKey("CORS_PROXY_URL")

View File

@ -97,6 +97,7 @@ export const defaultTheme = {
dropdown: { dropdown: {
background: "#171728", background: "#171728",
altBackground: "#151525", altBackground: "#151525",
hoverBackground: "#202036",
highlight: "#afa349", highlight: "#afa349",
highlightHover: "#FCEC61", highlightHover: "#FCEC61",
text: "#846D95", text: "#846D95",