Skip to content

Commit

Permalink
Merge pull request #615 from leepeuker/add-location-option-cinema
Browse files Browse the repository at this point in the history
Add location option cinema
  • Loading branch information
leepeuker authored Sep 4, 2024
2 parents d25eee6 + 7a209de commit a6b081c
Show file tree
Hide file tree
Showing 16 changed files with 185 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php declare(strict_types=1);

use Phinx\Migration\AbstractMigration;

final class AddCinemaFlagToLocationTable extends AbstractMigration
{
public function down() : void
{
$this->execute(
<<<SQL
ALTER TABLE location DROP COLUMN is_cinema;
SQL,
);
}

public function up() : void
{
$this->execute(
<<<SQL
ALTER TABLE location ADD COLUMN is_cinema TINYINT(1) DEFAULT 0 AFTER name;
SQL,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php declare(strict_types=1);

use Phinx\Migration\AbstractMigration;

final class AddCinemaFlagToLocationTable extends AbstractMigration
{
public function down() : void
{
$this->execute(
<<<SQL
CREATE TABLE `location_tmp` (
`id` INTEGER NOT NULL,
`user_id` TEXT NOT NULL,
`name` TEXT NOT NULL,
`created_at` TEXT NOT NULL,
`updated_at` TEXT DEFAULT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`user_id`) REFERENCES user (`id`) ON DELETE CASCADE
)
SQL,
);
$this->execute(
'INSERT INTO `location_tmp` (id, user_id, name, created_at, updated_at)
SELECT id, user_id, name, created_at, updated_at FROM location',
);
$this->execute('DROP TABLE `location`');
$this->execute('ALTER TABLE `location_tmp` RENAME TO `location`');
}

public function up() : void
{

$this->execute(
<<<SQL
CREATE TABLE `location_tmp` (
`id` INTEGER NOT NULL,
`user_id` TEXT NOT NULL,
`name` TEXT NOT NULL,
`is_cinema` INTEGER DEFAULT 0,
`created_at` TEXT NOT NULL,
`updated_at` TEXT DEFAULT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`user_id`) REFERENCES user (`id`) ON DELETE CASCADE
)
SQL,
);
$this->execute(
'INSERT INTO `location_tmp` (id, user_id, name, created_at, updated_at)
SELECT * FROM location',
);
$this->execute('DROP TABLE `location`');
$this->execute('ALTER TABLE `location_tmp` RENAME TO `location`');
}
}
4 changes: 3 additions & 1 deletion docs/features/locations.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
Locations (e.g. a cinema) can be set for a play.

Each user maintains a personal collection of available locations. The locations can be managed in the user settings at `/settings/account/locations`.
A location can be marked as a cinema.

When a users visibility is not set to private, others can see if a location is set for a play but not which exact location.

!!! Info

This feature is optional and can be disabled.

Disabling keeps the current data, but hides all locations UI elements.
Disabling keeps the current data, but hides all locations UI elements.

23 changes: 16 additions & 7 deletions public/js/settings-account-location.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ async function reloadTable(featureIsEnabled = true) {
locations.forEach((location) => {
let row = document.createElement('tr');
row.dataset.id = location.id
row.innerHTML += '<td >' + location.name + '</td>';
row.innerHTML += '<td>' + location.name + '</td>';
row.innerHTML += '<td class="d-none">' + location.isCinema + '</td>';
row.style.cursor = 'pointer'

table.getElementsByTagName('tbody')[0].appendChild(row);
Expand All @@ -78,24 +79,27 @@ function registerTableRowClickEvent() {
if (i === 0) continue

rows[i].onclick = function () {

prepareEditLocationsModal(
this.dataset.id,
this.cells[0].innerHTML
this.cells[0].innerHTML,
this.cells[1].innerHTML === 'true'
)

locationModal.show()
};
}
}

function prepareEditLocationsModal(id, name) {
function prepareEditLocationsModal(id, name, isCinema) {
document.getElementById('locationModalHeaderTitle').innerHTML = 'Edit Location'

document.getElementById('locationModalFooterCreateButton').classList.add('d-none')
document.getElementById('locationModalFooterButtons').classList.remove('d-none')

document.getElementById('locationModalIdInput').value = id
document.getElementById('locationModalNameInput').value = name
document.getElementById('locationModalCinemaInput').checked = isCinema

document.getElementById('locationModalAlerts').innerHTML = ''

Expand All @@ -116,6 +120,7 @@ function prepareCreateLocationModal(name) {

document.getElementById('locationModalIdInput').value = ''
document.getElementById('locationModalNameInput').value = ''
document.getElementById('locationModalCinemaInput').value = ''

document.getElementById('locationModalAlerts').innerHTML = ''

Expand All @@ -129,13 +134,15 @@ document.getElementById('createLocationButton').addEventListener('click', async
}

let categoryName = document.getElementById('locationModalNameInput').value;
let isCinema = document.getElementById('locationModalCinemaInput').value;
const response = await fetch('/settings/locations', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'name': categoryName,
'isCinema': isCinema
})
})

Expand Down Expand Up @@ -206,14 +213,16 @@ document.getElementById('updateLocationButton').addEventListener('click', async
return;
}

let categoryName = document.getElementById('locationModalNameInput').value;
let locationName = document.getElementById('locationModalNameInput').value;
let isCinema = document.getElementById('locationModalCinemaInput').checked;
const response = await fetch('/settings/locations/' + document.getElementById('locationModalIdInput').value, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'name': categoryName
'name': locationName,
'isCinema': isCinema
})
})

Expand All @@ -225,9 +234,9 @@ document.getElementById('updateLocationButton').addEventListener('click', async

let url = window.location.href;
if (url.indexOf('?') > -1){
url += '&categoryUpdated=' + categoryName
url += '&categoryUpdated=' + locationName
} else {
url += '?categoryUpdated=' + categoryName
url += '?categoryUpdated=' + locationName
}
window.location.href = url;
})
Expand Down
8 changes: 4 additions & 4 deletions src/Domain/Movie/History/Location/MovieHistoryLocationApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ public function __construct(private readonly MovieHistoryLocationRepository $loc
{
}

public function createLocation(int $userId, string $name) : void
public function createLocation(int $userId, string $name, bool $isCinema) : void
{
$this->locationRepository->createLocation($userId, $name);
$this->locationRepository->createLocation($userId, $name, $isCinema);
}

public function deleteLocation(int $locationId) : void
Expand All @@ -28,8 +28,8 @@ public function findLocationsByUserId(int $userId) : MovieHistoryLocationEntityL
return $this->locationRepository->findLocationsByUserId($userId);
}

public function updateLocation(int $locationId, string $name) : void
public function updateLocation(int $locationId, string $name, bool $isCinema) : void
{
$this->locationRepository->updateLocation($locationId, $name);
$this->locationRepository->updateLocation($locationId, $name, $isCinema);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ private function __construct(
private readonly int $id,
private readonly int $userId,
private readonly string $name,
private readonly bool $isCinema,
) {
}

Expand All @@ -19,6 +20,7 @@ public static function createFromArray(array $data) : self
(int)$data['id'],
(int)$data['user_id'],
(string)$data['name'],
(bool)$data['is_cinema'],
);
}

Expand All @@ -32,6 +34,7 @@ public function jsonSerialize() : array
return [
'id' => $this->id,
'name' => $this->name,
'isCinema' => $this->isCinema,
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function __construct(private readonly Connection $dbConnection)
{
}

public function createLocation(int $userId, string $name) : void
public function createLocation(int $userId, string $name, bool $isCinema) : void
{
$timestamp = DateTime::create();

Expand All @@ -22,6 +22,7 @@ public function createLocation(int $userId, string $name) : void
[
'user_id' => $userId,
'name' => $name,
'is_cinema' => (int)$isCinema,
'created_at' => (string)$timestamp,
'updated_at' => (string)$timestamp,
],
Expand Down Expand Up @@ -57,12 +58,13 @@ public function findLocationById(int $locationId) : ?MovieHistoryLocationEntity
return MovieHistoryLocationEntity::createFromArray($data);
}

public function updateLocation(int $locationId, string $name) : void
public function updateLocation(int $locationId, string $name, bool $isCinema) : void
{
$this->dbConnection->update(
'location',
[
'name' => $name,
'is_cinema' => (int)$isCinema,
'updated_at' => (string)DateTime::create(),
],
[
Expand Down
7 changes: 7 additions & 0 deletions src/Domain/Movie/History/MovieHistoryApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,13 @@ public function fetchLastPlays(int $userId) : array
return $this->urlGenerator->replacePosterPathWithImageSrcUrl($lastPlays);
}

public function fetchLastPlaysCinema(int $userId) : array
{
$lastPlays = $this->movieRepository->fetchLastPlaysCinema($userId);

return $this->urlGenerator->replacePosterPathWithImageSrcUrl($lastPlays);
}

public function fetchMostWatchedActorsCount(int $userId, ?string $searchTerm = null, ?Gender $gender = null, ?int $personFilterUserId = null) : int
{
return $this->movieRepository->fetchActorsCount($userId, $searchTerm, $gender, $personFilterUserId);
Expand Down
14 changes: 14 additions & 0 deletions src/Domain/Movie/MovieRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,20 @@ public function fetchLastPlays(int $userId) : array
)->fetchAllAssociative();
}

public function fetchLastPlaysCinema(int $userId) : array
{
return $this->dbConnection->executeQuery(
'SELECT m.*, mh.watched_at, mur.rating AS user_rating
FROM movie m
JOIN movie_user_watch_dates mh ON mh.movie_id = m.id AND mh.user_id = ? AND mh.watched_at IS NOT NULL
LEFT JOIN movie_user_rating mur ON mh.movie_id = mur.movie_id AND mur.user_id = ?
WHERE location_id IS NOT NULL
ORDER BY watched_at DESC, mh.position DESC
LIMIT 6',
[$userId, $userId],
)->fetchAllAssociative();
}

public function fetchMostWatchedGenres(int $userId) : array
{
return $this->dbConnection->fetchAllAssociative(
Expand Down
1 change: 1 addition & 0 deletions src/HttpController/Web/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public function render(Request $request) : Response
'mostWatchedReleaseYears' => $this->movieHistoryApi->fetchMostWatchedReleaseYears($userId),
'watchlistItems' => $this->movieWatchlistApi->fetchWatchlistPaginated($userId, 6, 1),
'topLocations' => $this->movieHistoryApi->fetchTopLocations($userId),
'lastPlaysCinema' => $this->movieHistoryApi->fetchLastPlaysCinema($userId),
'dashboardRows' => $dashboardRows,
]),
);
Expand Down
2 changes: 2 additions & 0 deletions src/HttpController/Web/LocationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function createLocation(Request $request) : Response
$this->locationApi->createLocation(
$currentUser->getId(),
$requestData['name'],
$requestData['isCinema'],
);

return Response::createOk();
Expand Down Expand Up @@ -92,6 +93,7 @@ public function updateLocation(Request $request) : Response
$this->locationApi->updateLocation(
$locationId,
$requestData['name'],
(bool)$requestData['isCinema'],
);

return Response::createOk();
Expand Down
2 changes: 2 additions & 0 deletions src/Service/Dashboard/DashboardFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public function createDefaultDashboardRows() : DashboardRowList
DashboardRow::createMostWatchedReleaseYears(),
DashboardRow::createWatchlist(),
DashboardRow::createTopLocations(),
DashboardRow::createLastPlaysCinema(),
);
}

Expand All @@ -72,6 +73,7 @@ private function createDashboardRowById(int $rowId, bool $isVisible, bool $isExt
DashboardRow::createMostWatchedReleaseYears()->getId() === $rowId => DashboardRow::createMostWatchedReleaseYears($isVisible, $isExtended),
DashboardRow::createWatchlist()->getId() === $rowId => DashboardRow::createWatchlist($isVisible, $isExtended),
DashboardRow::createTopLocations()->getId() === $rowId => DashboardRow::createTopLocations($isVisible, $isExtended),
DashboardRow::createLastPlaysCinema()->getId() === $rowId => DashboardRow::createLastPlaysCinema($isVisible, $isExtended),

default => throw new RuntimeException('Not supported dashboard row id: ' . $rowId)
};
Expand Down
10 changes: 10 additions & 0 deletions src/Service/Dashboard/Dto/DashboardRow.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ public static function createTopLocations(bool $isVisible = true, bool $isExtend
return self::create(10, 'Top Locations', $isVisible, $isExtended);
}

public static function createLastPlaysCinema(bool $isVisible = true, bool $isExtended = true) : self
{
return self::create(11, 'Last Plays Cinema', $isVisible, $isExtended);
}

private static function create(int $id, string $name, bool $isVisible, bool $isExtended) : self
{
return new self($id, $name, $isVisible, $isExtended);
Expand Down Expand Up @@ -136,4 +141,9 @@ public function isTopLocations() : bool
{
return $this->getId() === self::createTopLocations()->getId();
}

public function isLastPlaysCinema() : bool
{
return $this->getId() === self::createLastPlaysCinema()->getId();
}
}
32 changes: 32 additions & 0 deletions templates/component/dashboard/row-last-plays-cinema.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{% macro create(dashboardRow, lastPlaysCinema, routeUsername) %}

{% import 'component/dashboard/row-item-toggle.html.twig' as rowItemToggle %}
{{ rowItemToggle.create(dashboardRow) }}

<li class="list-group-item {{ dashboardRow.isExtended ? 'activeItem' : 'inactiveItem' }}" style="padding: 1rem 0.4rem 0 0.4rem;">
<div class="row row-cols-3 row-cols-md-3 row-cols-lg-6">
{% for lastPlay in lastPlaysCinema %}
<div class="col" style="padding-bottom: 1rem;">
<div class="card h-100" style="cursor: pointer" onclick="window.location='/users/{{ routeUsername }}/movies/{{ lastPlay.id }}'">
<div style="height: 100%">
<img src="{{ lastPlay.poster_path }}"
class="card-img-top"
alt="{{ lastPlay.title }} Poster">

</div>
<div class="card-footer" style="padding: 0.1rem">
<small class="text-muted" style="font-size: 0.8rem;">{{ lastPlay.watched_at|date(dateFormatPhp) }}</small>
</div>

{% if lastPlay.user_rating is not null %}
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-warning text-dark" style="font-size: 0.8rem">
{{ lastPlay.user_rating }}
</span>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<a class="btn btn-outline-secondary btn-sm" href="/users/{{ routeUsername }}/history" style="margin-bottom: 1rem;width: 100%;border-color: lightgrey">more</a>
</li>
{% endmacro %}
Loading

0 comments on commit a6b081c

Please sign in to comment.