Skip to content

Commit c46f315

Browse files
authored
Merge pull request #525 from leepeuker/add-person-more-button
Add person more button to frontend and add backend endpoints
2 parents 797306d + 181b30b commit c46f315

15 files changed

+301
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types=1);
2+
3+
use Phinx\Migration\AbstractMigration;
4+
5+
final class CreateUserPersonSettingsTable extends AbstractMigration
6+
{
7+
public function down() : void
8+
{
9+
$this->execute('DROP TABLE `user_person_settings`');
10+
}
11+
12+
public function up() : void
13+
{
14+
$this->execute(
15+
<<<SQL
16+
CREATE TABLE `user_person_settings` (
17+
`user_id` INT(10) UNSIGNED NOT NULL,
18+
`person_id` INT(10) UNSIGNED NOT NULL,
19+
`is_hidden_in_top_lists` TINYINT(1) DEFAULT 0,
20+
`updated_at` DATETIME NOT NULL,
21+
PRIMARY KEY (`user_id`, `person_id`),
22+
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON DELETE CASCADE,
23+
FOREIGN KEY (`person_id`) REFERENCES `person`(`id`) ON DELETE CASCADE
24+
) COLLATE="utf8mb4_unicode_ci" ENGINE=InnoDB
25+
SQL,
26+
);
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php declare(strict_types=1);
2+
3+
use Phinx\Migration\AbstractMigration;
4+
5+
final class CreateUserPersonSettingsTable extends AbstractMigration
6+
{
7+
public function down() : void
8+
{
9+
$this->execute('DROP TABLE `user_person_settings`');
10+
}
11+
12+
public function up() : void
13+
{
14+
$this->execute(
15+
<<<SQL
16+
CREATE TABLE `user_person_settings` (
17+
`user_id` INT(10) NOT NULL,
18+
`person_id` INT(10) NOT NULL,
19+
`is_hidden_in_top_lists` INTEGER DEFAULT 0,
20+
`updated_at` TEXT NOT NULL,
21+
PRIMARY KEY (`user_id`, `person_id`),
22+
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON DELETE CASCADE,
23+
FOREIGN KEY (`person_id`) REFERENCES `person`(`id`) ON DELETE CASCADE
24+
)
25+
SQL,
26+
);
27+
}
28+
}

Diff for: public/js/person.js

+63-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
function toggleBiography()
2-
{
1+
function toggleBiography() {
32
let expandContainer = document.getElementById('expandContainer');
4-
if(document.getElementsByClassName('truncated').length > 0) {
3+
if (document.getElementsByClassName('truncated').length > 0) {
54
document.getElementById('biographyParagraph').classList.remove('truncated');
65
expandContainer.getElementsByTagName('i')[0].classList.remove('bi-chevron-down');
76
expandContainer.getElementsByTagName('i')[0].classList.add('bi-chevron-up');
@@ -17,8 +16,68 @@ function toggleBiography()
1716
document.addEventListener("DOMContentLoaded", () => {
1817
let biographyHeight = document.getElementById('biographyParagraph').offsetHeight;
1918
let windowHeight = window.outerHeight;
20-
if(((biographyHeight / windowHeight) * 100) > 20) {
19+
if (((biographyHeight / windowHeight) * 100) > 20) {
2120
document.getElementById('biographyParagraph').classList.add('truncated');
2221
document.getElementById('expandContainer').classList.remove('d-none');
2322
}
2423
});
24+
25+
function refreshTmdbData() {
26+
disableMoreModalButtons(true)
27+
removeAlert('alertPersonOptionModalDiv')
28+
29+
sendRequest('refresh-tmdb').then(() => {
30+
location.reload()
31+
}).catch(() => {
32+
addAlert('alertPersonOptionModalDiv', 'Cannot refresh TMDB data', 'danger')
33+
}).finally(() => {
34+
disableMoreModalButtons(false)
35+
})
36+
}
37+
38+
function hideInTopLists() {
39+
disableMoreModalButtons(true)
40+
removeAlert('alertPersonOptionModalDiv')
41+
42+
sendRequest('hide-in-top-lists').then(() => {
43+
document.getElementById('hideInTopListsButton').classList.add('d-none');
44+
document.getElementById('showInTopListsButton').classList.remove('d-none');
45+
addAlert('alertPersonOptionModalDiv', 'Person not visible in top lists anymore', 'success')
46+
}).catch(() => {
47+
addAlert('alertPersonOptionModalDiv', 'Cannot hide person in top lists', 'danger')
48+
}).finally(() => {
49+
disableMoreModalButtons(false)
50+
})
51+
}
52+
53+
function showInTopLists() {
54+
disableMoreModalButtons(true)
55+
removeAlert('alertPersonOptionModalDiv')
56+
57+
sendRequest('show-in-top-lists').then(() => {
58+
document.getElementById('hideInTopListsButton').classList.remove('d-none');
59+
document.getElementById('showInTopListsButton').classList.add('d-none');
60+
addAlert('alertPersonOptionModalDiv', 'Person visible in top lists again', 'success')
61+
}).catch(() => {
62+
addAlert('alertPersonOptionModalDiv', 'Cannot show person in top lists', 'danger')
63+
}).finally(() => {
64+
disableMoreModalButtons(false)
65+
})
66+
}
67+
68+
async function sendRequest(action) {
69+
const personId = document.getElementById('personId').value;
70+
const response = await fetch('/persons/' + personId + '/' + action)
71+
72+
if (!response.ok) {
73+
throw new Error(`HTTP error! status: ${response.status}`)
74+
}
75+
76+
return true
77+
}
78+
79+
function disableMoreModalButtons(disable) {
80+
document.getElementById('refreshTmdbDataButton').disabled = disable;
81+
document.getElementById('hideInTopListsButton').disabled = disable;
82+
document.getElementById('showInTopListsButton').disabled = disable;
83+
}

Diff for: settings/routes.php

+7
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,13 @@ function addWebRoutes(RouterService $routerService, FastRoute\RouteCollector $ro
161161
$routes->add('GET', '/movies/{id:[0-9]+}/add-watchlist', [Web\Movie\MovieWatchlistController::class, 'addToWatchlist'], [Web\Middleware\UserIsAuthenticated::class]);
162162
$routes->add('GET', '/movies/{id:[0-9]+}/remove-watchlist', [Web\Movie\MovieWatchlistController::class, 'removeFromWatchlist'], [Web\Middleware\UserIsAuthenticated::class]);
163163

164+
##########
165+
# Person #
166+
##########
167+
$routes->add('GET', '/persons/{id:[0-9]+}/refresh-tmdb', [Web\PersonController::class, 'refreshTmdbData'], [Web\Middleware\UserIsAuthenticated::class]);
168+
$routes->add('GET', '/persons/{id:[0-9]+}/hide-in-top-lists', [Web\PersonController::class, 'hideInTopLists'], [Web\Middleware\UserIsAuthenticated::class]);
169+
$routes->add('GET', '/persons/{id:[0-9]+}/show-in-top-lists', [Web\PersonController::class, 'showInTopLists'], [Web\Middleware\UserIsAuthenticated::class]);
170+
164171
##############
165172
# User media #
166173
##############

Diff for: src/Domain/Movie/History/MovieHistoryApi.php

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public function fetchActors(
7575
string $sortBy = 'uniqueAppearances',
7676
?SortOrder $sortOrder = null,
7777
?Gender $gender = null,
78+
?int $personFilterUserId = null,
7879
) : array {
7980
if ($sortOrder === null) {
8081
$sortOrder = SortOrder::createDesc();
@@ -88,6 +89,7 @@ public function fetchActors(
8889
$sortBy,
8990
$sortOrder,
9091
$gender,
92+
$personFilterUserId,
9193
);
9294

9395
foreach ($actors as $index => $actor) {

Diff for: src/Domain/Movie/MovieRepository.php

+15-2
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,17 @@ public function fetchActors(
8181
string $sortBy,
8282
SortOrder $sortOrder,
8383
?Gender $gender,
84+
?int $personFilterUserId,
8485
) : array {
85-
$payload = [$userId, "%$searchTerm%"];
86+
$payload = [$userId];
87+
88+
$personFilterJoin = '';
89+
if ($personFilterUserId !== null) {
90+
$personFilterJoin = 'LEFT JOIN user_person_settings ups ON ups.person_id = p.id AND ups.user_id = ?';
91+
$payload[] = $personFilterUserId;
92+
}
93+
94+
$payload[] = "%$searchTerm%";
8695

8796
$offset = ($limit * $page) - $limit;
8897

@@ -92,12 +101,15 @@ public function fetchActors(
92101
default => 'name'
93102
};
94103

95-
$whereQuery = 'WHERE p.name LIKE ? AND p.name != "Stan Lee" ';
104+
$whereQuery = 'WHERE p.name LIKE ? ';
96105

97106
if (empty($gender) === false) {
98107
$whereQuery .= 'AND p.gender = ? ';
99108
$payload[] = $gender;
100109
}
110+
if ($personFilterUserId !== null) {
111+
$whereQuery .= 'AND ups.is_hidden_in_top_lists IS NULL OR ups.is_hidden_in_top_lists != 1';
112+
}
101113

102114
return $this->dbConnection->fetchAllAssociative(
103115
<<<SQL
@@ -106,6 +118,7 @@ public function fetchActors(
106118
JOIN movie_cast mc ON m.id = mc.movie_id
107119
JOIN person p ON mc.person_id = p.id
108120
JOIN movie_user_watch_dates muwd on mc.movie_id = muwd.movie_id and muwd.user_id = ?
121+
$personFilterJoin
109122
$whereQuery
110123
GROUP BY mc.person_id, name
111124
ORDER BY $sortBySanitized $sortOrder, name asc

Diff for: src/Domain/Person/PersonApi.php

+5
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ public function findByTmdbId(int $tmdbId) : ?PersonEntity
103103
return $this->repository->findByTmdbId($tmdbId);
104104
}
105105

106+
public function updateHideInTopLists(int $userId, int $personId, bool $isHidden) : void
107+
{
108+
$this->repository->updateHideInTopLists($userId, $personId, $isHidden);
109+
}
110+
106111
public function update(
107112
int $id,
108113
int $tmdbId,

Diff for: src/Domain/Person/PersonRepository.php

+19
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,25 @@ public function update(
146146
return $this->fetchById($id);
147147
}
148148

149+
public function updateHideInTopLists(int $userId, int $personId, bool $isHidden) : void
150+
{
151+
$this->dbConnection->executeQuery('DELETE FROM user_person_settings WHERE user_id = ? AND person_id = ?', [$userId, $personId]);
152+
153+
if ($isHidden === false) {
154+
return;
155+
}
156+
157+
$this->dbConnection->insert(
158+
'user_person_settings',
159+
[
160+
'user_id' => $userId,
161+
'person_id' => $personId,
162+
'is_hidden_in_top_lists' => 1,
163+
'updated_at' => (string)DateTime::create()
164+
],
165+
);
166+
}
167+
149168
private function fetchById(int $id) : PersonEntity
150169
{
151170
$data = $this->dbConnection->fetchAssociative('SELECT * FROM `person` WHERE id = ?', [$id]);

Diff for: src/Domain/User/UserApi.php

+5
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,11 @@ public function generateApiToken(int $userId) : void
249249
$this->repository->createApiToken($userId, $token);
250250
}
251251

252+
public function hasHiddenPerson(int $userId, int $personId) : bool
253+
{
254+
return $this->repository->hasHiddenPerson($userId, $personId);
255+
}
256+
252257
public function hasUsers() : bool
253258
{
254259
return $this->repository->getCountOfUsers() > 0;

Diff for: src/Domain/User/UserRepository.php

+14
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,20 @@ public function getCountOfUsers() : int
431431
return $count;
432432
}
433433

434+
public function hasHiddenPerson(int $userId, int $personId) : bool
435+
{
436+
$userPersonSettings = $this->dbConnection->fetchAllAssociative(
437+
'SELECT * FROM user_person_settings WHERE user_id = ? AND person_id = ?',
438+
[$userId, $personId],
439+
);
440+
441+
if (isset($userPersonSettings[0]['is_hidden_in_top_lists']) === false) {
442+
return false;
443+
}
444+
445+
return (bool)$userPersonSettings[0]['is_hidden_in_top_lists'];
446+
}
447+
434448
public function setEmbyWebhookId(int $userId, ?string $embyWebhookId) : void
435449
{
436450
$this->dbConnection->update(

Diff for: src/HttpController/Web/ActorsController.php

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Movary\HttpController\Web;
44

55
use Movary\Domain\Movie\History\MovieHistoryApi;
6+
use Movary\Domain\User\Service\Authentication;
67
use Movary\Domain\User\Service\UserPageAuthorizationChecker;
78
use Movary\HttpController\Web\Mapper\PersonsRequestMapper;
89
use Movary\Service\PaginationElementsCalculator;
@@ -19,6 +20,7 @@ public function __construct(
1920
private readonly UserPageAuthorizationChecker $userPageAuthorizationChecker,
2021
private readonly PersonsRequestMapper $requestMapper,
2122
private readonly PaginationElementsCalculator $paginationElementsCalculator,
23+
private readonly Authentication $authenticationService,
2224
) {
2325
}
2426

@@ -31,6 +33,11 @@ public function renderPage(Request $request) : Response
3133

3234
$requestData = $this->requestMapper->mapRenderPageRequest($request);
3335

36+
$currentUserId = null;
37+
if ($this->authenticationService->isUserAuthenticated() === true) {
38+
$currentUserId = $this->authenticationService->getCurrentUserId();
39+
}
40+
3441
$actors = $this->movieHistoryApi->fetchActors(
3542
$userId,
3643
$requestData->getLimit(),
@@ -39,6 +46,7 @@ public function renderPage(Request $request) : Response
3946
$requestData->getSortBy(),
4047
$requestData->getSortOrder(),
4148
$requestData->getGender(),
49+
personFilterUserId: $currentUserId,
4250
);
4351

4452
$actorsCount = $this->movieHistoryApi->fetchMostWatchedActorsCount($userId, $requestData->getSearchTerm(), $requestData->getGender());

Diff for: src/HttpController/Web/DashboardController.php

+9-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Movary\Domain\Movie\History\MovieHistoryApi;
66
use Movary\Domain\Movie\MovieApi;
77
use Movary\Domain\Movie\Watchlist\MovieWatchlistApi;
8+
use Movary\Domain\User\Service\Authentication;
89
use Movary\Domain\User\Service\UserPageAuthorizationChecker;
910
use Movary\Domain\User\UserApi;
1011
use Movary\Service\Dashboard\DashboardFactory;
@@ -24,6 +25,7 @@ public function __construct(
2425
private readonly UserPageAuthorizationChecker $userPageAuthorizationChecker,
2526
private readonly DashboardFactory $dashboardFactory,
2627
private readonly UserApi $userApi,
28+
private readonly Authentication $authenticationService,
2729
) {
2830
}
2931

@@ -34,6 +36,11 @@ public function render(Request $request) : Response
3436
return Response::createForbiddenRedirect($request->getPath());
3537
}
3638

39+
$currentUserId = null;
40+
if ($this->authenticationService->isUserAuthenticated() === true) {
41+
$currentUserId = $this->authenticationService->getCurrentUserId();
42+
}
43+
3744
$dashboardRows = $this->dashboardFactory->createDashboardRowsForUser($this->userApi->fetchUser($userId));
3845

3946
return Response::create(
@@ -48,8 +55,8 @@ public function render(Request $request) : Response
4855
'averageRuntime' => $this->movieHistoryApi->fetchAverageRuntime($userId),
4956
'firstDiaryEntry' => $this->movieHistoryApi->fetchFirstHistoryWatchDate($userId),
5057
'lastPlays' => $this->movieHistoryApi->fetchLastPlays($userId),
51-
'mostWatchedActors' => $this->movieHistoryApi->fetchActors($userId, 6, 1, gender: Gender::createMale()),
52-
'mostWatchedActresses' => $this->movieHistoryApi->fetchActors($userId, 6, 1, gender: Gender::createFemale()),
58+
'mostWatchedActors' => $this->movieHistoryApi->fetchActors($userId, 6, 1, gender: Gender::createMale(), personFilterUserId: $currentUserId),
59+
'mostWatchedActresses' => $this->movieHistoryApi->fetchActors($userId, 6, 1, gender: Gender::createFemale(), personFilterUserId: $currentUserId),
5360
'mostWatchedDirectors' => $this->movieHistoryApi->fetchDirectors($userId, 6, 1),
5461
'mostWatchedLanguages' => $this->movieHistoryApi->fetchMostWatchedLanguages($userId),
5562
'mostWatchedGenres' => $this->movieHistoryApi->fetchMostWatchedGenres($userId),

0 commit comments

Comments
 (0)