Skip to content

Commit

Permalink
Display if media in watchlist/seen (#809)
Browse files Browse the repository at this point in the history
* feat(backend): store whether it is in watchlist

* feat(database): migrate user_to_entity fields

* feat(backend): hoist join out of conditional query

* feat(backend): return media reason for metadata list

* refactor(frontend): extract var

* feat(frontend): pass `reason` to media component

* refactor(backend): rewrite cleanup function

* fix(backend): remove extremely verbose log

* fix(backend): delete old user_to_entity

* refactor(backend): change name of var

* refactor(frontend): change order of funcs

* fix(frontend): remove useless prop

* refactor(frontend): extract component

* feat(frontend): display icon for media reason

* build(backend): bump version

* refactor(frontend): extract common prop into style

* refactor(*): change order of imports
  • Loading branch information
IgnisDa committed May 3, 2024
1 parent 9bc97e4 commit 6ed0f9a
Show file tree
Hide file tree
Showing 14 changed files with 201 additions and 242 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/backend/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ryot"
version = "5.1.0"
version = "5.1.1"
edition = "2021"
repository = "https://github.com/IgnisDa/ryot"
license = "GPL-3.0"
Expand Down
282 changes: 85 additions & 197 deletions apps/backend/src/miscellaneous/resolver.rs

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion apps/backend/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ pub mod media {
pub struct MediaListItem {
pub data: MetadataSearchItem,
pub average_rating: Option<Decimal>,
pub media_reason: Vec<UserToMediaReason>,
}

#[derive(Debug, Serialize, Deserialize, SimpleObject, Clone, FromQueryResult)]
Expand Down Expand Up @@ -892,7 +893,17 @@ pub mod media {
}

#[derive(
Copy, Clone, Debug, PartialEq, Eq, DeriveActiveEnum, EnumIter, Serialize, Deserialize, Hash,
Copy,
Clone,
Debug,
Enum,
PartialEq,
Eq,
DeriveActiveEnum,
EnumIter,
Serialize,
Deserialize,
Hash,
)]
#[sea_orm(rs_type = "String", db_type = "String(None)")]
pub enum UserToMediaReason {
Expand All @@ -908,6 +919,8 @@ pub mod media {
Owned,
#[sea_orm(string_value = "Monitoring")]
Monitoring,
#[sea_orm(string_value = "Watchlist")]
Watchlist,
}

#[derive(Debug, SimpleObject)]
Expand Down
82 changes: 54 additions & 28 deletions apps/frontend/app/components/media.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Image,
Input,
Loader,
type MantineStyleProp,
Menu,
Modal,
NumberInput,
Expand All @@ -28,6 +29,7 @@ import {
Text,
TextInput,
Textarea,
ThemeIcon,
Title,
Tooltip,
useComputedColorScheme,
Expand All @@ -50,6 +52,7 @@ import {
type ReviewItem,
type UserMediaReminderPartFragment,
UserReviewScale,
UserToMediaReason,
Visibility,
} from "@ryot/generated/graphql/backend/graphql";
import { changeCase, formatDateToNaiveDate, getInitials } from "@ryot/ts-utils";
Expand All @@ -58,10 +61,12 @@ import {
IconArrowBigUp,
IconArrowsRight,
IconBackpack,
IconBookmarksFilled,
IconCheck,
IconCloudDownload,
IconEdit,
IconPercentage,
IconRosetteDiscountCheck,
IconStarFilled,
IconTrash,
IconX,
Expand Down Expand Up @@ -444,6 +449,12 @@ export const ReviewItemDisplay = (props: {
);
};

const blackBgStyles = {
backgroundColor: "rgba(0, 0, 0, 0.75)",
borderRadius: 3,
padding: 2,
} satisfies MantineStyleProp;

export const BaseDisplayItem = (props: {
name: string;
onClick?: (e: React.MouseEvent) => Promise<void>;
Expand All @@ -457,6 +468,7 @@ export const BaseDisplayItem = (props: {
highlightRightText?: string;
children?: ReactNode;
nameRight?: JSX.Element;
mediaReason?: UserToMediaReason[];
}) => {
const colorScheme = useComputedColorScheme("dark");

Expand All @@ -480,13 +492,18 @@ export const BaseDisplayItem = (props: {
</Box>
);

const themeIconSurrounder = (idx: number, icon?: JSX.Element) => (
<ThemeIcon variant="transparent" size="sm" color="lime" key={idx}>
{icon}
</ThemeIcon>
);

return (
<Flex
key={`${props.bottomLeft}-${props.bottomRight}-${props.name}`}
align="center"
justify="center"
direction="column"
pos="relative"
>
{props.topLeft}
<SurroundingElement style={{ flex: "none" }} pos="relative">
Expand All @@ -510,7 +527,32 @@ export const BaseDisplayItem = (props: {
getInitials(props.name),
)}
/>
{props.topRight}
<Box pos="absolute" top={5} right={5}>
{props.topRight}
</Box>
{props.mediaReason ? (
<Group
style={blackBgStyles}
pos="absolute"
bottom={5}
left={5}
gap="xs"
>
{props.mediaReason
.map((r) =>
match(r)
.with(UserToMediaReason.Seen, () => (
<IconRosetteDiscountCheck />
))
.with(UserToMediaReason.Watchlist, () => (
<IconBookmarksFilled />
))
.otherwise(() => undefined),
)
.filter(Boolean)
.map((icon, idx) => themeIconSurrounder(idx, icon))}
</Group>
) : null}
</SurroundingElement>
<Flex w="100%" direction="column" px={{ base: 10, md: 3 }} py={4}>
<Flex justify="space-between" direction="row" w="100%">
Expand Down Expand Up @@ -563,8 +605,10 @@ export const MediaItemWithoutUpdateModal = (props: {
noHref?: boolean;
onClick?: (e: React.MouseEvent) => Promise<void>;
nameRight?: JSX.Element;
mediaReason?: UserToMediaReason[];
}) => {
const navigate = useNavigate();
const id = props.item.identifier;

return (
<BaseDisplayItem
Expand All @@ -575,25 +619,19 @@ export const MediaItemWithoutUpdateModal = (props: {
? props.href
: match(props.entityLot)
.with(EntityLot.Media, undefined, null, () =>
$path("/media/item/:id", { id: props.item.identifier }),
$path("/media/item/:id", { id }),
)
.with(EntityLot.MediaGroup, () =>
$path("/media/groups/item/:id", {
id: props.item.identifier,
}),
$path("/media/groups/item/:id", { id }),
)
.with(EntityLot.Person, () =>
$path("/media/people/item/:id", {
id: props.item.identifier,
}),
$path("/media/people/item/:id", { id }),
)
.with(EntityLot.Exercise, () =>
$path("/fitness/exercises/item/:id", {
id: props.item.identifier,
}),
$path("/fitness/exercises/item/:id", { id }),
)
.with(EntityLot.Collection, () =>
$path("/collections/:id", { id: props.item.identifier }),
$path("/collections/:id", { id }),
)
.exhaustive()
: undefined
Expand All @@ -613,18 +651,10 @@ export const MediaItemWithoutUpdateModal = (props: {
/>
) : null
}
mediaReason={props.mediaReason}
topRight={
props.averageRating ? (
<Box
p={2}
pos="absolute"
top={5}
right={5}
style={{
backgroundColor: "rgba(0, 0, 0, 0.75)",
borderRadius: 3,
}}
>
<Box style={blackBgStyles}>
<Flex align="center" gap={4}>
<IconStarFilled size={12} style={{ color: "#EBE600FF" }} />
<Text c="white" size="xs" fw="bold" pr={4}>
Expand Down Expand Up @@ -656,11 +686,7 @@ export const MediaItemWithoutUpdateModal = (props: {
onClick={(e) => {
e.preventDefault();
navigate(
$path(
"/media/item/:id",
{ id: props.item.identifier },
{ openReviewModal: true },
),
$path("/media/item/:id", { id }, { openReviewModal: true }),
);
}}
>
Expand Down
10 changes: 5 additions & 5 deletions apps/frontend/app/lib/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ export function useGetMantineColor() {
export function useSearchParam() {
const [searchParams, setSearchParams] = useSearchParams();

const setP = (key: string, value?: string | null) => {
const delP = (key: string) => {
setSearchParams((prev) => {
if (!value) delP(key);
else prev.set(key, value);
prev.delete(key);
return prev;
});
};

const delP = (key: string) => {
const setP = (key: string, value?: string | null) => {
setSearchParams((prev) => {
prev.delete(key);
if (!value) delP(key);
else prev.set(key, value);
return prev;
});
};
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ export default function Page() {
publishYear: lm.data.publishYear?.toString(),
}}
averageRating={lm.averageRating ?? undefined}
mediaReason={lm.mediaReason}
lot={loaderData.lot}
href={$path("/media/item/:id", {
id: lm.data.identifier,
Expand Down
6 changes: 2 additions & 4 deletions apps/landing/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import sitemap from "@astrojs/sitemap";
import tailwind from "@astrojs/tailwind";
import { defineConfig } from "astro/config";
import robotsTxt from "astro-robots-txt";
import { defineConfig } from "astro/config";

// https://astro.build/config
export default defineConfig({
site: import.meta.env.DEV
? "http://localhost:4200"
: "https://ryot.io/",
site: import.meta.env.DEV ? "http://localhost:4200" : "https://ryot.io/",
integrations: [tailwind(), sitemap(), robotsTxt()],
});
2 changes: 1 addition & 1 deletion apps/landing/src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { Meta } from "@/config/landing.interface";
import "@fontsource-variable/rubik";
interface Props {
meta: Meta;
meta: Meta;
}
const { meta } = Astro.props;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use sea_orm_migration::prelude::*;

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
db.execute_unprepared(r#"update user_to_entity set needs_to_be_updated = true;"#)
.await?;

Ok(())
}

async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
Ok(())
}
}
2 changes: 2 additions & 0 deletions libs/database/src/migrations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod m20231219_create_metadata_relations;
mod m20240415_is_v5_migration;
mod m20240416_change_json_to_generic_json;
mod m20240425_add_created_by_user_id_column_to_execise;
mod m20240503_update_user_to_entity_to_recalculate;

pub use m20230410_create_metadata::Metadata as AliasedMetadata;
pub use m20230413_create_person::Person as AliasedPerson;
Expand Down Expand Up @@ -57,6 +58,7 @@ impl MigratorTrait for Migrator {
Box::new(m20240415_is_v5_migration::Migration),
Box::new(m20240416_change_json_to_generic_json::Migration),
Box::new(m20240425_add_created_by_user_id_column_to_execise::Migration),
Box::new(m20240503_update_user_to_entity_to_recalculate::Migration),
]
}
}
4 changes: 2 additions & 2 deletions libs/generated/src/graphql/backend/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const documents = {
"query MetadataGroupDetails($metadataGroupId: Int!) {\n metadataGroupDetails(metadataGroupId: $metadataGroupId) {\n details {\n id\n title\n lot\n source\n displayImages\n parts\n isPartial\n }\n sourceUrl\n contents {\n ...PartialMetadataPart\n }\n }\n}": types.MetadataGroupDetailsDocument,
"query MetadataGroupSearch($input: MetadataGroupSearchInput!) {\n metadataGroupSearch(input: $input) {\n details {\n total\n nextPage\n }\n items {\n identifier\n name\n image\n parts\n }\n }\n}": types.MetadataGroupSearchDocument,
"query MetadataGroupsList($input: SearchInput!) {\n metadataGroupsList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n id\n title\n lot\n parts\n image\n }\n }\n}": types.MetadataGroupsListDocument,
"query MetadataList($input: MetadataListInput!) {\n metadataList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n averageRating\n data {\n ...MetadataSearchItemPart\n }\n }\n }\n}": types.MetadataListDocument,
"query MetadataList($input: MetadataListInput!) {\n metadataList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n averageRating\n mediaReason\n data {\n ...MetadataSearchItemPart\n }\n }\n }\n}": types.MetadataListDocument,
"query MetadataMainDetails($metadataId: Int!) {\n metadataDetails(metadataId: $metadataId) {\n title\n lot\n source\n isNsfw\n isPartial\n sourceUrl\n identifier\n description\n publishYear\n publishDate\n providerRating\n productionStatus\n originalLanguage\n genres {\n id\n name\n }\n group {\n id\n name\n part\n }\n assets {\n images\n videos {\n videoId\n source\n }\n }\n }\n}": types.MetadataMainDetailsDocument,
"query MetadataSearch($input: MetadataSearchInput!) {\n metadataSearch(input: $input) {\n details {\n total\n nextPage\n }\n items {\n databaseId\n hasInteracted\n item {\n identifier\n title\n image\n publishYear\n }\n }\n }\n}": types.MetadataSearchDocument,
"query PeopleSearch($input: PeopleSearchInput!) {\n peopleSearch(input: $input) {\n details {\n total\n nextPage\n }\n items {\n identifier\n name\n image\n birthYear\n }\n }\n}": types.PeopleSearchDocument,
Expand Down Expand Up @@ -130,7 +130,7 @@ export function graphql(source: "query MetadataGroupsList($input: SearchInput!)
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "query MetadataList($input: MetadataListInput!) {\n metadataList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n averageRating\n data {\n ...MetadataSearchItemPart\n }\n }\n }\n}"): (typeof documents)["query MetadataList($input: MetadataListInput!) {\n metadataList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n averageRating\n data {\n ...MetadataSearchItemPart\n }\n }\n }\n}"];
export function graphql(source: "query MetadataList($input: MetadataListInput!) {\n metadataList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n averageRating\n mediaReason\n data {\n ...MetadataSearchItemPart\n }\n }\n }\n}"): (typeof documents)["query MetadataList($input: MetadataListInput!) {\n metadataList(input: $input) {\n details {\n total\n nextPage\n }\n items {\n averageRating\n mediaReason\n data {\n ...MetadataSearchItemPart\n }\n }\n }\n}"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down

0 comments on commit 6ed0f9a

Please sign in to comment.