This commit is contained in:
Fredrik Burmester
2024-08-18 17:10:31 +02:00
parent 21c1221138
commit 752cb1cdc6
13 changed files with 198 additions and 39 deletions

View File

@@ -1,15 +1,15 @@
import { router, Tabs } from "expo-router";
import React, { useEffect } from "react";
import * as NavigationBar from "expo-navigation-bar";
import { TabBarIcon } from "@/components/navigation/TabBarIcon";
import { Colors } from "@/constants/Colors";
import { Platform, TouchableOpacity, View } from "react-native";
import { Feather } from "@expo/vector-icons";
import { Chromecast } from "@/components/Chromecast";
import { BlurView } from "expo-blur";
import { StyleSheet } from "react-native";
import * as NavigationBar from "expo-navigation-bar";
import { Tabs } from "expo-router";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Platform, StyleSheet } from "react-native";
export default function TabLayout() {
const { t } = useTranslation();
useEffect(() => {
if (Platform.OS === "android") {
NavigationBar.setBackgroundColorAsync("#121212");
@@ -53,7 +53,7 @@ export default function TabLayout() {
name="home"
options={{
headerShown: false,
title: "Home",
title: t("tabs.home"),
tabBarIcon: ({ color, focused }) => (
<TabBarIcon
name={focused ? "home" : "home-outline"}
@@ -66,7 +66,7 @@ export default function TabLayout() {
name="search"
options={{
headerShown: false,
title: "Search",
title: t("tabs.search"),
tabBarIcon: ({ color, focused }) => (
<TabBarIcon name={focused ? "search" : "search"} color={color} />
),
@@ -76,7 +76,7 @@ export default function TabLayout() {
name="library"
options={{
headerShown: false,
title: "Library",
title: t("tabs.library"),
tabBarIcon: ({ color, focused }) => (
<TabBarIcon
name={focused ? "apps" : "apps-outline"}

View File

@@ -1,11 +1,13 @@
import { Chromecast } from "@/components/Chromecast";
import { Feather } from "@expo/vector-icons";
import { Stack, useRouter } from "expo-router";
import { useTranslation } from "react-i18next";
import { Platform, View } from "react-native";
import { TouchableOpacity } from "react-native";
export default function IndexLayout() {
const router = useRouter();
const { t } = useTranslation();
return (
<Stack>
<Stack.Screen
@@ -13,7 +15,7 @@ export default function IndexLayout() {
options={{
headerShown: true,
headerLargeTitle: true,
headerTitle: "Home",
headerTitle: t("home.home"),
headerBlurEffect: "prominent",
headerTransparent: Platform.OS === "ios" ? true : false,
headerShadowVisible: false,

View File

@@ -20,12 +20,15 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useRouter } from "expo-router";
import { useAtom } from "jotai";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { RefreshControl, ScrollView, View } from "react-native";
export default function index() {
const router = useRouter();
const queryClient = useQueryClient();
const { i18n, t } = useTranslation();
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
@@ -216,9 +219,9 @@ export default function index() {
if (isConnected === false) {
return (
<View className="flex flex-col items-center justify-center h-full -mt-6 px-8">
<Text className="text-3xl font-bold mb-2">No Internet</Text>
<Text className="text-3xl font-bold mb-2">{t("home.noInternet")}</Text>
<Text className="text-center opacity-70">
No worries, you can still watch{"\n"}downloaded content.
{t("home.noInternetMessage")}
</Text>
<View className="mt-4">
<Button
@@ -229,7 +232,7 @@ export default function index() {
<Ionicons name="arrow-forward" size={20} color="white" />
}
>
Go to downloads
{t("home.goToDownloads")}
</Button>
</View>
</View>
@@ -239,10 +242,8 @@ export default function index() {
if (isError)
return (
<View className="flex flex-col items-center justify-center h-full -mt-6">
<Text className="text-3xl font-bold mb-2">Oops!</Text>
<Text className="text-center opacity-70">
Something went wrong.{"\n"}Please log out and in again.
</Text>
<Text className="text-3xl font-bold mb-2">{t("home.oops")}</Text>
<Text className="text-center opacity-70">{t("home.errorMessage")}</Text>
</View>
);
@@ -265,14 +266,14 @@ export default function index() {
<LargeMovieCarousel />
<ScrollingCollectionList
title="Continue Watching"
title={t("home.continueWatching")}
data={data}
loading={isLoading}
orientation="horizontal"
/>
<ScrollingCollectionList
title="Next Up"
title={t("home.nextUp")}
data={nextUpData}
loading={isLoadingNextUp}
orientation="horizontal"
@@ -283,19 +284,19 @@ export default function index() {
))}
<ScrollingCollectionList
title="Recently Added in Movies"
title={t("home.recentlyAddedMovies")}
data={recentlyAddedInMovies}
loading={isLoadingRecentlyAddedMovies}
/>
<ScrollingCollectionList
title="Recently Added in TV-Shows"
title={t("home.recentlyAddedTVShows")}
data={recentlyAddedInTVShows}
loading={isLoadingRecentlyAddedTVShows}
/>
<ScrollingCollectionList
title="Suggestions"
title={t("home.suggestions")}
data={suggestions}
loading={isLoadingSuggestions}
orientation="horizontal"

View File

@@ -17,6 +17,9 @@ import { useKeepAwake } from "expo-keep-awake";
import { useSettings } from "@/utils/atoms/settings";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
import { I18nextProvider, useTranslation } from "react-i18next";
import i18n from "@/i18n";
import { getLocales } from "expo-localization";
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
@@ -42,7 +45,9 @@ export default function RootLayout() {
return (
<JotaiProvider>
<Layout />
<I18nextProvider i18n={i18n}>
<Layout />
</I18nextProvider>
</JotaiProvider>
);
}
@@ -52,6 +57,8 @@ function Layout() {
useKeepAwake();
const { i18n } = useTranslation();
const queryClientRef = useRef<QueryClient>(
new QueryClient({
defaultOptions: {
@@ -75,6 +82,12 @@ function Layout() {
);
}, [settings]);
useEffect(() => {
i18n.changeLanguage(
settings?.preferedLanguage || getLocales()[0].languageCode || "en"
);
}, [settings]);
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<QueryClientProvider client={queryClientRef.current}>

View File

@@ -1,11 +1,13 @@
import { Button } from "@/components/Button";
import { Input } from "@/components/common/Input";
import { Text } from "@/components/common/Text";
import { LanguageSwitcher } from "@/components/LanguageSwitcher";
import { apiAtom, useJellyfin } from "@/providers/JellyfinProvider";
import { Ionicons } from "@expo/vector-icons";
import { AxiosError } from "axios";
import { useAtom } from "jotai";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import {
Alert,
KeyboardAvoidingView,
@@ -21,6 +23,7 @@ const CredentialsSchema = z.object({
});
const Login: React.FC = () => {
const { t, i18n } = useTranslation();
const { setServer, login, removeServer } = useJellyfin();
const [api] = useAtom(apiAtom);
@@ -72,7 +75,7 @@ const Login: React.FC = () => {
<View className="mb-4">
<Text className="text-3xl font-bold mb-2">Streamyfin</Text>
<Text className="text-neutral-500 mb-2">
Server: {api.basePath}
{t("server.server_label", { serverURL: api.basePath })}
</Text>
<Button
color="black"
@@ -89,17 +92,17 @@ const Login: React.FC = () => {
/>
}
>
Change server
{t("server.change_server")}
</Button>
</View>
<View className="flex flex-col space-y-2">
<Text className="text-2xl font-bold">Log in</Text>
<Text className="text-2xl font-bold">{t("login.login")}</Text>
<Text className="text-neutral-500">
Log in to any user account
{t("login.login_subtitle")}
</Text>
<Input
placeholder="Username"
placeholder={t("login.username_placeholder")}
onChangeText={(text) =>
setCredentials({ ...credentials, username: text })
}
@@ -116,7 +119,7 @@ const Login: React.FC = () => {
<Input
className="mb-2"
placeholder="Password"
placeholder={t("login.password_placeholder")}
onChangeText={(text) =>
setCredentials({ ...credentials, password: text })
}
@@ -139,7 +142,7 @@ const Login: React.FC = () => {
loading={loading}
className="mt-auto mb-2"
>
Log in
{t("login.login_button")}
</Button>
</View>
</KeyboardAvoidingView>
@@ -158,10 +161,10 @@ const Login: React.FC = () => {
<View className="flex flex-col gap-y-2">
<Text className="text-3xl font-bold">Streamyfin</Text>
<Text className="text-neutral-500">
Connect to your Jellyfin server
{t("server.connect_to_server")}
</Text>
<Input
placeholder="Server URL"
placeholder={t("server.server_url_placeholder")}
onChangeText={setServerURL}
value={serverURL}
keyboardType="url"
@@ -170,12 +173,11 @@ const Login: React.FC = () => {
textContentType="URL"
maxLength={500}
/>
<Text className="opacity-30">
Server URL requires http or https
</Text>
<Text className="opacity-30">{t("server.server_url_hint")}</Text>
<LanguageSwitcher />
</View>
<Button onPress={() => handleConnect(serverURL)} className="mb-2">
Connect
{t("server.connect_button")}
</Button>
</View>
</KeyboardAvoidingView>