mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-02 03:58:36 +01:00
Compare commits
207 Commits
v0.23.0
...
chore/expo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4d9552401 | ||
|
|
c74336a1a1 | ||
|
|
17257ece85 | ||
|
|
d5c634b74b | ||
|
|
933f3f2f7c | ||
|
|
252fc4387b | ||
|
|
3e299e2136 | ||
|
|
01cab2277e | ||
|
|
e4f4e861e0 | ||
|
|
4d665013f0 | ||
|
|
9aa4ea4a2e | ||
|
|
93ae03f55c | ||
|
|
b311ac98a7 | ||
|
|
83d425b2fb | ||
|
|
007fbdd0a3 | ||
|
|
37df999db5 | ||
|
|
72b9675df4 | ||
|
|
7a30a63335 | ||
|
|
0ff0fab3f4 | ||
|
|
d9d9b0ee00 | ||
|
|
fdaa69a787 | ||
|
|
ed5403e597 | ||
|
|
e6f290b85f | ||
|
|
aa20d9c701 | ||
|
|
e7128afb32 | ||
|
|
a24b126539 | ||
|
|
e1fe20db86 | ||
|
|
cd9f6aa8bd | ||
|
|
747bd1b416 | ||
|
|
364ce46fe5 | ||
|
|
5703279b46 | ||
|
|
4022ccb213 | ||
|
|
3a836462f5 | ||
|
|
8a5f24002f | ||
|
|
c30f9860ee | ||
|
|
94c170e3d2 | ||
|
|
cd8aba32d8 | ||
|
|
15f3ddf612 | ||
|
|
90f20f6e46 | ||
|
|
ea1f45bbaf | ||
|
|
7e62c9bc9a | ||
|
|
23f9e9dfae | ||
|
|
580e12b605 | ||
|
|
ff4c5f28af | ||
|
|
1b931ea348 | ||
|
|
49c0437f81 | ||
|
|
d81ae94ce8 | ||
|
|
7c77c70024 | ||
|
|
b28c4a56f3 | ||
|
|
2495a318eb | ||
|
|
7832ea4d0a | ||
|
|
4a0a51ef1d | ||
|
|
8cc551d906 | ||
|
|
c8da365a00 | ||
|
|
74b7cbc530 | ||
|
|
a14063a736 | ||
|
|
a3307a90a3 | ||
|
|
a2145fd7e8 | ||
|
|
cab5e4d980 | ||
|
|
ab603e6997 | ||
|
|
957348fe19 | ||
|
|
444bd040b0 | ||
|
|
d0ae63235d | ||
|
|
1727125ea7 | ||
|
|
dc498d62d8 | ||
|
|
455bf08213 | ||
|
|
0f974ef2a3 | ||
|
|
2d9aaccfe0 | ||
|
|
2c6823eb53 | ||
|
|
9dfcc01f17 | ||
|
|
38aad9610b | ||
|
|
54af64abef | ||
|
|
e1720a00da | ||
|
|
882d0ea188 | ||
|
|
f3b539232f | ||
|
|
33ea657a5c | ||
|
|
75820adcbc | ||
|
|
76cdb2b3f8 | ||
|
|
0a2ea33635 | ||
|
|
aad6093852 | ||
|
|
c553cff9d1 | ||
|
|
dcd458bd3d | ||
|
|
05dc61d17d | ||
|
|
e4de11127f | ||
|
|
2dc49735f4 | ||
|
|
0ebacd4bd3 | ||
|
|
14c8c1aaed | ||
|
|
2da774272d | ||
|
|
ef42207174 | ||
|
|
efa5638b12 | ||
|
|
c63cea891d | ||
|
|
4e80f58823 | ||
|
|
cfe39d504c | ||
|
|
cf43d1a657 | ||
|
|
cbe3b18226 | ||
|
|
b637a0f7d2 | ||
|
|
a0ce7cc6d0 | ||
|
|
a640df30bc | ||
|
|
062e6e6c23 | ||
|
|
d709e3b13e | ||
|
|
b232bebd73 | ||
|
|
90ef8ef6f9 | ||
|
|
0df6b8e2a0 | ||
|
|
f48b26076d | ||
|
|
c86a8438e5 | ||
|
|
faa2baae68 | ||
|
|
ed42371353 | ||
|
|
24277135a8 | ||
|
|
23d9cd36d1 | ||
|
|
b243524a7d | ||
|
|
8288682e68 | ||
|
|
58ec915699 | ||
|
|
480abb216d | ||
|
|
249109a94e | ||
|
|
eb7fa93f9b | ||
|
|
e8fd322d30 | ||
|
|
cad03a3566 | ||
|
|
9baa4063bd | ||
|
|
41db34ed8e | ||
|
|
5aba66ce05 | ||
|
|
79407ccd70 | ||
|
|
9a93b3b3bb | ||
|
|
2b846a1aca | ||
|
|
55d61172f4 | ||
|
|
57173a62dc | ||
|
|
78f65be09d | ||
|
|
293a9517a5 | ||
|
|
38b6215046 | ||
|
|
fc4a11d916 | ||
|
|
cf2beb8299 | ||
|
|
49d157a95a | ||
|
|
9692c173ae | ||
|
|
a297ac4843 | ||
|
|
a061f9f480 | ||
|
|
0fb6f2fb30 | ||
|
|
0773f773ba | ||
|
|
39bb3a9370 | ||
|
|
b79e534692 | ||
|
|
e9336e9a67 | ||
|
|
adfde1a7cd | ||
|
|
cab6257fb2 | ||
|
|
3f0f0090af | ||
|
|
b278632581 | ||
|
|
5ee1a9cabb | ||
|
|
2169bea031 | ||
|
|
95cf252349 | ||
|
|
8470cbe8d5 | ||
|
|
636a27246f | ||
|
|
a488c68633 | ||
|
|
7342b7eb92 | ||
|
|
8370519758 | ||
|
|
85e21edbf1 | ||
|
|
8d4115f5a0 | ||
|
|
c5d7a6729b | ||
|
|
db4046267f | ||
|
|
1e869a2c2f | ||
|
|
b6502c042a | ||
|
|
b506871c46 | ||
|
|
734678b1d5 | ||
|
|
68e98bbb94 | ||
|
|
d84ed558f3 | ||
|
|
ad39e8e10a | ||
|
|
29bba04fdd | ||
|
|
5a24957e88 | ||
|
|
39f2735756 | ||
|
|
53ea1cc899 | ||
|
|
5dc86d4765 | ||
|
|
d13731c28f | ||
|
|
459ca3245b | ||
|
|
0d1fb87284 | ||
|
|
7f0446b85f | ||
|
|
11fbe19f80 | ||
|
|
495742c52c | ||
|
|
5c97b85492 | ||
|
|
894305e126 | ||
|
|
e60cec69f8 | ||
|
|
7bc1c22770 | ||
|
|
e86dab5613 | ||
|
|
eeb803223c | ||
|
|
1a43f7ef1b | ||
|
|
f4624bdc25 | ||
|
|
3c5f2b4079 | ||
|
|
955190a9cc | ||
|
|
e1e4f4833c | ||
|
|
ed993d07ce | ||
|
|
dc9008f31c | ||
|
|
f92fee4158 | ||
|
|
e23387a384 | ||
|
|
bb141cad57 | ||
|
|
e833b4bc68 | ||
|
|
34fc26ed18 | ||
|
|
40b8410390 | ||
|
|
723233381c | ||
|
|
602de34824 | ||
|
|
9b1f2a98e5 | ||
|
|
946de97580 | ||
|
|
f2eadabf6a | ||
|
|
373d83a0d5 | ||
|
|
2c0ba18b49 | ||
|
|
3e8e8e1163 | ||
|
|
fe9c73a8f0 | ||
|
|
4f62391027 | ||
|
|
53b5fdda87 | ||
|
|
c0b71eb73d | ||
|
|
9b4590c876 | ||
|
|
4b18bad3bc | ||
|
|
752cb1cdc6 |
3
app.json
3
app.json
@@ -130,6 +130,7 @@
|
|||||||
},
|
},
|
||||||
"updates": {
|
"updates": {
|
||||||
"url": "https://u.expo.dev/e79219d1-797f-4fbe-9fa1-cfd360690a68"
|
"url": "https://u.expo.dev/e79219d1-797f-4fbe-9fa1-cfd360690a68"
|
||||||
}
|
},
|
||||||
|
"newArchEnabled": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
import { useGlobalSearchParams } from "expo-router";
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
||||||
import { Alert, Dimensions, View } from "react-native";
|
|
||||||
import YoutubePlayer, { PLAYER_STATES } from "react-native-youtube-iframe";
|
|
||||||
|
|
||||||
export default function page() {
|
|
||||||
const searchParams = useGlobalSearchParams();
|
|
||||||
|
|
||||||
const { url } = searchParams as { url: string };
|
|
||||||
|
|
||||||
const videoId = useMemo(() => {
|
|
||||||
return url.split("v=")[1];
|
|
||||||
}, [url]);
|
|
||||||
|
|
||||||
const [playing, setPlaying] = useState(false);
|
|
||||||
|
|
||||||
const onStateChange = useCallback((state: PLAYER_STATES) => {
|
|
||||||
if (state === "ended") {
|
|
||||||
setPlaying(false);
|
|
||||||
Alert.alert("video has finished playing!");
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const togglePlaying = useCallback(() => {
|
|
||||||
setPlaying((prev) => !prev);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
togglePlaying();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const screenWidth = Dimensions.get("screen").width;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className="flex flex-col bg-black items-center justify-center h-full">
|
|
||||||
<YoutubePlayer
|
|
||||||
height={300}
|
|
||||||
play={playing}
|
|
||||||
videoId={videoId}
|
|
||||||
onChangeState={onStateChange}
|
|
||||||
width={screenWidth}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -319,7 +319,7 @@ function Layout() {
|
|||||||
<BottomSheetModalProvider>
|
<BottomSheetModalProvider>
|
||||||
<SystemBars style="light" hidden={false} />
|
<SystemBars style="light" hidden={false} />
|
||||||
<ThemeProvider value={DarkTheme}>
|
<ThemeProvider value={DarkTheme}>
|
||||||
<Stack initialRouteName="/home">
|
<Stack>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="(auth)/(tabs)"
|
name="(auth)/(tabs)"
|
||||||
options={{
|
options={{
|
||||||
@@ -336,14 +336,6 @@ function Layout() {
|
|||||||
header: () => null,
|
header: () => null,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
|
||||||
name="(auth)/trailer/page"
|
|
||||||
options={{
|
|
||||||
headerShown: false,
|
|
||||||
presentation: "modal",
|
|
||||||
title: "",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="login"
|
name="login"
|
||||||
options={{
|
options={{
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import renderer from 'react-test-renderer';
|
|
||||||
|
|
||||||
import { ThemedText } from '../ThemedText';
|
|
||||||
|
|
||||||
it(`renders correctly`, () => {
|
|
||||||
const tree = renderer.create(<ThemedText>Snapshot test!</ThemedText>).toJSON();
|
|
||||||
|
|
||||||
expect(tree).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders correctly 1`] = `
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"color": "#11181C",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fontSize": 16,
|
|
||||||
"lineHeight": 24,
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Snapshot test!
|
|
||||||
</Text>
|
|
||||||
`;
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import {useRouter, useSegments} from "expo-router";
|
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
||||||
import React, {PropsWithChildren, useCallback, useMemo} from "react";
|
import {
|
||||||
import {TouchableOpacity, TouchableOpacityProps} from "react-native";
|
hasPermission,
|
||||||
import * as ContextMenu from "zeego/context-menu";
|
Permission,
|
||||||
import {MovieResult, TvResult} from "@/utils/jellyseerr/server/models/Search";
|
} from "@/utils/jellyseerr/server/lib/permissions";
|
||||||
import {useJellyseerr} from "@/hooks/useJellyseerr";
|
import { MovieResult, TvResult } from "@/utils/jellyseerr/server/models/Search";
|
||||||
import {hasPermission, Permission} from "@/utils/jellyseerr/server/lib/permissions";
|
import { useRouter, useSegments } from "expo-router";
|
||||||
import {MediaType} from "@/utils/jellyseerr/server/constants/media";
|
import React, { PropsWithChildren, useCallback, useMemo } from "react";
|
||||||
|
import { TouchableOpacity, TouchableOpacityProps } from "react-native";
|
||||||
|
|
||||||
interface Props extends TouchableOpacityProps {
|
interface Props extends TouchableOpacityProps {
|
||||||
result: MovieResult | TvResult;
|
result: MovieResult | TvResult;
|
||||||
@@ -26,78 +27,49 @@ export const TouchableJellyseerrRouter: React.FC<PropsWithChildren<Props>> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const segments = useSegments();
|
const segments = useSegments();
|
||||||
const {jellyseerrApi, jellyseerrUser, requestMedia} = useJellyseerr()
|
const { jellyseerrApi, jellyseerrUser, requestMedia } = useJellyseerr();
|
||||||
|
|
||||||
const from = segments[2];
|
const from = segments[2];
|
||||||
|
|
||||||
const autoApprove = useMemo(() => {
|
const autoApprove = useMemo(() => {
|
||||||
return jellyseerrUser && hasPermission(
|
return (
|
||||||
Permission.AUTO_APPROVE,
|
jellyseerrUser &&
|
||||||
jellyseerrUser.permissions,
|
hasPermission(Permission.AUTO_APPROVE, jellyseerrUser.permissions, {
|
||||||
{type: 'or'}
|
type: "or",
|
||||||
)
|
})
|
||||||
}, [jellyseerrApi, jellyseerrUser])
|
);
|
||||||
|
}, [jellyseerrApi, jellyseerrUser]);
|
||||||
|
|
||||||
const request = useCallback(() =>
|
const request = useCallback(
|
||||||
|
() =>
|
||||||
requestMedia(mediaTitle, {
|
requestMedia(mediaTitle, {
|
||||||
mediaId: result.id,
|
mediaId: result.id,
|
||||||
mediaType: result.mediaType
|
mediaType: result.mediaType,
|
||||||
}
|
}),
|
||||||
),
|
|
||||||
[jellyseerrApi, result]
|
[jellyseerrApi, result]
|
||||||
)
|
);
|
||||||
|
|
||||||
if (from === "(home)" || from === "(search)" || from === "(libraries)")
|
if (from === "(home)" || from === "(search)" || from === "(libraries)")
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ContextMenu.Root>
|
<TouchableOpacity
|
||||||
<ContextMenu.Trigger>
|
onPress={() => {
|
||||||
<TouchableOpacity
|
router.push({
|
||||||
onPress={() => {
|
pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`,
|
||||||
// @ts-ignore
|
params: {
|
||||||
router.push({pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`, params: {...result, mediaTitle, releaseYear, canRequest, posterSrc}});
|
...result,
|
||||||
}}
|
mediaTitle,
|
||||||
{...props}
|
releaseYear,
|
||||||
>
|
// @ts-expect-error
|
||||||
{children}
|
canRequest,
|
||||||
</TouchableOpacity>
|
posterSrc,
|
||||||
</ContextMenu.Trigger>
|
},
|
||||||
<ContextMenu.Content
|
});
|
||||||
avoidCollisions
|
}}
|
||||||
alignOffset={0}
|
{...props}
|
||||||
collisionPadding={0}
|
>
|
||||||
loop={false}
|
{children}
|
||||||
key={"content"}
|
</TouchableOpacity>
|
||||||
>
|
|
||||||
<ContextMenu.Label key="label-1">Actions</ContextMenu.Label>
|
|
||||||
{canRequest && result.mediaType === MediaType.MOVIE && (
|
|
||||||
<ContextMenu.Item
|
|
||||||
key="item-1"
|
|
||||||
onSelect={() => {
|
|
||||||
if (autoApprove) {
|
|
||||||
request()
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
shouldDismissMenuOnSelect
|
|
||||||
>
|
|
||||||
<ContextMenu.ItemTitle key="item-1-title">Request</ContextMenu.ItemTitle>
|
|
||||||
<ContextMenu.ItemIcon
|
|
||||||
ios={{
|
|
||||||
name: "arrow.down.to.line",
|
|
||||||
pointSize: 18,
|
|
||||||
weight: "semibold",
|
|
||||||
scale: "medium",
|
|
||||||
hierarchicalColor: {
|
|
||||||
dark: "purple",
|
|
||||||
light: "purple",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
androidIconName="download"
|
|
||||||
/>
|
|
||||||
</ContextMenu.Item>
|
|
||||||
)}
|
|
||||||
</ContextMenu.Content>
|
|
||||||
</ContextMenu.Root>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
import { useRouter, useSegments } from "expo-router";
|
import { useRouter, useSegments } from "expo-router";
|
||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren } from "react";
|
||||||
import { TouchableOpacity, TouchableOpacityProps } from "react-native";
|
import { TouchableOpacity, TouchableOpacityProps } from "react-native";
|
||||||
import * as ContextMenu from "zeego/context-menu";
|
|
||||||
|
|
||||||
interface Props extends TouchableOpacityProps {
|
interface Props extends TouchableOpacityProps {
|
||||||
item: BaseItemDto;
|
item: BaseItemDto;
|
||||||
@@ -16,8 +15,6 @@ export const itemRouter = (
|
|||||||
item: BaseItemDto | BaseItemPerson,
|
item: BaseItemDto | BaseItemPerson,
|
||||||
from: string
|
from: string
|
||||||
) => {
|
) => {
|
||||||
console.log(item.Type, item?.CollectionType);
|
|
||||||
|
|
||||||
if ("CollectionType" in item && item.CollectionType === "livetv") {
|
if ("CollectionType" in item && item.CollectionType === "livetv") {
|
||||||
return `/(auth)/(tabs)/${from}/livetv`;
|
return `/(auth)/(tabs)/${from}/livetv`;
|
||||||
}
|
}
|
||||||
@@ -80,78 +77,15 @@ export const TouchableItemRouter: React.FC<PropsWithChildren<Props>> = ({
|
|||||||
from === "(favorites)"
|
from === "(favorites)"
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<ContextMenu.Root>
|
<TouchableOpacity
|
||||||
<ContextMenu.Trigger>
|
onPress={() => {
|
||||||
<TouchableOpacity
|
const url = itemRouter(item, from);
|
||||||
onPress={() => {
|
// @ts-ignore
|
||||||
const url = itemRouter(item, from);
|
router.push(url);
|
||||||
// @ts-ignore
|
}}
|
||||||
router.push(url);
|
{...props}
|
||||||
}}
|
>
|
||||||
{...props}
|
{children}
|
||||||
>
|
</TouchableOpacity>
|
||||||
{children}
|
|
||||||
</TouchableOpacity>
|
|
||||||
</ContextMenu.Trigger>
|
|
||||||
<ContextMenu.Content
|
|
||||||
avoidCollisions
|
|
||||||
alignOffset={0}
|
|
||||||
collisionPadding={0}
|
|
||||||
loop={false}
|
|
||||||
key={"content"}
|
|
||||||
>
|
|
||||||
<ContextMenu.Label key="label-1">Actions</ContextMenu.Label>
|
|
||||||
<ContextMenu.Item
|
|
||||||
key="item-1"
|
|
||||||
onSelect={() => {
|
|
||||||
markAsPlayedStatus(true);
|
|
||||||
}}
|
|
||||||
shouldDismissMenuOnSelect
|
|
||||||
>
|
|
||||||
<ContextMenu.ItemTitle key="item-1-title">
|
|
||||||
Mark as watched
|
|
||||||
</ContextMenu.ItemTitle>
|
|
||||||
<ContextMenu.ItemIcon
|
|
||||||
ios={{
|
|
||||||
name: "checkmark.circle", // Changed to "checkmark.circle" which represents "watched"
|
|
||||||
pointSize: 18,
|
|
||||||
weight: "semibold",
|
|
||||||
scale: "medium",
|
|
||||||
hierarchicalColor: {
|
|
||||||
dark: "green", // Changed to green for "watched"
|
|
||||||
light: "green",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
androidIconName="checkmark-circle"
|
|
||||||
></ContextMenu.ItemIcon>
|
|
||||||
</ContextMenu.Item>
|
|
||||||
<ContextMenu.Item
|
|
||||||
key="item-2"
|
|
||||||
onSelect={() => {
|
|
||||||
markAsPlayedStatus(false);
|
|
||||||
}}
|
|
||||||
shouldDismissMenuOnSelect
|
|
||||||
destructive
|
|
||||||
>
|
|
||||||
<ContextMenu.ItemTitle key="item-2-title">
|
|
||||||
Mark as not watched
|
|
||||||
</ContextMenu.ItemTitle>
|
|
||||||
<ContextMenu.ItemIcon
|
|
||||||
ios={{
|
|
||||||
name: "eye.slash", // Changed to "eye.slash" which represents "not watched"
|
|
||||||
pointSize: 18, // Adjusted for better visibility
|
|
||||||
weight: "semibold",
|
|
||||||
scale: "medium",
|
|
||||||
hierarchicalColor: {
|
|
||||||
dark: "red", // Changed to red for "not watched"
|
|
||||||
light: "red",
|
|
||||||
},
|
|
||||||
// Removed paletteColors as it's not necessary in this case
|
|
||||||
}}
|
|
||||||
androidIconName="eye-slash"
|
|
||||||
></ContextMenu.ItemIcon>
|
|
||||||
</ContextMenu.Item>
|
|
||||||
</ContextMenu.Content>
|
|
||||||
</ContextMenu.Root>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr";
|
import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr";
|
||||||
import { userAtom } from "@/providers/JellyfinProvider";
|
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
|
||||||
import { useMutation } from "@tanstack/react-query";
|
|
||||||
import { useAtom } from "jotai";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { toast } from "sonner-native";
|
|
||||||
import { Button } from "../Button";
|
|
||||||
import { Input } from "../common/Input";
|
|
||||||
import { Text } from "../common/Text";
|
import { Text } from "../common/Text";
|
||||||
import { ListGroup } from "../list/ListGroup";
|
import { useCallback, useRef, useState } from "react";
|
||||||
|
import { Input } from "../common/Input";
|
||||||
import { ListItem } from "../list/ListItem";
|
import { ListItem } from "../list/ListItem";
|
||||||
|
import { Loader } from "../Loader";
|
||||||
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
|
import { Button } from "../Button";
|
||||||
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
import { toast } from "sonner-native";
|
||||||
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import { ListGroup } from "../list/ListGroup";
|
||||||
|
|
||||||
export const JellyseerrSettings = () => {
|
export const JellyseerrSettings = () => {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -1,113 +1,59 @@
|
|||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { Alert, View, ViewProps } from "react-native";
|
||||||
import {
|
import { Text } from "../common/Text";
|
||||||
BottomSheetBackdrop,
|
import { ListItem } from "../list/ListItem";
|
||||||
BottomSheetBackdropProps,
|
import { Button } from "../Button";
|
||||||
BottomSheetModal,
|
import { apiAtom, useJellyfin, userAtom } from "@/providers/JellyfinProvider";
|
||||||
BottomSheetTextInput,
|
import { useAtom } from "jotai";
|
||||||
BottomSheetView,
|
import Constants from "expo-constants";
|
||||||
} from "@gorhom/bottom-sheet";
|
import Application from "expo-application";
|
||||||
|
import { ListGroup } from "../list/ListGroup";
|
||||||
import { getQuickConnectApi } from "@jellyfin/sdk/lib/utils/api";
|
import { getQuickConnectApi } from "@jellyfin/sdk/lib/utils/api";
|
||||||
import * as Haptics from "expo-haptics";
|
import * as Haptics from "expo-haptics";
|
||||||
import { useAtom } from "jotai";
|
|
||||||
import React, { useCallback, useRef, useState } from "react";
|
|
||||||
import { Alert, View, ViewProps } from "react-native";
|
|
||||||
import { Button } from "../Button";
|
|
||||||
import { Text } from "../common/Text";
|
|
||||||
import { ListGroup } from "../list/ListGroup";
|
|
||||||
import { ListItem } from "../list/ListItem";
|
|
||||||
|
|
||||||
interface Props extends ViewProps {}
|
interface Props extends ViewProps {}
|
||||||
|
|
||||||
export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
export const QuickConnect: React.FC<Props> = ({ ...props }) => {
|
||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
const [user] = useAtom(userAtom);
|
const [user] = useAtom(userAtom);
|
||||||
const [quickConnectCode, setQuickConnectCode] = useState<string>();
|
|
||||||
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
|
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
const openQuickConnectAuthCodeInput = () => {
|
||||||
(props: BottomSheetBackdropProps) => (
|
Alert.prompt(
|
||||||
<BottomSheetBackdrop
|
"Quick connect",
|
||||||
{...props}
|
"Enter the quick connect code",
|
||||||
disappearsOnIndex={-1}
|
async (text) => {
|
||||||
appearsOnIndex={0}
|
if (text) {
|
||||||
/>
|
try {
|
||||||
),
|
const res = await getQuickConnectApi(api!).authorizeQuickConnect({
|
||||||
[]
|
code: text,
|
||||||
);
|
userId: user?.Id,
|
||||||
|
});
|
||||||
const authorizeQuickConnect = useCallback(async () => {
|
if (res.status === 200) {
|
||||||
if (quickConnectCode) {
|
Haptics.notificationAsync(
|
||||||
try {
|
Haptics.NotificationFeedbackType.Success
|
||||||
const res = await getQuickConnectApi(api!).authorizeQuickConnect({
|
);
|
||||||
code: quickConnectCode,
|
Alert.alert("Success", "Quick connect authorized");
|
||||||
userId: user?.Id,
|
} else {
|
||||||
});
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
||||||
if (res.status === 200) {
|
Alert.alert("Error", "Invalid code");
|
||||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
}
|
||||||
Alert.alert("Success", "Quick connect authorized");
|
} catch (e) {
|
||||||
setQuickConnectCode(undefined);
|
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
||||||
bottomSheetModalRef?.current?.close();
|
Alert.alert("Error", "Invalid code");
|
||||||
} else {
|
}
|
||||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
|
||||||
Alert.alert("Error", "Invalid code");
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
|
|
||||||
Alert.alert("Error", "Invalid code");
|
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
}, [api, user, quickConnectCode]);
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View {...props}>
|
<View {...props}>
|
||||||
<ListGroup title={"Quick Connect"}>
|
<ListGroup title={"Quick Connect"}>
|
||||||
<ListItem
|
<ListItem
|
||||||
onPress={() => bottomSheetModalRef?.current?.present()}
|
onPress={openQuickConnectAuthCodeInput}
|
||||||
title="Authorize Quick Connect"
|
title="Authorize Quick Connect"
|
||||||
textColor="blue"
|
textColor="blue"
|
||||||
/>
|
></ListItem>
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
|
|
||||||
<BottomSheetModal
|
|
||||||
ref={bottomSheetModalRef}
|
|
||||||
enableDynamicSizing
|
|
||||||
handleIndicatorStyle={{
|
|
||||||
backgroundColor: "white",
|
|
||||||
}}
|
|
||||||
backgroundStyle={{
|
|
||||||
backgroundColor: "#171717",
|
|
||||||
}}
|
|
||||||
backdropComponent={renderBackdrop}
|
|
||||||
>
|
|
||||||
<BottomSheetView>
|
|
||||||
<View className="flex flex-col space-y-4 px-4 pb-8 pt-2">
|
|
||||||
<View>
|
|
||||||
<Text className="font-bold text-2xl text-neutral-100">
|
|
||||||
Quick Connect
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View className="flex flex-col space-y-2">
|
|
||||||
<View className="p-4 border border-neutral-800 rounded-xl bg-neutral-900 w-full">
|
|
||||||
<BottomSheetTextInput
|
|
||||||
style={{ color: "white" }}
|
|
||||||
clearButtonMode="always"
|
|
||||||
placeholder="Enter the quick connect code..."
|
|
||||||
placeholderTextColor="#9CA3AF"
|
|
||||||
value={quickConnectCode}
|
|
||||||
onChangeText={setQuickConnectCode}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<Button
|
|
||||||
className="mt-auto"
|
|
||||||
onPress={authorizeQuickConnect}
|
|
||||||
color="purple"
|
|
||||||
>
|
|
||||||
Authorize
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</BottomSheetView>
|
|
||||||
</BottomSheetModal>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
104
package.json
104
package.json
@@ -4,102 +4,100 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"submodule-reload": "git submodule update --init --remote --recursive",
|
"submodule-reload": "git submodule update --init --remote --recursive",
|
||||||
|
"clean": "echo y | expo prebuild --clean",
|
||||||
"start": "bun run submodule-reload && expo start",
|
"start": "bun run submodule-reload && expo start",
|
||||||
"reset-project": "node ./scripts/reset-project.js",
|
"reset-project": "node ./scripts/reset-project.js",
|
||||||
"android": "bun run submodule-reload && expo run:android",
|
"android": "bun run submodule-reload && expo run:android",
|
||||||
"ios": "bun run submodule-reload && expo run:ios",
|
"ios": "bun run submodule-reload && expo run:ios",
|
||||||
"web": "bun run submodule-reload && expo start --web",
|
"web": "bun run submodule-reload && expo start --web",
|
||||||
"test": "jest --watchAll",
|
|
||||||
"lint": "expo lint",
|
"lint": "expo lint",
|
||||||
"postinstall": "patch-package"
|
"postinstall": "patch-package"
|
||||||
},
|
},
|
||||||
"jest": {
|
|
||||||
"preset": "jest-expo"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bottom-tabs/react-navigation": "^0.7.1",
|
"@bottom-tabs/react-navigation": "0.8.0",
|
||||||
|
"react-native-bottom-tabs": "0.8.0",
|
||||||
|
"@react-navigation/material-top-tabs": "^7.1.0",
|
||||||
|
"@react-navigation/native": "^7.0.14",
|
||||||
"@config-plugins/ffmpeg-kit-react-native": "^8.0.0",
|
"@config-plugins/ffmpeg-kit-react-native": "^8.0.0",
|
||||||
"@expo/react-native-action-sheet": "^4.1.0",
|
"@expo/react-native-action-sheet": "^4.1.0",
|
||||||
"@expo/vector-icons": "^14.0.4",
|
"@expo/vector-icons": "^14.0.4",
|
||||||
"@futurejj/react-native-visibility-sensor": "^1.3.5",
|
"@futurejj/react-native-visibility-sensor": "^1.3.5",
|
||||||
"@gorhom/bottom-sheet": "^4.6.4",
|
"@gorhom/bottom-sheet": "^5.0.6",
|
||||||
"@jellyfin/sdk": "^0.11.0",
|
"@jellyfin/sdk": "^0.11.0",
|
||||||
"@kesha-antonov/react-native-background-downloader": "3.1.2",
|
"@kesha-antonov/react-native-background-downloader": "3.2.6",
|
||||||
"@react-native-async-storage/async-storage": "1.23.1",
|
"@react-native-async-storage/async-storage": "1.23.1",
|
||||||
"@react-native-community/netinfo": "11.3.1",
|
"@react-native-community/netinfo": "11.4.1",
|
||||||
"@react-native-menu/menu": "^1.1.6",
|
"@react-native-menu/menu": "^1.1.6",
|
||||||
"@react-navigation/material-top-tabs": "^6.6.14",
|
"@shopify/flash-list": "1.7.1",
|
||||||
"@react-navigation/native": "^6.1.18",
|
|
||||||
"@shopify/flash-list": "1.6.4",
|
|
||||||
"@tanstack/react-query": "^5.59.20",
|
"@tanstack/react-query": "^5.59.20",
|
||||||
"@types/lodash": "^4.17.13",
|
"@types/lodash": "^4.17.13",
|
||||||
"@types/react-native-vector-icons": "^6.4.18",
|
"@types/react-native-vector-icons": "^6.4.18",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"add": "^2.0.6",
|
"add": "^2.0.6",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"expo": "~51.0.39",
|
"expo": "^52.0.0",
|
||||||
"expo-asset": "~10.0.10",
|
"expo-asset": "~11.0.2",
|
||||||
"expo-background-fetch": "~12.0.1",
|
"expo-background-fetch": "~13.0.4",
|
||||||
"expo-blur": "~13.0.2",
|
"expo-blur": "~14.0.3",
|
||||||
"expo-brightness": "~12.0.1",
|
"expo-brightness": "~13.0.3",
|
||||||
"expo-build-properties": "~0.12.5",
|
"expo-build-properties": "~0.13.2",
|
||||||
"expo-constants": "~16.0.2",
|
"expo-constants": "~17.0.4",
|
||||||
"expo-dev-client": "~4.0.29",
|
"expo-dev-client": "~5.0.10",
|
||||||
"expo-device": "~6.0.2",
|
"expo-device": "~7.0.2",
|
||||||
"expo-font": "~12.0.10",
|
"expo-font": "~13.0.3",
|
||||||
"expo-haptics": "~13.0.1",
|
"expo-haptics": "~14.0.1",
|
||||||
"expo-image": "~1.13.0",
|
"expo-image": "~2.0.4",
|
||||||
"expo-keep-awake": "~13.0.2",
|
"expo-keep-awake": "~14.0.2",
|
||||||
"expo-linear-gradient": "~13.0.2",
|
"expo-linear-gradient": "~14.0.2",
|
||||||
"expo-linking": "~6.3.1",
|
"expo-linking": "~7.0.4",
|
||||||
"expo-network": "~6.0.1",
|
"expo-network": "~7.0.5",
|
||||||
"expo-notifications": "~0.28.19",
|
"expo-notifications": "~0.29.12",
|
||||||
"expo-router": "~3.5.24",
|
"expo-router": "~4.0.17",
|
||||||
"expo-screen-orientation": "~7.0.5",
|
"expo-screen-orientation": "~8.0.4",
|
||||||
"expo-sensors": "~13.0.9",
|
"expo-sensors": "~14.0.2",
|
||||||
"expo-splash-screen": "~0.27.7",
|
"expo-splash-screen": "~0.29.21",
|
||||||
"expo-status-bar": "~1.12.1",
|
"expo-status-bar": "~2.0.1",
|
||||||
"expo-system-ui": "^3.0.7",
|
"expo-system-ui": "~4.0.7",
|
||||||
"expo-task-manager": "~11.8.2",
|
"expo-task-manager": "~12.0.4",
|
||||||
"expo-updates": "~0.25.27",
|
"expo-updates": "~0.26.13",
|
||||||
"expo-web-browser": "~13.0.3",
|
"expo-web-browser": "~14.0.2",
|
||||||
"ffmpeg-kit-react-native": "^6.0.2",
|
"ffmpeg-kit-react-native": "^6.0.2",
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"jotai": "^2.10.1",
|
"jotai": "^2.10.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"nativewind": "^2.0.11",
|
"nativewind": "^2.0.11",
|
||||||
"react": "18.2.0",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.3.1",
|
||||||
"react-native": "0.74.5",
|
"react-native": "0.76.6",
|
||||||
"react-native-awesome-slider": "^2.5.6",
|
"react-native-awesome-slider": "^2.5.6",
|
||||||
"react-native-bottom-tabs": "0.7.1",
|
|
||||||
"react-native-circular-progress": "^1.4.1",
|
"react-native-circular-progress": "^1.4.1",
|
||||||
"react-native-compressor": "^1.9.0",
|
"react-native-compressor": "^1.9.0",
|
||||||
|
"react-native-country-flag": "^2.0.2",
|
||||||
"react-native-device-info": "^14.0.1",
|
"react-native-device-info": "^14.0.1",
|
||||||
"react-native-edge-to-edge": "^1.1.1",
|
"react-native-edge-to-edge": "^1.1.3",
|
||||||
"react-native-gesture-handler": "~2.16.1",
|
"react-native-gesture-handler": "~2.20.2",
|
||||||
"react-native-get-random-values": "^1.11.0",
|
"react-native-get-random-values": "^1.11.0",
|
||||||
"react-native-google-cast": "^4.8.3",
|
"react-native-google-cast": "^4.8.3",
|
||||||
"react-native-image-colors": "^2.4.0",
|
"react-native-image-colors": "^2.4.0",
|
||||||
"react-native-ios-context-menu": "^2.5.2",
|
"react-native-ios-context-menu": "^2.5.2",
|
||||||
"react-native-ios-utilities": "^4.5.1",
|
"react-native-ios-utilities": "4.5.3",
|
||||||
"react-native-mmkv": "^2.12.2",
|
"react-native-mmkv": "^2.12.2",
|
||||||
"react-native-pager-view": "6.3.0",
|
"react-native-pager-view": "6.5.1",
|
||||||
"react-native-progress": "^5.0.1",
|
"react-native-progress": "^5.0.1",
|
||||||
"react-native-reanimated": "~3.10.1",
|
"react-native-reanimated": "~3.16.1",
|
||||||
"react-native-reanimated-carousel": "4.0.0-canary.22",
|
"react-native-reanimated-carousel": "4.0.0-canary.22",
|
||||||
"react-native-safe-area-context": "4.10.5",
|
"react-native-safe-area-context": "4.12.0",
|
||||||
"react-native-screens": "3.31.1",
|
"react-native-screens": "~4.4.0",
|
||||||
"react-native-svg": "15.2.0",
|
"react-native-svg": "15.8.0",
|
||||||
"react-native-tab-view": "^3.5.2",
|
"react-native-tab-view": "^3.5.2",
|
||||||
|
"react-native-udp": "^4.1.7",
|
||||||
"react-native-uitextview": "^1.4.0",
|
"react-native-uitextview": "^1.4.0",
|
||||||
"react-native-url-polyfill": "^2.0.0",
|
"react-native-url-polyfill": "^2.0.0",
|
||||||
"react-native-uuid": "^2.0.2",
|
"react-native-uuid": "^2.0.2",
|
||||||
"react-native-video": "^6.7.0",
|
"react-native-video": "^6.7.0",
|
||||||
"react-native-volume-manager": "^1.10.0",
|
"react-native-volume-manager": "^1.10.0",
|
||||||
"react-native-web": "~0.19.13",
|
"react-native-web": "~0.19.13",
|
||||||
"react-native-webview": "13.8.6",
|
"react-native-webview": "13.12.5",
|
||||||
"react-native-youtube-iframe": "^2.3.0",
|
|
||||||
"sonner-native": "^0.14.2",
|
"sonner-native": "^0.14.2",
|
||||||
"tailwindcss": "3.3.2",
|
"tailwindcss": "3.3.2",
|
||||||
"use-debounce": "^10.0.4",
|
"use-debounce": "^10.0.4",
|
||||||
@@ -110,10 +108,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.26.0",
|
"@babel/core": "^7.26.0",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/react": "~18.2.79",
|
"@types/react": "~18.3.12",
|
||||||
"@types/react-test-renderer": "^18.0.7",
|
"@types/react-test-renderer": "^18.0.7",
|
||||||
"jest": "^29.2.1",
|
|
||||||
"jest-expo": "~51.0.4",
|
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"react-test-renderer": "18.2.0",
|
"react-test-renderer": "18.2.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user