Repository: IgnisDa/ryot (language: rust)

Issue:
Notifications not working (properly?)
I have setup a Discord notifications and the test messages work fine. But I never get notification when a new episode is released even when I monitor it. How is it supposed to work, when it is released AKA a date to air is confirmed or when the date to air is now? I need to have a notification when the episode is watchable.

Everything shows fine in "Upcoming" list but not notification sent. I have everything toggled on in the settings.


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 1ba4efde85..2ef9300153 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -423,17 +423,6 @@ dependencies = [
  "serde_json",
 ]
 
-[[package]]
-name = "async-lock"
-version = "3.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
-dependencies = [
- "event-listener",
- "event-listener-strategy",
- "pin-project-lite",
-]
-
 [[package]]
 name = "async-oneshot"
 version = "0.5.9"
@@ -874,7 +863,6 @@ dependencies = [
  "migrations",
  "miscellaneous-resolver",
  "miscellaneous-service",
- "moka",
  "openidconnect",
  "reqwest 0.12.9",
  "router-resolver",
@@ -1500,24 +1488,6 @@ dependencies = [
  "once_cell",
 ]
 
-[[package]]
-name = "crossbeam-channel"
-version = "0.5.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
-dependencies = [
- "crossbeam-utils",
-]
-
 [[package]]
 name = "crossbeam-queue"
 version = "0.3.11"
@@ -2219,16 +2189,6 @@ dependencies = [
  "pin-project-lite",
 ]
 
-[[package]]
-name = "event-listener-strategy"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
-dependencies = [
- "event-listener",
- "pin-project-lite",
-]
-
 [[package]]
 name = "eventsource-stream"
 version = "0.2.3"
@@ -3894,30 +3854,6 @@ dependencies = [
  "uuid",
 ]
 
-[[package]]
-name = "moka"
-version = "0.12.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f"
-dependencies = [
- "async-lock",
- "async-trait",
- "crossbeam-channel",
- "crossbeam-epoch",
- "crossbeam-utils",
- "event-listener",
- "futures-util",
- "once_cell",
- "parking_lot",
- "quanta",
- "rustc_version",
- "smallvec",
- "tagptr",
- "thiserror",
- "triomphe",
- "uuid",
-]
-
 [[package]]
 name = "multer"
 version = "3.1.0"
@@ -4749,21 +4685,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "quanta"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5"
-dependencies = [
- "crossbeam-utils",
- "libc",
- "once_cell",
- "raw-cpuid",
- "wasi",
- "web-sys",
- "winapi",
-]
-
 [[package]]
 name = "quote"
 version = "1.0.37"
@@ -4861,15 +4782,6 @@ dependencies = [
  "zerocopy",
 ]
 
-[[package]]
-name = "raw-cpuid"
-version = "11.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0"
-dependencies = [
- "bitflags 2.6.0",
-]
-
 [[package]]
 name = "redis"
 version = "0.25.4"
@@ -6381,8 +6293,6 @@ dependencies = [
  "chrono-tz",
  "config",
  "file-storage-service",
- "media-models",
- "moka",
  "openidconnect",
  "sea-orm",
 ]
@@ -6498,12 +6408,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "tagptr"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
-
 [[package]]
 name = "tap"
 version = "1.0.1"
@@ -6928,12 +6832,6 @@ dependencies = [
  "tracing",
 ]
 
-[[package]]
-name = "triomphe"
-version = "0.1.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3"
-
 [[package]]
 name = "try-lock"
 version = "0.2.5"
diff --git a/Cargo.toml b/Cargo.toml
index fb76d04975..17a46b52d0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -79,7 +79,6 @@ itertools = "=0.13.0"
 futures = "=0.3.31"
 hashbag = "=0.1.12"
 markdown = "=1.0.0-alpha.21"
-moka = { version = "=0.12.8", features = ["future"] }
 nanoid = "=0.4.0"
 openidconnect = "=3.5.0"
 regex = "=1.11.1"
diff --git a/apps/backend/Cargo.toml b/apps/backend/Cargo.toml
index 1966b38fe4..660b3ecc40 100644
--- a/apps/backend/Cargo.toml
+++ b/apps/backend/Cargo.toml
@@ -38,7 +38,6 @@ integration-service = { path = "../../crates/services/integration" }
 itertools = { workspace = true }
 logs-wheel = "=0.3.1"
 media-models = { path = "../../crates/models/media" }
-moka = { workspace = true }
 migrations = { path = "../../crates/migrations" }
 miscellaneous-resolver = { path = "../../crates/resolvers/miscellaneous" }
 miscellaneous-service = { path = "../../crates/services/miscellaneous" }
diff --git a/apps/backend/src/common.rs b/apps/backend/src/common.rs
index 0601cdeac7..f2a5b2a794 100644
--- a/apps/backend/src/common.rs
+++ b/apps/backend/src/common.rs
@@ -12,7 +12,6 @@ use axum::{
 };
 use background::{ApplicationJob, CoreApplicationJob};
 use cache_service::CacheService;
-use chrono::Duration;
 use collection_resolver::{CollectionMutation, CollectionQuery};
 use collection_service::CollectionService;
 use common_utils::{ryot_log, FRONTEND_OAUTH_ENDPOINT};
@@ -28,7 +27,6 @@ use integration_service::IntegrationService;
 use itertools::Itertools;
 use miscellaneous_resolver::{MiscellaneousMutation, MiscellaneousQuery};
 use miscellaneous_service::MiscellaneousService;
-use moka::future::Cache;
 use openidconnect::{
     core::{CoreClient, CoreProviderMetadata},
     reqwest::async_http_client,
@@ -48,13 +46,13 @@ use user_service::UserService;
 
 /// All the services that are used by the app
 pub struct AppServices {
-    pub miscellaneous_service: Arc<MiscellaneousService>,
+    pub app_router: Router,
     pub importer_service: Arc<ImporterService>,
     pub exporter_service: Arc<ExporterService>,
     pub exercise_service: Arc<ExerciseService>,
-    pub integration_service: Arc<IntegrationService>,
     pub statistics_service: Arc<StatisticsService>,
-    pub app_router: Router,
+    pub integration_service: Arc<IntegrationService>,
+    pub miscellaneous_service: Arc<MiscellaneousService>,
 }
 
 #[allow(clippy::too_many_arguments)]
@@ -73,9 +71,6 @@ pub async fn create_app_services(
         config.file_storage.s3_bucket_name.clone(),
     ));
     let cache_service = CacheService::new(&db);
-    let commit_cache = Cache::builder()
-        .time_to_live(Duration::try_hours(1).unwrap().to_std().unwrap())
-        .build();
     let supporting_service = Arc::new(
         SupportingService::new(
             is_pro,
@@ -84,7 +79,6 @@ pub async fn create_app_services(
             cache_service,
             config.clone(),
             oidc_client,
-            commit_cache,
             file_storage_service.clone(),
             perform_application_job,
             perform_core_application_job,
diff --git a/apps/frontend/app/components/common.tsx b/apps/frontend/app/components/common.tsx
index 32d0e909a1..9b2536980b 100644
--- a/apps/frontend/app/components/common.tsx
+++ b/apps/frontend/app/components/common.tsx
@@ -29,24 +29,12 @@ import {
 	rem,
 } from "@mantine/core";
 import { useDebouncedValue, useDidUpdate, useDisclosure } from "@mantine/hooks";
+import { Form, Link, useFetcher, useNavigate } from "@remix-run/react";
 import {
-	Form,
-	Link,
-	useFetcher,
-	useNavigate,
-	useRevalidator,
-} from "@remix-run/react";
-import {
-	DeployUpdateMetadataGroupJobDocument,
-	DeployUpdateMetadataJobDocument,
-	DeployUpdatePersonJobDocument,
 	EntityLot,
 	GridPacking,
 	type MediaLot,
 	type MediaSource,
-	MetadataDetailsDocument,
-	MetadataGroupDetailsDocument,
-	PersonDetailsDocument,
 	type ReviewItem,
 	UserReviewScale,
 } from "@ryot/generated/graphql/backend/graphql";
@@ -71,7 +59,6 @@ import {
 	IconTrash,
 	IconX,
 } from "@tabler/icons-react";
-import { useMutation, useQuery } from "@tanstack/react-query";
 import Cookies from "js-cookie";
 import type { ReactNode, Ref } from "react";
 import { useState } from "react";
@@ -82,7 +69,6 @@ import { withQuery } from "ufo";
 import {
 	PRO_REQUIRED_MESSAGE,
 	ThreePointSmileyRating,
-	clientGqlService,
 	convertDecimalToThreePointSmiley,
 	dayjsLib,
 	getSurroundingElements,
@@ -132,81 +118,16 @@ export const ApplicationGrid = (props: {
 };
 
 export const MediaDetailsLayout = (props: {
-	children: Array<ReactNode | (ReactNode | undefined)>;
 	images: Array<string | null | undefined>;
-	entityDetails: { id: string; lot: EntityLot; isPartial?: boolean | null };
+	children: Array<ReactNode | (ReactNode | undefined)>;
 	externalLink?: {
 		source: MediaSource;
 		lot?: MediaLot;
 		href?: string | null;
 	};
 }) => {
-	const revalidator = useRevalidator();
 	const [activeImageId, setActiveImageId] = useState(0);
 	const fallbackImageUrl = useFallbackImageUrl();
-	const [mutationHasRunOnce, setMutationHasRunOnce] = useState(false);
-	const entityIsPartial = useQuery({
-		queryKey: ["MediaDetailsLayout", "entityIsPartial", props.entityDetails],
-		queryFn: async () => {
-			const isPartial = await match(props.entityDetails.lot)
-				.with(EntityLot.Metadata, () =>
-					clientGqlService
-						.request(MetadataDetailsDocument, {
-							metadataId: props.entityDetails.id,
-						})
-						.then((data) => data.metadataDetails.isPartial),
-				)
-				.with(EntityLot.Person, () =>
-					clientGqlService
-						.request(PersonDetailsDocument, {
-							personId: props.entityDetails.id,
-						})
-						.then((data) => data.personDetails.details.isPartial),
-				)
-				.with(EntityLot.MetadataGroup, () =>
-					clientGqlService
-						.request(MetadataGroupDetailsDocument, {
-							metadataGroupId: props.entityDetails.id,
-						})
-						.then((data) => data.metadataGroupDetails.details.isPartial),
-				)
-				.run();
-			return isPartial;
-		},
-		refetchInterval: (query) =>
-			query.state.data
-				? dayjsLib.duration(2, "seconds").asMilliseconds()
-				: false,
-		enabled: props.entityDetails.isPartial === true,
-	});
-	const commitEntity = useMutation({
-		mutationFn: async () =>
-			match(props.entityDetails.lot)
-				.with(EntityLot.Metadata, () =>
-					clientGqlService.request(DeployUpdateMetadataJobDocument, {
-						metadataId: props.entityDetails.id,
-					}),
-				)
-				.with(EntityLot.MetadataGroup, () =>
-					clientGqlService.request(DeployUpdateMetadataGroupJobDocument, {
-						metadataGroupId: props.entityDetails.id,
-					}),
-				)
-				.with(EntityLot.Person, () =>
-					clientGqlService.request(DeployUpdatePersonJobDocument, {
-						personId: props.entityDetails.id,
-					}),
-				)
-				.run(),
-		onSuccess: () => {
-			setMutationHasRunOnce(true);
-			setTimeout(() => revalidator.revalidate(), 5000);
-		},
-	});
-
-	useDidUpdate(() => {
-		if (entityIsPartial.data && !mutationHasRunOnce) commitEntity.mutate();
-	}, [entityIsPartial]);
 
 	return (
 		<Flex direction={{ base: "column", md: "row" }} gap="lg">
diff --git a/apps/frontend/app/lib/generals.ts b/apps/frontend/app/lib/generals.ts
index b9ee300f3f..ade37f551a 100644
--- a/apps/frontend/app/lib/generals.ts
+++ b/apps/frontend/app/lib/generals.ts
@@ -43,11 +43,11 @@ declare global {
 	}
 }
 
-dayjs.extend(relativeTime);
-dayjs.extend(duration);
-dayjs.extend(localizedFormat);
 dayjs.extend(utc);
+dayjs.extend(duration);
 dayjs.extend(timezone);
+dayjs.extend(relativeTime);
+dayjs.extend(localizedFormat);
 
 export { dayjs as dayjsLib };
 
diff --git a/apps/frontend/app/routes/_dashboard.calendar.tsx b/apps/frontend/app/routes/_dashboard.calendar.tsx
index 14d45e25e0..b8111315c8 100644
--- a/apps/frontend/app/routes/_dashboard.calendar.tsx
+++ b/apps/frontend/app/routes/_dashboard.calendar.tsx
@@ -1,27 +1,29 @@
 import {
 	ActionIcon,
-	Anchor,
 	Box,
 	Button,
-	Card,
 	Container,
 	Group,
 	Stack,
 	Text,
 	Title,
-	Tooltip,
 } from "@mantine/core";
 import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
-import { Link, useLoaderData } from "@remix-run/react";
+import { useLoaderData } from "@remix-run/react";
 import {
+	type CalendarEventPartFragment,
+	MediaLot,
 	UserCalendarEventsDocument,
 	type UserCalendarEventsQuery,
 } from "@ryot/generated/graphql/backend/graphql";
-import { isNumber, snakeCase, startCase, sum, truncate } from "@ryot/ts-utils";
+import { sum } from "@ryot/ts-utils";
 import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
-import { $path } from "remix-routes";
+import { Fragment } from "react/jsx-runtime";
+import { match } from "ts-pattern";
 import { z } from "zod";
 import { zx } from "zodix";
+import { ApplicationGrid } from "~/components/common";
+import { MetadataDisplayItem } from "~/components/media";
 import { dayjsLib } from "~/lib/generals";
 import { useAppSearchParam } from "~/lib/hooks";
 import {
@@ -46,7 +48,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
 			input: { month: date.month() + 1, year: date.year() },
 		}),
 	]);
-	return { query, userCalendarEvents, cookieName };
+	return { userCalendarEvents, cookieName, query };
 };
 
 export const meta = (_args: MetaArgs<typeof loader>) => {
@@ -59,7 +61,7 @@ export default function Page() {
 	const date = dayjsLib(loaderData.query.date);
 
 	return (
-		<Container size="xs">
+		<Container>
 			<Stack>
 				<Group justify="space-between">
 					<Title order={3} td="underline">
@@ -88,7 +90,7 @@ export default function Page() {
 					</Button.Group>
 				</Group>
 				{loaderData.userCalendarEvents.length > 0 ? (
-					<Box>
+					<Stack gap={4}>
 						<Box>
 							<Text display="inline" fw="bold">
 								{sum(loaderData.userCalendarEvents.map((e) => e.events.length))}
@@ -96,9 +98,9 @@ export default function Page() {
 							items found
 						</Box>
 						{loaderData.userCalendarEvents.map((ce) => (
-							<CalendarEvent day={ce} key={ce.date} />
+							<CalendarEvent key={ce.date} data={ce} />
 						))}
-					</Box>
+					</Stack>
 				) : (
 					<Text fs="italic">No events in this time period</Text>
 				)}
@@ -108,64 +110,52 @@ export default function Page() {
 }
 
 const CalendarEvent = (props: {
-	day: UserCalendarEventsQuery["userCalendarEvents"][number];
+	data: UserCalendarEventsQuery["userCalendarEvents"][number];
 }) => {
-	const date = dayjsLib(props.day.date);
+	const date = dayjsLib(props.data.date);
 
 	return (
-		<Card
-			data-calendar-date={props.day.date}
-			withBorder
-			radius="sm"
-			padding="xs"
-			mt="sm"
-		>
-			<Card.Section withBorder p="sm">
-				<Group justify="space-between">
-					<Text>{date.format("D MMMM")}</Text>
-					<Text>{date.format("dddd")}</Text>
-				</Group>
-			</Card.Section>
-			{props.day.events.map((evt) => (
-				<Group
-					key={evt.calendarEventId}
-					data-calendar-event-id={evt.calendarEventId}
-					justify="space-between"
-					align="end"
-				>
-					<Text mt="sm" size="sm">
-						<Tooltip label={evt.metadataTitle} disabled={!evt.episodeName}>
-							<Anchor
-								component={Link}
-								to={$path("/media/item/:id", {
-									id: evt.metadataId,
-								})}
-							>
-								{truncate(evt.episodeName ?? evt.metadataTitle, { length: 40 })}
-							</Anchor>
-						</Tooltip>{" "}
-						{isNumber(evt.showExtraInformation?.season) ? (
-							<Text span c="dimmed" size="sm">
-								(S{evt.showExtraInformation.season}-E
-								{evt.showExtraInformation.episode})
-							</Text>
-						) : null}
-						{isNumber(evt.podcastExtraInformation?.episode) ? (
-							<Text span c="dimmed" size="sm">
-								(EP-{evt.podcastExtraInformation.episode})
-							</Text>
-						) : null}
-						{isNumber(evt.animeExtraInformation?.episode) ? (
-							<Text span c="dimmed" size="sm">
-								(EP-{evt.animeExtraInformation.episode})
-							</Text>
-						) : null}
+		<Fragment>
+			<Group data-calendar-date={props.data.date}>
+				<Text fz={{ base: "h1" }} fw="bold">
+					{date.format("D")}
+				</Text>
+				<Stack gap={2}>
+					<Text size="sm" style={{ lineHeight: "0.9" }}>
+						{date.format("MMMM")}
 					</Text>
-					<Text size="sm" c="dimmed">
-						{startCase(snakeCase(evt.metadataLot))}
+					<Text size="sm" fw="bold">
+						{date.format("dddd")}
 					</Text>
-				</Group>
-			))}
-		</Card>
+				</Stack>
+			</Group>
+			<ApplicationGrid>
+				{props.data.events.map((evt) => (
+					<DisplayCalendarEvent key={evt.calendarEventId} calEvent={evt} />
+				))}
+			</ApplicationGrid>
+		</Fragment>
+	);
+};
+
+const DisplayCalendarEvent = ({
+	calEvent,
+}: { calEvent: CalendarEventPartFragment }) => {
+	return (
+		<MetadataDisplayItem
+			metadataId={calEvent.metadataId}
+			altName={calEvent.episodeName || calEvent.metadataTitle}
+			rightLabel={`${match(calEvent.metadataLot)
+				.with(
+					MediaLot.Show,
+					() =>
+						`S${calEvent.showExtraInformation?.season}-E${calEvent.showExtraInformation?.episode}`,
+				)
+				.with(
+					MediaLot.Podcast,
+					() => `EP-${calEvent.podcastExtraInformation?.episode}`,
+				)
+				.otherwise(() => "")}`}
+		/>
 	);
 };
diff --git a/apps/frontend/app/routes/_dashboard.media.groups.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.media.groups.item.$id._index.tsx
index f3f1b146a2..72b5468c85 100644
--- a/apps/frontend/app/routes/_dashboard.media.groups.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.groups.item.$id._index.tsx
@@ -13,6 +13,7 @@ import {
 import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
 import { useLoaderData } from "@remix-run/react";
 import {
+	DeployUpdateMetadataGroupJobDocument,
 	EntityLot,
 	MetadataGroupDetailsDocument,
 	UserMetadataGroupDetailsDocument,
@@ -58,6 +59,10 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 				{ metadataGroupId },
 			),
 		]);
+	if (metadataGroupDetails.details.isPartial)
+		await serverGqlService.request(DeployUpdateMetadataGroupJobDocument, {
+			metadataGroupId,
+		});
 	return {
 		query,
 		metadataGroupId,
@@ -80,11 +85,6 @@ export default function Page() {
 		<Container>
 			<MediaDetailsLayout
 				images={loaderData.metadataGroupDetails.details.displayImages}
-				entityDetails={{
-					id: loaderData.metadataGroupId,
-					lot: EntityLot.MetadataGroup,
-					isPartial: loaderData.metadataGroupDetails.details.isPartial,
-				}}
 				externalLink={{
 					source: loaderData.metadataGroupDetails.details.source,
 					lot: loaderData.metadataGroupDetails.details.lot,
diff --git a/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
index 3d926e60d9..3a4dadbe00 100644
--- a/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.item.$id._index.tsx
@@ -40,6 +40,7 @@ import type {
 import { Form, Link, useLoaderData } from "@remix-run/react";
 import {
 	DeleteSeenItemDocument,
+	DeployUpdateMetadataJobDocument,
 	DisassociateMetadataDocument,
 	EntityLot,
 	MediaLot,
@@ -157,6 +158,10 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 			{ metadataId },
 		),
 	]);
+	if (metadataDetails.isPartial)
+		await serverGqlService.request(DeployUpdateMetadataJobDocument, {
+			metadataId,
+		});
 	return { query, metadataId, metadataDetails, userMetadataDetails };
 };
 
@@ -314,11 +319,6 @@ export default function Page() {
 		<Container>
 			<MediaDetailsLayout
 				images={loaderData.metadataDetails.assets.images}
-				entityDetails={{
-					id: loaderData.metadataId,
-					lot: EntityLot.Metadata,
-					isPartial: loaderData.metadataDetails.isPartial,
-				}}
 				externalLink={{
 					source: loaderData.metadataDetails.source,
 					lot: loaderData.metadataDetails.lot,
diff --git a/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx b/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx
index a0b434f757..89e01c5651 100644
--- a/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx
+++ b/apps/frontend/app/routes/_dashboard.media.people.item.$id._index.tsx
@@ -14,6 +14,7 @@ import {
 import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
 import { useLoaderData } from "@remix-run/react";
 import {
+	DeployUpdatePersonJobDocument,
 	EntityLot,
 	PersonDetailsDocument,
 	UserPersonDetailsDocument,
@@ -57,6 +58,10 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
 			personId,
 		}),
 	]);
+	if (personDetails.details.isPartial)
+		await serverGqlService.request(DeployUpdatePersonJobDocument, {
+			personId,
+		});
 	return { query, personId, userPersonDetails, personDetails };
 };
 
@@ -82,11 +87,6 @@ export default function Page() {
 		<Container>
 			<MediaDetailsLayout
 				images={loaderData.personDetails.details.displayImages}
-				entityDetails={{
-					id: loaderData.personId,
-					lot: EntityLot.Person,
-					isPartial: loaderData.personDetails.details.isPartial,
-				}}
 				externalLink={{
 					source: loaderData.personDetails.details.source,
 					href: loaderData.personDetails.sourceUrl,
diff --git a/apps/frontend/app/routes/actions.tsx b/apps/frontend/app/routes/actions.tsx
index 87c5586aee..2901c11191 100644
--- a/apps/frontend/app/routes/actions.tsx
+++ b/apps/frontend/app/routes/actions.tsx
@@ -336,7 +336,6 @@ export const action = async ({ request }: ActionFunctionArgs) => {
 						e.seasonNumber === submission.showSeasonNumber &&
 						e.episodeNumber === submission.showEpisodeNumber,
 				);
-				invariant(selectedEpisodeIndex !== -1);
 				const firstEpisodeOfShow = allEpisodesInShow[0];
 				const lastSeenEpisode = latestHistoryItem?.showExtraInformation || {
 					episode: firstEpisodeOfShow.episodeNumber,
@@ -347,7 +346,6 @@ export const action = async ({ request }: ActionFunctionArgs) => {
 						e.seasonNumber === lastSeenEpisode.season &&
 						e.episodeNumber === lastSeenEpisode.episode,
 				);
-				invariant(lastSeenEpisodeIndex !== -1);
 				const firstEpisodeIndexToMark =
 					lastSeenEpisodeIndex + (latestHistoryItem ? 1 : 0);
 				if (selectedEpisodeIndex > firstEpisodeIndexToMark) {
diff --git a/apps/frontend/app/routes/auth.tsx b/apps/frontend/app/routes/auth.tsx
index d7117bc1f6..ce12194341 100644
--- a/apps/frontend/app/routes/auth.tsx
+++ b/apps/frontend/app/routes/auth.tsx
@@ -14,6 +14,7 @@ import {
 	type ActionFunctionArgs,
 	type LoaderFunctionArgs,
 	type MetaArgs,
+	data,
 	redirect,
 } from "@remix-run/node";
 import { Form, Link, useLoaderData, useSearchParams } from "@remix-run/react";
@@ -102,8 +103,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
 				schema: registerSchema,
 			});
 			if (submission.status !== "success")
-				return Response.json({} as const, {
-					status: 400,
+				return data({} as const, {
 					headers: await createToastHeaders({
 						type: "error",
 						message:
@@ -133,8 +133,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
 						() => "This username already exists",
 					)
 					.exhaustive();
-				return Response.json({} as const, {
-					status: 400,
+				return data({} as const, {
 					headers: await createToastHeaders({ message, type: "error" }),
 				});
 			}
@@ -176,7 +175,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
 					() => "The provider chosen was incorrect",
 				)
 				.exhaustive();
-			return Response.json({} as const, {
+			return data({} as const, {
 				headers: await createToastHeaders({ message, type: "error" }),
 			});
 		})
diff --git a/apps/website/app/routes/features.tsx b/apps/website/app/routes/features.tsx
index c06528c238..89fe233054 100644
--- a/apps/website/app/routes/features.tsx
+++ b/apps/website/app/routes/features.tsx
@@ -1,5 +1,5 @@
 import type { LoaderFunctionArgs, MetaArgs } from "@remix-run/node";
-import { cn } from "@ryot/ts-utils";
+import { cn, snakeCase } from "@ryot/ts-utils";
 import Autoplay from "embla-carousel-autoplay";
 import {
 	LucideAmpersands,
@@ -69,6 +69,7 @@ export default function Page() {
 			{dataToDisplay.map((data, index) => (
 				<div
 					key={data.heading}
+					id={snakeCase(data.heading)}
 					className={cn(
 						"py-10 space-y-4 md:space-y-8",
 						index % 2 === 1 ? "bg-muted" : "bg-white",
diff --git a/crates/models/media/src/lib.rs b/crates/models/media/src/lib.rs
index 20c7fdac3d..e8637e2715 100644
--- a/crates/models/media/src/lib.rs
+++ b/crates/models/media/src/lib.rs
@@ -1545,9 +1545,3 @@ pub struct DailyUserActivityItem {
     pub total_count: i64,
     pub total_duration: i64,
 }
-
-#[derive(Debug, Ord, PartialEq, Eq, PartialOrd, Clone, Hash)]
-pub struct CommitCache {
-    pub id: String,
-    pub lot: EntityLot,
-}
diff --git a/crates/services/miscellaneous/src/lib.rs b/crates/services/miscellaneous/src/lib.rs
index ddb4ccc1f1..a95d6e3164 100644
--- a/crates/services/miscellaneous/src/lib.rs
+++ b/crates/services/miscellaneous/src/lib.rs
@@ -1135,7 +1135,7 @@ ORDER BY RANDOM() LIMIT 10;
         input: Vec<ProgressUpdateInput>,
     ) -> Result<bool> {
         for seen in input {
-            progress_update(seen, &user_id, false, &self.0)
+            progress_update(&user_id, false, seen, &self.0)
                 .await
                 .trace_ok();
         }
diff --git a/crates/services/supporting/Cargo.toml b/crates/services/supporting/Cargo.toml
index 9a1e6c4692..ca5ba20547 100644
--- a/crates/services/supporting/Cargo.toml
+++ b/crates/services/supporting/Cargo.toml
@@ -11,7 +11,5 @@ cache-service = { path = "../../services/cache" }
 chrono-tz = { workspace = true }
 config = { path = "../../config" }
 file-storage-service = { path = "../../services/file-storage" }
-media-models = { path = "../../models/media" }
-moka = { workspace = true }
 openidconnect = { workspace = true }
 sea-orm = { workspace = true }
diff --git a/crates/services/supporting/src/lib.rs b/crates/services/supporting/src/lib.rs
index 717cf75901..f4699086d8 100644
--- a/crates/services/supporting/src/lib.rs
+++ b/crates/services/supporting/src/lib.rs
@@ -5,8 +5,6 @@ use async_graphql::Result;
 use background::{ApplicationJob, CoreApplicationJob};
 use cache_service::CacheService;
 use file_storage_service::FileStorageService;
-use media_models::CommitCache;
-use moka::future::Cache;
 use openidconnect::core::CoreClient;
 use sea_orm::DatabaseConnection;
 
@@ -17,7 +15,6 @@ pub struct SupportingService {
     pub cache_service: CacheService,
     pub config: Arc<config::AppConfig>,
     pub oidc_client: Option<CoreClient>,
-    pub commit_cache: Cache<CommitCache, ()>,
     pub file_storage_service: Arc<FileStorageService>,
 
     perform_application_job: MemoryStorage<ApplicationJob>,
@@ -33,7 +30,6 @@ impl SupportingService {
         cache_service: CacheService,
         config: Arc<config::AppConfig>,
         oidc_client: Option<CoreClient>,
-        commit_cache: Cache<CommitCache, ()>,
         file_storage_service: Arc<FileStorageService>,
         perform_application_job: &MemoryStorage<ApplicationJob>,
         perform_core_application_job: &MemoryStorage<CoreApplicationJob>,
@@ -43,7 +39,6 @@ impl SupportingService {
             is_pro,
             timezone,
             oidc_client,
-            commit_cache,
             cache_service,
             db: db.clone(),
             file_storage_service,
diff --git a/crates/utils/dependent/src/lib.rs b/crates/utils/dependent/src/lib.rs
index c05a26ed4a..e94345d27e 100644
--- a/crates/utils/dependent/src/lib.rs
+++ b/crates/utils/dependent/src/lib.rs
@@ -39,9 +39,9 @@ use fitness_models::{
 use importer_models::{ImportDetails, ImportFailStep, ImportFailedItem, ImportResultResponse};
 use itertools::Itertools;
 use media_models::{
-    CommitCache, CommitMediaInput, CommitPersonInput, CreateOrUpdateCollectionInput,
-    CreateOrUpdateReviewInput, ImportOrExportItemRating, MetadataDetails, MetadataImage,
-    PartialMetadata, PartialMetadataPerson, PartialMetadataWithoutId, ProgressUpdateError,
+    CommitMediaInput, CommitPersonInput, CreateOrUpdateCollectionInput, CreateOrUpdateReviewInput,
+    ImportOrExportItemRating, MetadataDetails, MetadataImage, PartialMetadata,
+    PartialMetadataPerson, PartialMetadataWithoutId, ProgressUpdateError,
     ProgressUpdateErrorVariant, ProgressUpdateInput, ProgressUpdateResultUnion, ReviewPostedEvent,
     SeenAnimeExtraInformation, SeenMangaExtraInformation, SeenPodcastExtraInformation,
     SeenShowExtraInformation,
@@ -692,8 +692,8 @@ pub async fn get_users_and_cte_monitoring_entity(
 ) -> Result<Vec<(String, Uuid)>> {
     let all_entities = MonitoredEntity::find()
         .select_only()
-        .column(monitored_entity::Column::CollectionToEntityId)
         .column(monitored_entity::Column::UserId)
+        .column(monitored_entity::Column::CollectionToEntityId)
         .filter(monitored_entity::Column::EntityId.eq(entity_id))
         .filter(monitored_entity::Column::EntityLot.eq(entity_lot))
         .into_tuple::<(String, Uuid)>()
@@ -875,19 +875,10 @@ pub async fn commit_metadata(
         let media = commit_metadata_internal(details, None, ss).await?;
         return Ok(media);
     };
-    let cached_metadata = CommitCache {
-        id: m.id.clone(),
-        lot: EntityLot::Metadata,
-    };
-    if ss.commit_cache.get(&cached_metadata).await.is_some() {
-        return Ok(m);
-    }
-    ss.commit_cache.insert(cached_metadata.clone(), ()).await;
     if input.force_update.unwrap_or_default() {
         ryot_log!(debug, "Forcing update of metadata with id {}", &m.id);
         update_metadata_and_notify_users(&m.id, true, ss).await?;
     }
-    ss.commit_cache.remove(&cached_metadata).await;
     Ok(m)
 }
 
@@ -1293,10 +1284,10 @@ pub async fn handle_after_media_seen_tasks(
 }
 
 pub async fn progress_update(
-    input: ProgressUpdateInput,
     user_id: &String,
     // update only if media has not been consumed for this user in the last `n` duration
     respect_cache: bool,
+    input: ProgressUpdateInput,
     ss: &Arc<SupportingService>,
 ) -> Result<ProgressUpdateResultUnion> {
     let cache = ApplicationCacheKey::ProgressUpdateCache {
@@ -2020,6 +2011,8 @@ pub async fn process_import(
                 Some(dec!(100))
             };
             if let Err(e) = progress_update(
+                user_id,
+                respect_cache,
                 ProgressUpdateInput {
                     metadata_id: metadata.id.clone(),
                     progress,
@@ -2033,8 +2026,6 @@ pub async fn process_import(
                     provider_watched_on: seen.provider_watched_on.clone(),
                     change_state: None,
                 },
-                user_id,
-                respect_cache,
                 ss,
             )
             .await
@@ -2086,8 +2077,8 @@ pub async fn process_import(
     for (idx, item) in import.metadata_groups.into_iter().enumerate() {
         ryot_log!(
             debug,
-            "Importing media group with identifier = {iden}",
-            iden = &item.title
+            "Importing media group with identifier = {identifier}",
+            identifier = &item.title
         );
         let rev_length = item.reviews.len();
         let data =


Test patch: