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

feat(usersettings): add ability to hide tags on details page #3810

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions server/entity/UserSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ export class UserSettings {
@Column({ nullable: true })
public watchlistSyncTv?: boolean;

@Column({ nullable: true })
public collapseTags?: boolean;

@Column({
type: 'text',
nullable: true,
Expand Down
1 change: 1 addition & 0 deletions server/interfaces/api/userSettingsInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface UserSettingsGeneralResponse {
globalTvQuotaDays?: number;
watchlistSyncMovies?: boolean;
watchlistSyncTv?: boolean;
collapseTags?: boolean;
}

export type NotificationAgentTypes = Record<NotificationAgentKey, number>;
Expand Down
2 changes: 2 additions & 0 deletions server/lib/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export interface MainSettings {
tv: Quota;
};
hideAvailable: boolean;
collapseTags: boolean;
localLogin: boolean;
newPlexLogin: boolean;
region: string;
Expand Down Expand Up @@ -293,6 +294,7 @@ class Settings {
tv: {},
},
hideAvailable: false,
collapseTags: false,
localLogin: true,
newPlexLogin: true,
region: '',
Expand Down
31 changes: 31 additions & 0 deletions server/migration/1710339059307-AddCollapseTagsColumn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { MigrationInterface, QueryRunner } from 'typeorm';

export class AddCollapseTagsColumn1710339059307 implements MigrationInterface {
name = 'AddCollapseTagsColumn1710339059307';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "temporary_user_settings" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "locale" varchar NOT NULL DEFAULT (''), "region" varchar, "originalLanguage" varchar, "pgpKey" varchar, "discordId" varchar, "pushbulletAccessToken" varchar, "pushoverApplicationToken" varchar, "pushoverUserKey" varchar, "pushoverSound" varchar, "telegramChatId" varchar, "telegramSendSilently" boolean, "watchlistSyncMovies" boolean, "watchlistSyncTv" boolean, "notificationTypes" text, "userId" integer, "collapseTags" boolean, CONSTRAINT "REL_986a2b6d3c05eb4091bb8066f7" UNIQUE ("userId"), CONSTRAINT "FK_986a2b6d3c05eb4091bb8066f78" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
);
await queryRunner.query(
`INSERT INTO "temporary_user_settings"("id", "locale", "region", "originalLanguage", "pgpKey", "discordId", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey", "pushoverSound", "telegramChatId", "telegramSendSilently", "watchlistSyncMovies", "watchlistSyncTv", "notificationTypes", "userId") SELECT "id", "locale", "region", "originalLanguage", "pgpKey", "discordId", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey", "pushoverSound", "telegramChatId", "telegramSendSilently", "watchlistSyncMovies", "watchlistSyncTv", "notificationTypes", "userId" FROM "user_settings"`
);
await queryRunner.query(`DROP TABLE "user_settings"`);
await queryRunner.query(
`ALTER TABLE "temporary_user_settings" RENAME TO "user_settings"`
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "user_settings" RENAME TO "temporary_user_settings"`
);
await queryRunner.query(
`CREATE TABLE "user_settings" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "locale" varchar NOT NULL DEFAULT (''), "region" varchar, "originalLanguage" varchar, "pgpKey" varchar, "discordId" varchar, "pushbulletAccessToken" varchar, "pushoverApplicationToken" varchar, "pushoverUserKey" varchar, "pushoverSound" varchar, "telegramChatId" varchar, "telegramSendSilently" boolean, "watchlistSyncMovies" boolean, "watchlistSyncTv" boolean, "notificationTypes" text, "userId" integer, CONSTRAINT "REL_986a2b6d3c05eb4091bb8066f7" UNIQUE ("userId"), CONSTRAINT "FK_986a2b6d3c05eb4091bb8066f78" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
);
await queryRunner.query(
`INSERT INTO "user_settings"("id", "locale", "region", "originalLanguage", "pgpKey", "discordId", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey", "pushoverSound", "telegramChatId", "telegramSendSilently", "watchlistSyncMovies", "watchlistSyncTv", "notificationTypes", "userId") SELECT "id", "locale", "region", "originalLanguage", "pgpKey", "discordId", "pushbulletAccessToken", "pushoverApplicationToken", "pushoverUserKey", "pushoverSound", "telegramChatId", "telegramSendSilently", "watchlistSyncMovies", "watchlistSyncTv", "notificationTypes", "userId" FROM "temporary_user_settings"`
);
await queryRunner.query(`DROP TABLE "temporary_user_settings"`);
}
}
4 changes: 4 additions & 0 deletions server/routes/user/usersettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ userSettingsRoutes.get<{ id: string }, UserSettingsGeneralResponse>(
globalTvQuotaLimit: defaultQuotas.tv.quotaLimit,
watchlistSyncMovies: user.settings?.watchlistSyncMovies,
watchlistSyncTv: user.settings?.watchlistSyncTv,
collapseTags: user.settings?.collapseTags,
});
} catch (e) {
next({ status: 500, message: e.message });
Expand Down Expand Up @@ -118,6 +119,7 @@ userSettingsRoutes.post<
originalLanguage: req.body.originalLanguage,
watchlistSyncMovies: req.body.watchlistSyncMovies,
watchlistSyncTv: req.body.watchlistSyncTv,
collapseTags: req.body.collapseTags,
});
} else {
user.settings.discordId = req.body.discordId;
Expand All @@ -126,6 +128,7 @@ userSettingsRoutes.post<
user.settings.originalLanguage = req.body.originalLanguage;
user.settings.watchlistSyncMovies = req.body.watchlistSyncMovies;
user.settings.watchlistSyncTv = req.body.watchlistSyncTv;
user.settings.collapseTags = req.body.collapseTags;
}

await userRepository.save(user);
Expand All @@ -138,6 +141,7 @@ userSettingsRoutes.post<
originalLanguage: user.settings.originalLanguage,
watchlistSyncMovies: user.settings.watchlistSyncMovies,
watchlistSyncTv: user.settings.watchlistSyncTv,
collapseTags: user.settings.collapseTags,
});
} catch (e) {
next({ status: 500, message: e.message });
Expand Down
66 changes: 66 additions & 0 deletions src/components/KeywordDisclosure/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import Tag from '@app/components/Common/Tag';
import { useUser } from '@app/hooks/useUser';
import { Disclosure, Transition } from '@headlessui/react';
import { ChevronDownIcon } from '@heroicons/react/24/solid';
import type { Keyword } from '@server/models/common';
import Link from 'next/link';

interface KeywordDisclosureProps {
keywords: Keyword[];
type: 'tv' | 'movies';
}

const KeywordDisclosure = ({ keywords, type }: KeywordDisclosureProps) => {
const { user } = useUser();

return (
<>
{keywords.length > 0 && (
<Disclosure defaultOpen={!user?.settings?.collapseTags}>
{({ open }) => (
<>
<Disclosure.Button
className={`mt-2 flex w-full items-center justify-between space-x-2 border-gray-700 bg-gray-800 px-4 py-2 text-gray-200 ${
open
? 'rounded-t-md border-t border-l border-r'
: 'rounded-md border'
}`}
>
<span className="text-lg">Tags</span>
<ChevronDownIcon
className={`${
open ? 'rotate-180' : ''
} h-6 w-6 text-gray-500`}
/>
</Disclosure.Button>
<Transition
show={open}
enter="transition-opacity duration-100 ease-out"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity duration-75 ease-out"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Disclosure.Panel className="w-full rounded-b-md border-b border-l border-r border-gray-700 px-4 py-4">
{keywords.map((keyword) => (
<Link
href={`/discover/${type}?keywords=${keyword.id}`}
key={`keyword-id-${keyword.id}`}
>
<a className="mr-2 inline-flex last:mr-0">
<Tag>{keyword.name}</Tag>
</a>
</Link>
))}
</Disclosure.Panel>
</Transition>
</>
)}
</Disclosure>
)}
</>
);
};

export default KeywordDisclosure;
20 changes: 5 additions & 15 deletions src/components/MovieDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import LoadingSpinner from '@app/components/Common/LoadingSpinner';
import PageTitle from '@app/components/Common/PageTitle';
import type { PlayButtonLink } from '@app/components/Common/PlayButton';
import PlayButton from '@app/components/Common/PlayButton';
import Tag from '@app/components/Common/Tag';
import Tooltip from '@app/components/Common/Tooltip';
import ExternalLinkBlock from '@app/components/ExternalLinkBlock';
import IssueModal from '@app/components/IssueModal';
import KeywordDisclosure from '@app/components/KeywordDisclosure';
import ManageSlideOver from '@app/components/ManageSlideOver';
import MediaSlider from '@app/components/MediaSlider';
import PersonCard from '@app/components/PersonCard';
Expand Down Expand Up @@ -464,20 +464,10 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => {
</div>
</>
)}
{data.keywords.length > 0 && (
<div className="mt-6">
{data.keywords.map((keyword) => (
<Link
href={`/discover/movies?keywords=${keyword.id}`}
key={`keyword-id-${keyword.id}`}
>
<a className="mb-2 mr-2 inline-flex last:mr-0">
<Tag>{keyword.name}</Tag>
</a>
</Link>
))}
</div>
)}
<KeywordDisclosure
keywords={data.keywords}
type="movies"
></KeywordDisclosure>
</div>
<div className="media-overview-right">
{data.collection && (
Expand Down
20 changes: 5 additions & 15 deletions src/components/TvDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import PageTitle from '@app/components/Common/PageTitle';
import type { PlayButtonLink } from '@app/components/Common/PlayButton';
import PlayButton from '@app/components/Common/PlayButton';
import StatusBadgeMini from '@app/components/Common/StatusBadgeMini';
import Tag from '@app/components/Common/Tag';
import Tooltip from '@app/components/Common/Tooltip';
import ExternalLinkBlock from '@app/components/ExternalLinkBlock';
import IssueModal from '@app/components/IssueModal';
import KeywordDisclosure from '@app/components/KeywordDisclosure';
import ManageSlideOver from '@app/components/ManageSlideOver';
import MediaSlider from '@app/components/MediaSlider';
import PersonCard from '@app/components/PersonCard';
Expand Down Expand Up @@ -503,20 +503,10 @@ const TvDetails = ({ tv }: TvDetailsProps) => {
</div>
</>
)}
{data.keywords.length > 0 && (
<div className="mt-6">
{data.keywords.map((keyword) => (
<Link
href={`/discover/tv?keywords=${keyword.id}`}
key={`keyword-id-${keyword.id}`}
>
<a className="mb-2 mr-2 inline-flex last:mr-0">
<Tag>{keyword.name}</Tag>
</a>
</Link>
))}
</div>
)}
<KeywordDisclosure
keywords={data.keywords}
type="tv"
></KeywordDisclosure>
<h2 className="py-4">{intl.formatMessage(messages.seasonstitle)}</h2>
<div className="flex w-full flex-col space-y-2">
{data.seasons
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ const messages = defineMessages({
plexwatchlistsyncseries: 'Auto-Request Series',
plexwatchlistsyncseriestip:
'Automatically request series on your <PlexWatchlistSupportLink>Plex Watchlist</PlexWatchlistSupportLink>',
collapsetags: 'Collapse Tags',
collapsetagstip: 'Collapse tags by default in Movie/Series detail page',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think this sounds slightly better: Collapse tags by default on the Movie/Series details page

});

const UserGeneralSettings = () => {
Expand Down Expand Up @@ -130,6 +132,7 @@ const UserGeneralSettings = () => {
tvQuotaDays: data?.tvQuotaDays,
watchlistSyncMovies: data?.watchlistSyncMovies,
watchlistSyncTv: data?.watchlistSyncTv,
collapseTags: data?.collapseTags,
}}
validationSchema={UserGeneralSettingsSchema}
enableReinitialize
Expand All @@ -149,6 +152,7 @@ const UserGeneralSettings = () => {
tvQuotaDays: tvQuotaEnabled ? values.tvQuotaDays : null,
watchlistSyncMovies: values.watchlistSyncMovies,
watchlistSyncTv: values.watchlistSyncTv,
collapseTags: values.collapseTags,
});

if (currentUser?.id === user?.id && setLocale) {
Expand Down Expand Up @@ -334,6 +338,24 @@ const UserGeneralSettings = () => {
</div>
</div>
</div>
<div className="form-row">
<label htmlFor="collapseTags" className="checkbox-label">
<span>{intl.formatMessage(messages.collapsetags)}</span>
<span className="label-tip">
{intl.formatMessage(messages.collapsetagstip)}
</span>
</label>
<div className="form-input-area">
<Field
type="checkbox"
id="collapseTags"
name="collapseTags"
onChange={() => {
setFieldValue('collapseTags', !values.collapseTags);
}}
/>
</div>
</div>
{currentHasPermission(Permission.MANAGE_USERS) &&
!hasPermission(Permission.MANAGE_USERS) && (
<>
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface UserSettings {
notificationTypes: Partial<NotificationAgentTypes>;
watchlistSyncMovies?: boolean;
watchlistSyncTv?: boolean;
collapseTags?: boolean;
}

interface UserHookResponse {
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,8 @@
"components.UserProfile.UserSettings.UserGeneralSettings.enableOverride": "Override Global Limit",
"components.UserProfile.UserSettings.UserGeneralSettings.general": "General",
"components.UserProfile.UserSettings.UserGeneralSettings.generalsettings": "General Settings",
"components.UserProfile.UserSettings.UserGeneralSettings.collapsetags": "Collapse Tags",
"components.UserProfile.UserSettings.UserGeneralSettings.collapsetagstip": "Collapse tags by default in Movie/Series detail page",
"components.UserProfile.UserSettings.UserGeneralSettings.languageDefault": "Default ({language})",
"components.UserProfile.UserSettings.UserGeneralSettings.localuser": "Local User",
"components.UserProfile.UserSettings.UserGeneralSettings.movierequestlimit": "Movie Request Limit",
Expand Down