feat(tv): add long-press mark as watched action using alert dialog

This commit is contained in:
Fredrik Burmester
2026-01-28 20:36:57 +01:00
parent 8dcd4c40f9
commit 2ff9625903
21 changed files with 212 additions and 13 deletions

View File

@@ -36,6 +36,7 @@ import {
} from "@/components/tv";
import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes";
import useRouter from "@/hooks/useAppRouter";
import { useTVItemActionModal } from "@/hooks/useTVItemActionModal";
import { useTVOptionModal } from "@/hooks/useTVOptionModal";
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
@@ -65,6 +66,7 @@ const page: React.FC = () => {
const navigation = useNavigation();
const router = useRouter();
const { showOptions } = useTVOptionModal();
const { showItemActions } = useTVItemActionModal();
const { width: screenWidth } = useWindowDimensions();
const [orientation, _setOrientation] = useState(
ScreenOrientation.Orientation.PORTRAIT_UP,
@@ -294,7 +296,10 @@ const page: React.FC = () => {
width: posterSizes.poster,
}}
>
<TVFocusablePoster onPress={handlePress}>
<TVFocusablePoster
onPress={handlePress}
onLongPress={() => showItemActions(item)}
>
{item.Type === "Movie" && <MoviePoster item={item} />}
{(item.Type === "Series" || item.Type === "Episode") && (
<SeriesPoster item={item} />
@@ -307,7 +312,7 @@ const page: React.FC = () => {
</View>
);
},
[router],
[router, showItemActions],
);
const keyExtractor = useCallback((item: BaseItemDto) => item.Id || "", []);

View File

@@ -44,6 +44,7 @@ import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes";
import { useScaledTVTypography } from "@/constants/TVTypography";
import useRouter from "@/hooks/useAppRouter";
import { useOrientation } from "@/hooks/useOrientation";
import { useTVItemActionModal } from "@/hooks/useTVItemActionModal";
import { useTVOptionModal } from "@/hooks/useTVOptionModal";
import * as ScreenOrientation from "@/packages/expo-screen-orientation";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
@@ -108,6 +109,7 @@ const Page = () => {
const { t } = useTranslation();
const router = useRouter();
const { showOptions } = useTVOptionModal();
const { showItemActions } = useTVItemActionModal();
// TV Filter queries
const { data: tvGenreOptions } = useQuery({
@@ -412,7 +414,10 @@ const Page = () => {
width: posterSizes.poster,
}}
>
<TVFocusablePoster onPress={handlePress}>
<TVFocusablePoster
onPress={handlePress}
onLongPress={() => showItemActions(item)}
>
{item.Type === "Movie" && <MoviePoster item={item} />}
{(item.Type === "Series" || item.Type === "Episode") && (
<SeriesPoster item={item} />
@@ -425,7 +430,7 @@ const Page = () => {
</View>
);
},
[router],
[router, showItemActions],
);
const keyExtractor = useCallback((item: BaseItemDto) => item.Id || "", []);

View File

@@ -42,6 +42,7 @@ import { SearchTabButtons } from "@/components/search/SearchTabButtons";
import { TVSearchPage } from "@/components/search/TVSearchPage";
import useRouter from "@/hooks/useAppRouter";
import { useJellyseerr } from "@/hooks/useJellyseerr";
import { useTVItemActionModal } from "@/hooks/useTVItemActionModal";
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
import { useSettings } from "@/utils/atoms/settings";
import { eventBus } from "@/utils/eventBus";
@@ -69,6 +70,7 @@ export default function search() {
const params = useLocalSearchParams();
const insets = useSafeAreaInsets();
const router = useRouter();
const { showItemActions } = useTVItemActionModal();
const segments = useSegments();
const from = (segments as string[])[2] || "(search)";
@@ -607,6 +609,7 @@ export default function search() {
loading={loading}
noResults={noResults}
onItemPress={handleItemPress}
onItemLongPress={showItemActions}
searchType={searchType}
setSearchType={setSearchType}
showDiscover={!!jellyseerrApi}

View File

@@ -31,6 +31,7 @@ import { useScaledTVPosterSizes } from "@/constants/TVPosterSizes";
import { useScaledTVTypography } from "@/constants/TVTypography";
import useRouter from "@/hooks/useAppRouter";
import { useOrientation } from "@/hooks/useOrientation";
import { useTVItemActionModal } from "@/hooks/useTVItemActionModal";
import {
useDeleteWatchlist,
useRemoveFromWatchlist,
@@ -75,6 +76,7 @@ export default function WatchlistDetailScreen() {
const posterSizes = useScaledTVPosterSizes();
const { t } = useTranslation();
const router = useRouter();
const { showItemActions } = useTVItemActionModal();
const navigation = useNavigation();
const insets = useSafeAreaInsets();
const { watchlistId } = useLocalSearchParams<{ watchlistId: string }>();
@@ -211,6 +213,7 @@ export default function WatchlistDetailScreen() {
>
<TVFocusablePoster
onPress={handlePress}
onLongPress={() => showItemActions(item)}
hasTVPreferredFocus={index === 0}
>
{item.Type === "Movie" && <MoviePoster item={item} />}
@@ -222,7 +225,7 @@ export default function WatchlistDetailScreen() {
</View>
);
},
[router, typography],
[router, showItemActions, typography],
);
const renderItem = useCallback(