feat: add WatchMedium field, general review editing, configurable deploy
Some checks failed
CI / Check / Test (push) Failing after 27m0s

- WatchMedium enum (cinema/streaming/tv/physical_media/download/media_server/other)
- PATCH /api/v1/reviews/:id partial update (rating, comment, watched_at, watch_medium)
- edit_review use case w/ ownership + remote review guard, best-effort AP Update broadcast
- SPA: icon picker, edit sheet (long-press mobile / pencil desktop), watch medium badge
- shared ReviewFormFields, EditableContextMenu, parse_watched_at/format_watched_at
- deploy.sh parameterized (--features, --tag), CORS allows PATCH
- CONTEXT.md glossary, ADR-0001 general review editing
This commit is contained in:
2026-07-10 00:01:14 +02:00
parent 9794babe06
commit 29cc68b07c
72 changed files with 1298 additions and 124 deletions

View File

@@ -41,8 +41,8 @@ impl ReviewRepository for SqliteReviewRepository {
};
sqlx::query(
"INSERT INTO reviews (id, movie_id, user_id, rating, comment, watched_at, created_at, remote_actor_url)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
"INSERT INTO reviews (id, movie_id, user_id, rating, comment, watched_at, created_at, remote_actor_url, watch_medium)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
)
.bind(&id)
.bind(&movie_id)
@@ -52,6 +52,7 @@ impl ReviewRepository for SqliteReviewRepository {
.bind(&watched_at)
.bind(&created_at)
.bind(&remote_actor_url)
.bind(review.watch_medium().map(|wm| wm.to_string()))
.execute(&self.pool)
.await
.map_err(Self::map_err)?;
@@ -68,7 +69,7 @@ impl ReviewRepository for SqliteReviewRepository {
async fn get_review_by_id(&self, review_id: &ReviewId) -> Result<Option<Review>, DomainError> {
let id = review_id.value().to_string();
sqlx::query_as::<_, ReviewRow>(
"SELECT id, movie_id, user_id, rating, comment, watched_at, created_at, remote_actor_url
"SELECT id, movie_id, user_id, rating, comment, watched_at, created_at, remote_actor_url, watch_medium
FROM reviews WHERE id = ?",
)
.bind(&id)
@@ -79,6 +80,28 @@ impl ReviewRepository for SqliteReviewRepository {
.transpose()
}
async fn update_review(&self, review: &Review) -> Result<(), DomainError> {
let id = review.id().value().to_string();
let rating = review.rating().value() as i64;
let comment = review.comment().map(|c| c.value().to_string());
let watched_at = datetime_to_str(review.watched_at());
let watch_medium = review.watch_medium().map(|wm| wm.to_string());
sqlx::query(
"UPDATE reviews SET rating = ?, comment = ?, watched_at = ?, watch_medium = ? WHERE id = ?",
)
.bind(rating)
.bind(&comment)
.bind(&watched_at)
.bind(&watch_medium)
.bind(&id)
.execute(&self.pool)
.await
.map_err(Self::map_err)?;
Ok(())
}
async fn delete_review(&self, review_id: &ReviewId) -> Result<(), DomainError> {
let id = review_id.value().to_string();
sqlx::query("DELETE FROM reviews WHERE id = ?")
@@ -92,7 +115,7 @@ impl ReviewRepository for SqliteReviewRepository {
async fn get_all_reviews_for_user(&self, user_id: &UserId) -> Result<Vec<Review>, DomainError> {
let uid = user_id.value().to_string();
sqlx::query_as::<_, ReviewRow>(
"SELECT id, movie_id, user_id, rating, comment, watched_at, created_at, remote_actor_url
"SELECT id, movie_id, user_id, rating, comment, watched_at, created_at, remote_actor_url, watch_medium
FROM reviews WHERE user_id = ? ORDER BY watched_at DESC",
)
.bind(&uid)