mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 23:59:08 +00:00
Compare commits
2 Commits
feat/disab
...
as-any-rem
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
379e93edf9 | ||
|
|
75d6948a81 |
@@ -1,3 +1,4 @@
|
||||
import { MMKV } from "react-native-mmkv";
|
||||
import { storage } from "@/utils/mmkv";
|
||||
|
||||
declare module "react-native-mmkv" {
|
||||
@@ -7,9 +8,9 @@ declare module "react-native-mmkv" {
|
||||
}
|
||||
}
|
||||
|
||||
// Add the augmentation methods directly to the MMKV prototype
|
||||
// This follows the recommended pattern while adding the helper methods your app uses
|
||||
(storage as any).get = function <T>(key: string): T | undefined {
|
||||
// Add the augmentation methods directly to the MMKV instance
|
||||
// We need to bind these methods to preserve the 'this' context
|
||||
storage.get = function <T>(this: MMKV, key: string): T | undefined {
|
||||
try {
|
||||
const serializedItem = this.getString(key);
|
||||
if (!serializedItem) return undefined;
|
||||
@@ -20,7 +21,11 @@ declare module "react-native-mmkv" {
|
||||
}
|
||||
};
|
||||
|
||||
(storage as any).setAny = function (key: string, value: any | undefined): void {
|
||||
storage.setAny = function (
|
||||
this: MMKV,
|
||||
key: string,
|
||||
value: any | undefined,
|
||||
): void {
|
||||
try {
|
||||
if (value === undefined) {
|
||||
this.remove(key);
|
||||
|
||||
@@ -108,10 +108,10 @@ export const BitrateSheet: React.FC<Props> = ({
|
||||
values={selected ? [selected] : []}
|
||||
multiple={false}
|
||||
searchFilter={(item, query) => {
|
||||
const label = (item as any).key || "";
|
||||
const label = item.key || "";
|
||||
return label.toLowerCase().includes(query.toLowerCase());
|
||||
}}
|
||||
renderItemLabel={(item) => <Text>{(item as any).key || ""}</Text>}
|
||||
renderItemLabel={(item) => <Text>{item.key || ""}</Text>}
|
||||
set={(vals) => {
|
||||
const chosen = vals[0] as Bitrate | undefined;
|
||||
if (chosen) onChange(chosen);
|
||||
|
||||
@@ -4,7 +4,19 @@ import type { PropsWithChildren } from "react";
|
||||
import { Platform, TouchableOpacity, type ViewProps } from "react-native";
|
||||
import { useHaptic } from "@/hooks/useHaptic";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
interface Props
|
||||
extends Omit<
|
||||
ViewProps,
|
||||
| "children"
|
||||
| "onPressIn"
|
||||
| "onPressOut"
|
||||
| "onPress"
|
||||
| "nextFocusDown"
|
||||
| "nextFocusForward"
|
||||
| "nextFocusLeft"
|
||||
| "nextFocusRight"
|
||||
| "nextFocusUp"
|
||||
> {
|
||||
onPress?: () => void;
|
||||
icon?: keyof typeof Ionicons.glyphMap;
|
||||
background?: boolean;
|
||||
@@ -41,7 +53,7 @@ export const RoundButton: React.FC<PropsWithChildren<Props>> = ({
|
||||
<TouchableOpacity
|
||||
onPress={handlePress}
|
||||
className={`rounded-full ${buttonSize} flex items-center justify-center ${fillColorClass}`}
|
||||
{...(viewProps as any)}
|
||||
{...viewProps}
|
||||
>
|
||||
{icon ? (
|
||||
<Ionicons
|
||||
@@ -60,7 +72,7 @@ export const RoundButton: React.FC<PropsWithChildren<Props>> = ({
|
||||
<TouchableOpacity
|
||||
onPress={handlePress}
|
||||
className={`rounded-full ${buttonSize} flex items-center justify-center ${fillColorClass}`}
|
||||
{...(viewProps as any)}
|
||||
{...viewProps}
|
||||
>
|
||||
{icon ? (
|
||||
<Ionicons
|
||||
@@ -78,7 +90,7 @@ export const RoundButton: React.FC<PropsWithChildren<Props>> = ({
|
||||
<TouchableOpacity
|
||||
onPress={handlePress}
|
||||
className={`rounded-full ${buttonSize} flex items-center justify-center ${fillColorClass}`}
|
||||
{...(viewProps as any)}
|
||||
{...viewProps}
|
||||
>
|
||||
{icon ? (
|
||||
<Ionicons
|
||||
@@ -98,7 +110,7 @@ export const RoundButton: React.FC<PropsWithChildren<Props>> = ({
|
||||
className={`rounded-full ${buttonSize} flex items-center justify-center ${
|
||||
fillColor ? fillColorClass : "bg-transparent"
|
||||
}`}
|
||||
{...(viewProps as any)}
|
||||
{...viewProps}
|
||||
>
|
||||
{icon ? (
|
||||
<Ionicons
|
||||
@@ -112,11 +124,11 @@ export const RoundButton: React.FC<PropsWithChildren<Props>> = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={handlePress} {...(viewProps as any)}>
|
||||
<TouchableOpacity onPress={handlePress} {...viewProps}>
|
||||
<BlurView
|
||||
intensity={90}
|
||||
className={`rounded-full overflow-hidden ${buttonSize} flex items-center justify-center ${fillColorClass}`}
|
||||
{...(viewProps as any)}
|
||||
{...viewProps}
|
||||
>
|
||||
{icon ? (
|
||||
<Ionicons
|
||||
|
||||
@@ -81,14 +81,12 @@ export const TrackSheet: React.FC<Props> = ({
|
||||
}
|
||||
multiple={false}
|
||||
searchFilter={(item, query) => {
|
||||
const label = (item as any).DisplayTitle || "";
|
||||
const label = item.DisplayTitle || "";
|
||||
return label.toLowerCase().includes(query.toLowerCase());
|
||||
}}
|
||||
renderItemLabel={(item) => (
|
||||
<Text>{(item as any).DisplayTitle || ""}</Text>
|
||||
)}
|
||||
renderItemLabel={(item) => <Text>{item.DisplayTitle || ""}</Text>}
|
||||
set={(vals) => {
|
||||
const chosen = vals[0] as any;
|
||||
const chosen = vals[0];
|
||||
if (chosen && chosen.Index !== null && chosen.Index !== undefined) {
|
||||
onChange(chosen.Index);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Image } from "expo-image";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { useRouter } from "expo-router";
|
||||
import { type Href, useRouter } from "expo-router";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
@@ -340,7 +340,7 @@ export const AppleTVCarousel: React.FC<AppleTVCarouselProps> = ({
|
||||
const navigateToItem = useCallback(
|
||||
(item: BaseItemDto) => {
|
||||
const navigation = getItemNavigation(item, "(home)");
|
||||
router.push(navigation as any);
|
||||
router.push(navigation as Href);
|
||||
},
|
||||
[router],
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useActionSheet } from "@expo/react-native-action-sheet";
|
||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { useRouter, useSegments } from "expo-router";
|
||||
import { type Href, useRouter, useSegments } from "expo-router";
|
||||
import { type PropsWithChildren, useCallback } from "react";
|
||||
import { TouchableOpacity, type TouchableOpacityProps } from "react-native";
|
||||
import { useFavorite } from "@/hooks/useFavorite";
|
||||
@@ -146,12 +146,12 @@ export const TouchableItemRouter: React.FC<PropsWithChildren<Props>> = ({
|
||||
if (isOffline) {
|
||||
// For offline mode, we still need to use query params
|
||||
const url = `${itemRouter(item, from)}&offline=true`;
|
||||
router.push(url as any);
|
||||
router.push(url as Href);
|
||||
return;
|
||||
}
|
||||
|
||||
const navigation = getItemNavigation(item, from);
|
||||
router.push(navigation as any);
|
||||
router.push(navigation as Href);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { getItemsApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Image } from "expo-image";
|
||||
import { useRouter, useSegments } from "expo-router";
|
||||
import { type Href, useRouter, useSegments } from "expo-router";
|
||||
import { useAtom } from "jotai";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import { Dimensions, View, type ViewProps } from "react-native";
|
||||
@@ -156,7 +156,7 @@ const RenderItem: React.FC<{ item: BaseItemDto }> = ({ item }) => {
|
||||
if (!from) return;
|
||||
lightHapticFeedback();
|
||||
const navigation = getItemNavigation(item, from);
|
||||
router.push(navigation as any);
|
||||
router.push(navigation as Href);
|
||||
}, [item, from]);
|
||||
|
||||
const tap = Gesture.Tap()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { router, useSegments } from "expo-router";
|
||||
import { type Href, router, useSegments } from "expo-router";
|
||||
import type React from "react";
|
||||
import { useCallback } from "react";
|
||||
import { TouchableOpacity, type ViewProps } from "react-native";
|
||||
@@ -21,10 +21,10 @@ const CompanySlide: React.FC<
|
||||
const navigate = useCallback(
|
||||
({ id, image, name }: Network | Studio) =>
|
||||
router.push({
|
||||
pathname: `/(auth)/(tabs)/${from}/jellyseerr/company/${id}` as any,
|
||||
pathname: `/(auth)/(tabs)/${from}/jellyseerr/company/${id}`,
|
||||
params: { id, image, name, type: slide.type },
|
||||
}),
|
||||
[slide],
|
||||
} as Href),
|
||||
[slide, from],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { router, useSegments } from "expo-router";
|
||||
import { type Href, router, useSegments } from "expo-router";
|
||||
import type React from "react";
|
||||
import { useCallback } from "react";
|
||||
import { TouchableOpacity, type ViewProps } from "react-native";
|
||||
@@ -18,10 +18,10 @@ const GenreSlide: React.FC<SlideProps & ViewProps> = ({ slide, ...props }) => {
|
||||
const navigate = useCallback(
|
||||
(genre: GenreSliderItem) =>
|
||||
router.push({
|
||||
pathname: `/(auth)/(tabs)/${from}/jellyseerr/genre/${genre.id}` as any,
|
||||
pathname: `/(auth)/(tabs)/${from}/jellyseerr/genre/${genre.id}`,
|
||||
params: { type: slide.type, name: genre.name },
|
||||
}),
|
||||
[slide],
|
||||
} as Href),
|
||||
[slide, from],
|
||||
);
|
||||
|
||||
const { data } = useQuery({
|
||||
|
||||
@@ -31,15 +31,16 @@ export const ListGroup: React.FC<PropsWithChildren<Props>> = ({
|
||||
className='flex flex-col rounded-xl overflow-hidden pl-0 bg-neutral-900'
|
||||
>
|
||||
{Children.map(childrenArray, (child, index) => {
|
||||
if (isValidElement<{ style?: ViewStyle }>(child)) {
|
||||
return cloneElement(child as any, {
|
||||
style: StyleSheet.compose(
|
||||
child.props.style,
|
||||
index < childrenArray.length - 1
|
||||
? styles.borderBottom
|
||||
: undefined,
|
||||
),
|
||||
});
|
||||
if (isValidElement(child)) {
|
||||
const style = StyleSheet.compose(
|
||||
(child.props as { style?: ViewStyle }).style,
|
||||
index < childrenArray.length - 1
|
||||
? styles.borderBottom
|
||||
: undefined,
|
||||
);
|
||||
return cloneElement(child, { style } as Partial<
|
||||
typeof child.props
|
||||
>);
|
||||
}
|
||||
return child;
|
||||
})}
|
||||
|
||||
@@ -3,7 +3,18 @@ import type { PropsWithChildren, ReactNode } from "react";
|
||||
import { TouchableOpacity, View, type ViewProps } from "react-native";
|
||||
import { Text } from "../common/Text";
|
||||
|
||||
interface Props extends ViewProps {
|
||||
interface Props
|
||||
extends Omit<
|
||||
ViewProps,
|
||||
| "children"
|
||||
| "onPressIn"
|
||||
| "onPressOut"
|
||||
| "nextFocusDown"
|
||||
| "nextFocusForward"
|
||||
| "nextFocusLeft"
|
||||
| "nextFocusRight"
|
||||
| "nextFocusUp"
|
||||
> {
|
||||
title?: string | null | undefined;
|
||||
subtitle?: string | null | undefined;
|
||||
value?: string | null | undefined;
|
||||
@@ -37,7 +48,7 @@ export const ListItem: React.FC<PropsWithChildren<Props>> = ({
|
||||
className={`flex flex-row items-center justify-between bg-neutral-900 min-h-[42px] py-2 pr-4 pl-4 ${
|
||||
disabled ? "opacity-50" : ""
|
||||
}`}
|
||||
{...(viewProps as any)}
|
||||
{...viewProps}
|
||||
>
|
||||
<ListItemContent
|
||||
title={title}
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
BaseItemDto,
|
||||
MediaSourceInfo,
|
||||
} from "@jellyfin/sdk/lib/generated-client";
|
||||
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { type Href, useLocalSearchParams, useRouter } from "expo-router";
|
||||
import {
|
||||
type Dispatch,
|
||||
type FC,
|
||||
@@ -379,7 +379,7 @@ export const Controls: FC<Props> = ({
|
||||
|
||||
console.log("queryParams", queryParams);
|
||||
|
||||
router.replace(`player/direct-player?${queryParams}` as any);
|
||||
router.replace(`player/direct-player?${queryParams}` as Href);
|
||||
},
|
||||
[settings, subtitleIndex, audioIndex, mediaSource, bitrateValue, router],
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ interface Props {
|
||||
|
||||
interface FeedbackState {
|
||||
visible: boolean;
|
||||
icon: string;
|
||||
icon: keyof typeof Ionicons.glyphMap;
|
||||
text: string;
|
||||
side?: "left" | "right";
|
||||
}
|
||||
@@ -36,7 +36,7 @@ export const GestureOverlay = ({
|
||||
|
||||
const [feedback, setFeedback] = useState<FeedbackState>({
|
||||
visible: false,
|
||||
icon: "",
|
||||
icon: "play",
|
||||
text: "",
|
||||
});
|
||||
const [fadeAnim] = useState(new Animated.Value(0));
|
||||
@@ -46,7 +46,7 @@ export const GestureOverlay = ({
|
||||
|
||||
const showFeedback = useCallback(
|
||||
(
|
||||
icon: string,
|
||||
icon: keyof typeof Ionicons.glyphMap,
|
||||
text: string,
|
||||
side?: "left" | "right",
|
||||
isDuringDrag = false,
|
||||
@@ -320,7 +320,7 @@ export const GestureOverlay = ({
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name={feedback.icon as any}
|
||||
name={feedback.icon}
|
||||
size={24}
|
||||
color='white'
|
||||
style={{ marginRight: 8 }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SubtitleDeliveryMethod } from "@jellyfin/sdk/lib/generated-client";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { type Href, router, useLocalSearchParams } from "expo-router";
|
||||
import type React from "react";
|
||||
import {
|
||||
createContext,
|
||||
@@ -95,7 +95,7 @@ export const VideoProvider: React.FC<VideoProviderProps> = ({
|
||||
playbackPosition: playbackPosition,
|
||||
}).toString();
|
||||
|
||||
router.replace(`player/direct-player?${queryParams}` as any);
|
||||
router.replace(`player/direct-player?${queryParams}` as Href);
|
||||
};
|
||||
|
||||
const setTrackParams = (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { type Href, useLocalSearchParams, useRouter } from "expo-router";
|
||||
import { useCallback, useMemo, useRef } from "react";
|
||||
import { Platform, View } from "react-native";
|
||||
import { BITRATES } from "@/components/BitrateSelector";
|
||||
@@ -53,7 +53,7 @@ const DropdownView = () => {
|
||||
bitrateValue: bitrate.toString(),
|
||||
playbackPosition: playbackPositionRef.current,
|
||||
}).toString();
|
||||
router.replace(`player/direct-player?${queryParams}` as any);
|
||||
router.replace(`player/direct-player?${queryParams}` as Href);
|
||||
},
|
||||
[audioIndex, subtitleIndex, router],
|
||||
);
|
||||
|
||||
@@ -362,10 +362,11 @@ export const useSettings = () => {
|
||||
value !== undefined &&
|
||||
_settings?.[settingsKey] !== value
|
||||
) {
|
||||
(unlockedPluginDefaults as any)[settingsKey] = value;
|
||||
(unlockedPluginDefaults as Record<string, unknown>)[settingsKey] =
|
||||
value;
|
||||
}
|
||||
|
||||
(acc as any)[settingsKey] = locked
|
||||
(acc as Record<string, unknown>)[settingsKey] = locked
|
||||
? value
|
||||
: (_settings?.[settingsKey] ?? value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user