diff --git a/components/ItemContent.tsx b/components/ItemContent.tsx
index 402f3171..de10e375 100644
--- a/components/ItemContent.tsx
+++ b/components/ItemContent.tsx
@@ -90,7 +90,7 @@ export const ItemContent: React.FC<{ item: BaseItemDto }> = React.memo(
{item.Type !== "Program" && (
-
+
)}
diff --git a/components/PlayedStatus.tsx b/components/PlayedStatus.tsx
index f86e4525..5d738af4 100644
--- a/components/PlayedStatus.tsx
+++ b/components/PlayedStatus.tsx
@@ -6,16 +6,19 @@ import { View, ViewProps } from "react-native";
import { RoundButton } from "./RoundButton";
interface Props extends ViewProps {
- item: BaseItemDto;
+ items: BaseItemDto[];
+ size?: "default" | "large";
}
-export const PlayedStatus: React.FC = ({ item, ...props }) => {
+export const PlayedStatus: React.FC = ({ items, ...props }) => {
const queryClient = useQueryClient();
const invalidateQueries = () => {
- queryClient.invalidateQueries({
- queryKey: ["item", item.Id],
- });
+ items.forEach((item) => {
+ queryClient.invalidateQueries({
+ queryKey: ["item", item.Id],
+ });
+ })
queryClient.invalidateQueries({
queryKey: ["resumeItems"],
});
@@ -39,15 +42,20 @@ export const PlayedStatus: React.FC = ({ item, ...props }) => {
});
};
- const markAsPlayedStatus = useMarkAsPlayed(item);
+ const allPlayed = items.every((item) => item.UserData?.Played);
+
+ const markAsPlayedStatus = useMarkAsPlayed(items);
return (
markAsPlayedStatus(!item.UserData?.Played)}
- size="large"
+ fillColor={allPlayed ? "primary" : undefined}
+ icon={allPlayed ? "checkmark" : "checkmark"}
+ onPress={async () => {
+ console.log(allPlayed);
+ await markAsPlayedStatus(!allPlayed)
+ }}
+ size={props.size}
/>
);
diff --git a/components/common/TouchableItemRouter.tsx b/components/common/TouchableItemRouter.tsx
index 9ca97bee..774b043d 100644
--- a/components/common/TouchableItemRouter.tsx
+++ b/components/common/TouchableItemRouter.tsx
@@ -57,7 +57,7 @@ export const TouchableItemRouter: React.FC> = ({
const router = useRouter();
const segments = useSegments();
const { showActionSheetWithOptions } = useActionSheet();
- const markAsPlayedStatus = useMarkAsPlayed(item);
+ const markAsPlayedStatus = useMarkAsPlayed([item]);
const from = segments[2];
diff --git a/components/series/SeasonPicker.tsx b/components/series/SeasonPicker.tsx
index be625432..6851bbbc 100644
--- a/components/series/SeasonPicker.tsx
+++ b/components/series/SeasonPicker.tsx
@@ -17,7 +17,9 @@ import {
SeasonIndexState,
} from "@/components/series/SeasonDropdown";
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
+import { PlayedStatus } from "../PlayedStatus";
import { useTranslation } from "react-i18next";
+
type Props = {
item: BaseItemDto;
initialSeasonIndex?: number;
@@ -145,17 +147,20 @@ export const SeasonPicker: React.FC = ({ item, initialSeasonIndex }) => {
}}
/>
{episodes?.length || 0 > 0 ? (
- (
-
- )}
- DownloadedIconComponent={() => (
-
- )}
- />
+
+ (
+
+ )}
+ DownloadedIconComponent={() => (
+
+ )}
+ />
+
+
) : null}
diff --git a/components/video-player/controls/NextEpisodeCountDownButton.tsx b/components/video-player/controls/NextEpisodeCountDownButton.tsx
index e77c6198..73e3f828 100644
--- a/components/video-player/controls/NextEpisodeCountDownButton.tsx
+++ b/components/video-player/controls/NextEpisodeCountDownButton.tsx
@@ -60,12 +60,12 @@ const NextEpisodeCountDownButton: React.FC = ({
}
};
+ const { t } = useTranslation();
+
if (!show) {
return null;
}
- const { t } = useTranslation();
-
return (
{
+export const useMarkAsPlayed = (items: BaseItemDto[]) => {
const [api] = useAtom(apiAtom);
const [user] = useAtom(userAtom);
const queryClient = useQueryClient();
@@ -14,7 +14,6 @@ export const useMarkAsPlayed = (item: BaseItemDto) => {
const invalidateQueries = () => {
const queriesToInvalidate = [
- ["item", item.Id],
["resumeItems"],
["continueWatching"],
["nextUp-all"],
@@ -24,6 +23,11 @@ export const useMarkAsPlayed = (item: BaseItemDto) => {
["home"],
];
+ items.forEach((item) => {
+ if(!item.Id) return;
+ queriesToInvalidate.push(["item", item.Id]);
+ });
+
queriesToInvalidate.forEach((queryKey) => {
queryClient.invalidateQueries({ queryKey });
});
@@ -32,40 +36,8 @@ export const useMarkAsPlayed = (item: BaseItemDto) => {
const markAsPlayedStatus = async (played: boolean) => {
lightHapticFeedback();
- // Optimistic update
- queryClient.setQueryData(
- ["item", item.Id],
- (oldData: BaseItemDto | undefined) => {
- if (oldData) {
- return {
- ...oldData,
- UserData: {
- ...oldData.UserData,
- Played: !played,
- },
- };
- }
- return oldData;
- }
- );
-
- try {
- if (played) {
- await markAsPlayed({
- api: api,
- item: item,
- userId: user?.Id,
- });
- } else {
- await markAsNotPlayed({
- api: api,
- itemId: item?.Id,
- userId: user?.Id,
- });
- }
- invalidateQueries();
- } catch (error) {
- // Revert optimistic update on error
+ items.forEach((item) => {
+ // Optimistic update
queryClient.setQueryData(
["item", item.Id],
(oldData: BaseItemDto | undefined) => {
@@ -81,8 +53,45 @@ export const useMarkAsPlayed = (item: BaseItemDto) => {
return oldData;
}
);
+ })
+
+ try {
+ // Process all items
+ await Promise.all(items.map(item =>
+ played
+ ? markAsPlayed({ api, item, userId: user?.Id })
+ : markAsNotPlayed({ api, itemId: item?.Id, userId: user?.Id })
+ ));
+
+ // Bulk invalidate
+ queryClient.invalidateQueries({
+ queryKey: [
+ "resumeItems",
+ "continueWatching",
+ "nextUp-all",
+ "nextUp",
+ "episodes",
+ "seasons",
+ "home",
+ ...items.map(item => ["item", item.Id])
+ ].flat()
+ });
+ } catch (error) {
+ // Revert all optimistic updates on any failure
+ items.forEach(item => {
+ queryClient.setQueryData(
+ ["item", item.Id],
+ (oldData: BaseItemDto | undefined) =>
+ oldData ? {
+ ...oldData,
+ UserData: { ...oldData.UserData, Played: played }
+ } : oldData
+ );
+ });
console.error("Error updating played status:", error);
}
+
+ invalidateQueries();
};
return markAsPlayedStatus;