Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow hiding collections #1234

Merged
merged 24 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b67a3dd
chore(services/integration): make it a debug log
IgnisDa Feb 2, 2025
f959bb5
feat(migrations): add new column for storing collection to entity
IgnisDa Feb 4, 2025
43cf9d1
fix(services/importer): better trakt errors
IgnisDa Feb 4, 2025
b7c5df8
feat(backend): allow hiding collections
IgnisDa Feb 4, 2025
daa20e8
feat(backend): allow hiding collections
IgnisDa Feb 4, 2025
4f5ac24
feat(backend): return extra information for user collection list
IgnisDa Feb 4, 2025
9ec90d5
docs: add information about importing data back
IgnisDa Feb 4, 2025
1f4abc9
feat(utils/dependent): insert with conflict correctly
IgnisDa Feb 5, 2025
c307231
fix(services/collection): include current user in the collabs list
IgnisDa Feb 5, 2025
3214192
fix(utils/dependent): allow showing collection back
IgnisDa Feb 5, 2025
99a38ea
feat(frontend): allow marking collections as hidden
IgnisDa Feb 5, 2025
ef77891
feat(backend): remove name filter for collections list
IgnisDa Feb 5, 2025
1967e02
refactor(frontend): make filter more efficient
IgnisDa Feb 5, 2025
8d4292b
fix(frontend): always trim the search input
IgnisDa Feb 5, 2025
e83a1a3
fix(frontend): display correct number of collaborators
IgnisDa Feb 5, 2025
721e61d
feat(frontend): do not show hidden collections
IgnisDa Feb 5, 2025
b60ef00
feat(frontend): allow display hidden collections
IgnisDa Feb 5, 2025
65f1b40
feat(frontend): remove hidden collections from all filters
IgnisDa Feb 5, 2025
2a4a56f
feat(frontend): display only user collections
IgnisDa Feb 5, 2025
17dc5f4
feat(frontend): display only non hidden collections
IgnisDa Feb 5, 2025
5b46081
refactor(frontend): change name of hook
IgnisDa Feb 5, 2025
d985bb3
ci: Run CI
IgnisDa Feb 5, 2025
d983e53
chore(frontend): change order of attributes
IgnisDa Feb 5, 2025
8c5259b
chore(frontend): disable checkbox if server key is not validated
IgnisDa Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/frontend/app/components/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ import {
useFallbackImageUrl,
useGetMantineColors,
useGetRandomMantineColor,
useUserCollections,
useNonHiddenUserCollections,
useUserDetails,
useUserPreferences,
useUserUnitSystem,
Expand Down Expand Up @@ -265,7 +265,7 @@ export const DebouncedSearchInput = (props: {
);

useDidUpdate(() => {
setP(props.queryParam || "query", debounced);
setP(props.queryParam || "query", debounced.trim());
}, [debounced]);

return (
Expand Down Expand Up @@ -508,7 +508,7 @@ export const CollectionsFilter = (props: {
collections?: string[];
invertCollection?: boolean;
}) => {
const collections = useUserCollections();
const collections = useNonHiddenUserCollections();
const [_, { setP }] = useAppSearchParam(props.cookieName);

return (
Expand All @@ -525,8 +525,8 @@ export const CollectionsFilter = (props: {
{
group: "My collections",
items: collections.map((c) => ({
value: c.id.toString(),
label: c.name,
value: c.id.toString(),
})),
},
]}
Expand Down
11 changes: 11 additions & 0 deletions apps/frontend/app/lib/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,17 @@ export const useUserPreferences = () => useUserDetails().preferences;
export const useUserCollections = () =>
useDashboardLayoutData().userCollections;

export const useNonHiddenUserCollections = () => {
const userCollections = useUserCollections();
const userDetails = useUserDetails();
const toDisplay = userCollections.filter(
(c) =>
c.collaborators.find((c) => c.collaborator.id === userDetails.id)
?.extraInformation?.isHidden !== true,
);
return toDisplay;
};

export const useUserUnitSystem = () =>
useUserPreferences().fitness.exercises.unitSystem;

Expand Down
77 changes: 53 additions & 24 deletions apps/frontend/app/routes/_dashboard.collections.list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,7 @@ import {
import { useQuery } from "@tanstack/react-query";
import { ClientError } from "graphql-request";
import { useEffect, useState } from "react";
import {
Form,
Link,
useLoaderData,
useNavigation,
useSearchParams,
} from "react-router";
import { Form, Link, useLoaderData, useNavigation } from "react-router";
import { Virtuoso } from "react-virtuoso";
import { $path } from "safe-routes";
import { match } from "ts-pattern";
Expand All @@ -79,6 +73,7 @@ import {
zodCommaDelimitedString,
} from "~/lib/generals";
import {
useAppSearchParam,
useConfirmSubmit,
useCoreDetails,
useFallbackImageUrl,
Expand Down Expand Up @@ -181,6 +176,9 @@ const createOrUpdateSchema = z.object({
updateId: z.string().optional(),
description: z.string().optional(),
collaborators: zodCommaDelimitedString,
extraInformation: z
.object({ isHidden: zodCheckboxAsString.optional() })
.optional(),
informationTemplate: z
.array(
z.object({
Expand All @@ -205,23 +203,30 @@ type UpdateCollectionInput = {

export default function Page() {
const transition = useNavigation();
const userDetails = useUserDetails();
const collections = useUserCollections();
const loaderData = useLoaderData<typeof loader>();
const [params] = useSearchParams();
const query = params.get("query") || undefined;

const filteredCollections = collections.filter((c) =>
query ? c.name.toLowerCase().includes(query.toLowerCase()) : true,
);

const [toUpdateCollection, setToUpdateCollection] =
useState<UpdateCollectionInput | null>(null);
const [params, { setP }] = useAppSearchParam(loaderData.cookieName);
const query = params.get("query") || undefined;
const showHidden = Boolean(params.get("showHidden"));

useEffect(() => {
if (transition.state !== "submitting") {
setToUpdateCollection(null);
}
if (transition.state !== "submitting") setToUpdateCollection(null);
}, [transition.state]);

const filteredCollections = collections
.filter((c) =>
showHidden
? true
: c.collaborators.find((c) => c.collaborator.id === userDetails.id)
?.extraInformation?.isHidden !== true,
)
.filter((c) =>
query ? c.name.toLowerCase().includes(query.toLowerCase()) : true,
);

return (
<Container size="sm">
<Stack>
Expand Down Expand Up @@ -249,10 +254,19 @@ export default function Page() {
cacheId={loaderData.userCollectionsList.cacheId}
/>
</Group>
<DebouncedSearchInput
initialValue={query}
enhancedQueryParams={loaderData.cookieName}
/>
<Group wrap="nowrap">
<DebouncedSearchInput
initialValue={query}
enhancedQueryParams={loaderData.cookieName}
/>
<Checkbox
size="xs"
name="showHidden"
label="Show hidden"
defaultChecked={showHidden}
onChange={(e) => setP("showHidden", e.target.checked ? "yes" : "")}
/>
</Group>
<Virtuoso
style={{ height: "80vh" }}
data={filteredCollections}
Expand Down Expand Up @@ -330,9 +344,9 @@ const DisplayCollection = (props: {
additionalDisplay.push(`By ${props.collection.creator.name}`);
if (props.collection.count > 0)
additionalDisplay.push(`${props.collection.count} items`);
if (props.collection.collaborators.length > 0)
if (props.collection.collaborators.length > 1)
additionalDisplay.push(
`${props.collection.collaborators.length} collaborators`,
`${props.collection.collaborators.length - 1} collaborators`,
);

const FallBackImage = () => (
Expand Down Expand Up @@ -538,6 +552,21 @@ const CreateOrUpdateModal = (props: {
label="Description"
defaultValue={props.toUpdateCollection?.description}
/>
<Tooltip
label={PRO_REQUIRED_MESSAGE}
disabled={coreDetails.isServerKeyValidated}
>
<Checkbox
label="Hide collection"
name="extraInformation.isHidden"
disabled={!coreDetails.isServerKeyValidated}
defaultChecked={
props.toUpdateCollection?.collaborators?.find(
(c) => c.collaborator.id === userDetails.id,
)?.extraInformation?.isHidden || undefined
}
/>
</Tooltip>
<Tooltip
label={PRO_REQUIRED_MESSAGE}
disabled={coreDetails.isServerKeyValidated}
Expand All @@ -548,7 +577,7 @@ const CreateOrUpdateModal = (props: {
disabled={!coreDetails.isServerKeyValidated}
description="Add collaborators to this collection"
defaultValue={(props.toUpdateCollection?.collaborators || []).map(
(c) => c.id,
(c) => c.collaborator.id,
)}
data={loaderData.usersList.map((u) => ({
value: u.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ import {
useAppSearchParam,
useCoreDetails,
useIsFitnessActionActive,
useUserCollections,
useNonHiddenUserCollections,
useUserPreferences,
} from "~/lib/hooks";
import {
Expand Down Expand Up @@ -331,7 +331,7 @@ export default function Page() {
const FiltersModalForm = () => {
const loaderData = useLoaderData<typeof loader>();
const coreDetails = useCoreDetails();
const collections = useUserCollections();
const collections = useNonHiddenUserCollections();
const [_, { setP }] = useAppSearchParam(loaderData.cookieName);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import {
useApplicationEvents,
useConfirmSubmit,
useCoreDetails,
useUserCollections,
useNonHiddenUserCollections,
} from "~/lib/hooks";
import {
createToastHeaders,
Expand Down Expand Up @@ -188,7 +188,7 @@ export default function Page() {
const coreDetails = useCoreDetails();
const submit = useConfirmSubmit();
const fileUploadNotAllowed = !coreDetails.fileStorageEnabled;
const userCollections = useUserCollections();
const userCollections = useNonHiddenUserCollections();
const events = useApplicationEvents();
const [deployImportSource, setDeployImportSource] = useState<ImportSource>();

Expand Down
4 changes: 2 additions & 2 deletions apps/frontend/app/routes/_dashboard.settings.integrations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import {
import {
useConfirmSubmit,
useCoreDetails,
useUserCollections,
useNonHiddenUserCollections,
} from "~/lib/hooks";
import { createToastHeaders, serverGqlService } from "~/lib/utilities.server";
import type { Route } from "./+types/_dashboard.settings.integrations";
Expand Down Expand Up @@ -625,7 +625,7 @@ const CreateIntegrationModal = (props: {
};

const ArrInputs = (props: { name: string }) => {
const collections = useUserCollections();
const collections = useNonHiddenUserCollections();

return (
<>
Expand Down
4 changes: 2 additions & 2 deletions apps/frontend/app/routes/_dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ import {
useGetWatchProviders,
useIsFitnessActionActive,
useMetadataDetails,
useUserCollections,
useNonHiddenUserCollections,
useUserDetails,
useUserMetadataDetails,
useUserPreferences,
Expand Down Expand Up @@ -1776,7 +1776,7 @@ const AddEntityToCollectionForm = ({
closeAddEntityToCollectionModal: () => void;
}) => {
const userDetails = useUserDetails();
const collections = useUserCollections();
const collections = useNonHiddenUserCollections();
const events = useApplicationEvents();
const submit = useConfirmSubmit();
const [selectedCollection, setSelectedCollection] =
Expand Down
2 changes: 2 additions & 0 deletions crates/migrations/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mod m20250118_is_v8_migration;
mod m20250122_changes_for_issue_1188;
mod m20250126_changes_for_issue_1201;
mod m20250201_changes_for_issue_1211;
mod m20250204_changes_for_issue_1231;

pub use m20230404_create_user::User as AliasedUser;
pub use m20230410_create_metadata::Metadata as AliasedMetadata;
Expand Down Expand Up @@ -81,6 +82,7 @@ impl MigratorTrait for Migrator {
Box::new(m20250122_changes_for_issue_1188::Migration),
Box::new(m20250126_changes_for_issue_1201::Migration),
Box::new(m20250201_changes_for_issue_1211::Migration),
Box::new(m20250204_changes_for_issue_1231::Migration),
]
}
}
20 changes: 10 additions & 10 deletions crates/migrations/src/m20231017_create_user_to_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use indoc::indoc;
use sea_orm_migration::prelude::*;

use super::{
m20230410_create_metadata::Metadata, m20230413_create_person::Person,
m20230404_create_user::User, m20230411_create_metadata_group::MetadataGroup,
m20230404_create_user::User, m20230410_create_metadata::Metadata,
m20230411_create_metadata_group::MetadataGroup, m20230413_create_person::Person,
m20230504_create_collection::Collection, m20230505_create_exercise::Exercise,
};

Expand Down Expand Up @@ -36,22 +36,21 @@ pub static CONSTRAINT_SQL: &str = indoc! { r#"
/// - has reviewed it
#[derive(Iden)]
pub enum UserToEntity {
Table,
Id,
Table,
UserId,
PersonId,
CreatedOn,
LastUpdatedOn,
NeedsToBeUpdated,
// the entities that can be associated
MetadataId,
ExerciseId,
PersonId,
MetadataGroupId,
MediaReason,
CollectionId,
// specifics
LastUpdatedOn,
MetadataGroupId,
NeedsToBeUpdated,
ExerciseExtraInformation,
CollectionExtraInformation,
ExerciseNumTimesInteracted,
MediaReason,
}

#[async_trait::async_trait]
Expand Down Expand Up @@ -91,6 +90,7 @@ impl MigrationTrait for Migration {
.default(PgFunc::gen_random_uuid())
.primary_key(),
)
.col(ColumnDef::new(UserToEntity::CollectionExtraInformation).json_binary())
.foreign_key(
ForeignKey::create()
.name("user_to_entity-fk1")
Expand Down
20 changes: 20 additions & 0 deletions crates/migrations/src/m20250204_changes_for_issue_1231.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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#"ALTER TABLE "user_to_entity" ADD COLUMN "collection_extra_information" JSONB"#,
)
.await?;
Ok(())
}

async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
Ok(())
}
}
17 changes: 17 additions & 0 deletions crates/models/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,20 @@ pub struct YoutubeMusicSongListened {
pub id: String,
pub listened_on: NaiveDate,
}

#[derive(
Eq,
Debug,
Clone,
Default,
Serialize,
PartialEq,
InputObject,
Deserialize,
SimpleObject,
FromJsonQueryResult,
)]
#[graphql(input_name = "UserToCollectionExtraInformationInput")]
pub struct UserToCollectionExtraInformation {
pub is_hidden: Option<bool>,
}
Loading