diff --git a/db/migrations/20251109010105_add_annotation_rating_date.sql b/db/migrations/20251109010105_add_annotation_rating_date.sql new file mode 100644 index 00000000..9dac46a5 --- /dev/null +++ b/db/migrations/20251109010105_add_annotation_rating_date.sql @@ -0,0 +1,7 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE annotation ADD COLUMN rated_at datetime; +-- +goose StatementEnd + +-- +goose Down + \ No newline at end of file diff --git a/model/annotation.go b/model/annotation.go index 2ec72c1b..fbff5f17 100644 --- a/model/annotation.go +++ b/model/annotation.go @@ -6,6 +6,7 @@ type Annotations struct { PlayCount int64 `structs:"play_count" json:"playCount,omitempty"` PlayDate *time.Time `structs:"play_date" json:"playDate,omitempty" ` Rating int `structs:"rating" json:"rating,omitempty" ` + RatedAt *time.Time `structs:"rated_at" json:"ratedAt,omitempty" ` Starred bool `structs:"starred" json:"starred,omitempty" ` StarredAt *time.Time `structs:"starred_at" json:"starredAt,omitempty"` } diff --git a/model/criteria/fields.go b/model/criteria/fields.go index 70719cd6..5381ae59 100644 --- a/model/criteria/fields.go +++ b/model/criteria/fields.go @@ -44,6 +44,7 @@ var fieldMap = map[string]*mappedField{ "loved": {field: "COALESCE(annotation.starred, false)"}, "dateloved": {field: "annotation.starred_at"}, "lastplayed": {field: "annotation.play_date"}, + "daterated": {field: "annotation.rated_at"}, "playcount": {field: "COALESCE(annotation.play_count, 0)"}, "rating": {field: "COALESCE(annotation.rating, 0)"}, "mbz_album_id": {field: "media_file.mbz_album_id"}, diff --git a/persistence/album_repository.go b/persistence/album_repository.go index b1ce23e2..dab25578 100644 --- a/persistence/album_repository.go +++ b/persistence/album_repository.go @@ -106,6 +106,7 @@ func NewAlbumRepository(ctx context.Context, db dbx.Builder) model.AlbumReposito "random": "random", "recently_added": recentlyAddedSort(), "starred_at": "starred, starred_at", + "rated_at": "rating, rated_at", }) return r } diff --git a/persistence/artist_repository.go b/persistence/artist_repository.go index 6d08c27d..c9e38a1e 100644 --- a/persistence/artist_repository.go +++ b/persistence/artist_repository.go @@ -141,6 +141,7 @@ func NewArtistRepository(ctx context.Context, db dbx.Builder) model.ArtistReposi r.setSortMappings(map[string]string{ "name": "order_artist_name", "starred_at": "starred, starred_at", + "rated_at": "rating, rated_at", "song_count": "stats->>'total'->>'m'", "album_count": "stats->>'total'->>'a'", "size": "stats->>'total'->>'s'", diff --git a/persistence/mediafile_repository.go b/persistence/mediafile_repository.go index e7883947..8f32accc 100644 --- a/persistence/mediafile_repository.go +++ b/persistence/mediafile_repository.go @@ -84,6 +84,7 @@ func NewMediaFileRepository(ctx context.Context, db dbx.Builder) model.MediaFile "created_at": "media_file.created_at", "recently_added": mediaFileRecentlyAddedSort(), "starred_at": "starred, starred_at", + "rated_at": "rating, rated_at", }) return r } diff --git a/persistence/playlist_repository.go b/persistence/playlist_repository.go index 046284e1..a94f95a7 100644 --- a/persistence/playlist_repository.go +++ b/persistence/playlist_repository.go @@ -388,6 +388,7 @@ func (r *playlistRepository) loadTracks(sel SelectBuilder, id string) (model.Pla "coalesce(play_count, 0) as play_count", "play_date", "coalesce(rating, 0) as rating", + "rated_at", "f.*", "playlist_tracks.*", "library.path as library_path", diff --git a/persistence/playlist_track_repository.go b/persistence/playlist_track_repository.go index b3f9e0c0..666f227e 100644 --- a/persistence/playlist_track_repository.go +++ b/persistence/playlist_track_repository.go @@ -97,6 +97,7 @@ func (r *playlistTrackRepository) Read(id string) (interface{}, error) { "coalesce(rating, 0) as rating", "starred_at", "play_date", + "rated_at", "f.*", "playlist_tracks.*", ). diff --git a/persistence/sql_annotations.go b/persistence/sql_annotations.go index 98ade6e2..108e9be9 100644 --- a/persistence/sql_annotations.go +++ b/persistence/sql_annotations.go @@ -28,6 +28,7 @@ func (r sqlRepository) withAnnotation(query SelectBuilder, idField string) Selec "coalesce(rating, 0) as rating", "starred_at", "play_date", + "rated_at", ) if conf.Server.AlbumPlayCountMode == consts.AlbumPlayCountModeNormalized && r.tableName == "album" { query = query.Columns( @@ -77,7 +78,8 @@ func (r sqlRepository) SetStar(starred bool, ids ...string) error { } func (r sqlRepository) SetRating(rating int, itemID string) error { - return r.annUpsert(map[string]interface{}{"rating": rating}, itemID) + ratedAt := time.Now() + return r.annUpsert(map[string]interface{}{"rating": rating, "rated_at": ratedAt}, itemID) } func (r sqlRepository) IncPlayCount(itemID string, ts time.Time) error { diff --git a/ui/src/common/DateField.jsx b/ui/src/common/DateField.jsx index fab15b53..dce24a2b 100644 --- a/ui/src/common/DateField.jsx +++ b/ui/src/common/DateField.jsx @@ -1,10 +1,11 @@ import React from 'react' +import { isDateSet } from '../utils/validations' import { DateField as RADateField } from 'react-admin' export const DateField = (props) => { const { record, source } = props const value = record?.[source] - if (value === '0001-01-01T00:00:00Z' || value === null) return null + if (!isDateSet(value)) return null return } diff --git a/ui/src/common/LoveButton.jsx b/ui/src/common/LoveButton.jsx index f42d92ff..c940acf1 100644 --- a/ui/src/common/LoveButton.jsx +++ b/ui/src/common/LoveButton.jsx @@ -7,6 +7,7 @@ import { makeStyles } from '@material-ui/core/styles' import { useToggleLove } from './useToggleLove' import { useRecordContext } from 'react-admin' import config from '../config' +import { isDateSet } from '../utils/validations' const useStyles = makeStyles({ love: { @@ -46,8 +47,13 @@ export const LoveButton = ({