Albums: Improve UX and indexing

This commit is contained in:
Michael Mayer 2020-12-08 22:40:13 +01:00
parent def8d50995
commit ee49073cf2
8 changed files with 53 additions and 19 deletions

View file

@ -91,7 +91,7 @@
:title="$gettext('Archive')" :title="$gettext('Archive')"
@click.stop="dialog.archive = true" @click.stop="dialog.archive = true"
:disabled="selection.length === 0" :disabled="selection.length === 0"
v-if="!manualAlbum && context !== 'archive' && $config.feature('archive')" v-if="!isAlbum && context !== 'archive' && $config.feature('archive')"
class="action-archive" class="action-archive"
> >
<v-icon>archive</v-icon> <v-icon>archive</v-icon>
@ -113,7 +113,7 @@
color="remove" color="remove"
@click.stop="removeFromAlbum" @click.stop="removeFromAlbum"
:disabled="selection.length === 0" :disabled="selection.length === 0"
v-if="manualAlbum" v-if="isAlbum"
class="action-delete" class="action-delete"
> >
<v-icon>remove</v-icon> <v-icon>remove</v-icon>
@ -153,7 +153,7 @@ export default {
return { return {
config: this.$config.values, config: this.$config.values,
expanded: false, expanded: false,
manualAlbum: this.album && this.album.Type === 'album', isAlbum: this.album && this.album.Type === 'album',
dialog: { dialog: {
archive: false, archive: false,
album: false, album: false,

View file

@ -174,6 +174,11 @@
<translate>Add photos or videos from search results by selecting them.</translate> <translate>Add photos or videos from search results by selecting them.</translate>
</button> </button>
</div> </div>
<div class="caption mb-2" v-else-if="album.Type === 'folder'">
<button @click.exact="edit(album)">
/{{ album.Path | truncate(100) }}
</button>
</div>
<div class="caption mb-2 d-block" v-if="album.Location"> <div class="caption mb-2 d-block" v-if="album.Location">
<button @click.exact="edit(album)"> <button @click.exact="edit(album)">

View file

@ -29,7 +29,7 @@ import (
func ClearAlbumThumbCache(uid string) { func ClearAlbumThumbCache(uid string) {
cache := service.Cache() cache := service.Cache()
for typeName, _ := range thumb.Types { for typeName := range thumb.Types {
cacheKey := fmt.Sprintf("album-thumbs:%s:%s", uid, typeName) cacheKey := fmt.Sprintf("album-thumbs:%s:%s", uid, typeName)
if err := cache.Delete(cacheKey); err == nil { if err := cache.Delete(cacheKey); err == nil {
@ -229,7 +229,6 @@ func LikeAlbum(router *gin.RouterGroup) {
return return
} }
conf := service.Config()
id := c.Param("uid") id := c.Param("uid")
album, err := query.AlbumByUID(id) album, err := query.AlbumByUID(id)
@ -238,8 +237,10 @@ func LikeAlbum(router *gin.RouterGroup) {
return return
} }
album.AlbumFavorite = true if err := album.Update("AlbumFavorite", true); err != nil {
conf.Db().Save(&album) Abort(c, http.StatusInternalServerError, i18n.ErrSaveFailed)
return
}
UpdateClientConfig() UpdateClientConfig()
PublishAlbumEvent(EntityUpdated, id, c) PublishAlbumEvent(EntityUpdated, id, c)
@ -261,7 +262,6 @@ func DislikeAlbum(router *gin.RouterGroup) {
return return
} }
conf := service.Config()
id := c.Param("uid") id := c.Param("uid")
album, err := query.AlbumByUID(id) album, err := query.AlbumByUID(id)
@ -270,8 +270,10 @@ func DislikeAlbum(router *gin.RouterGroup) {
return return
} }
album.AlbumFavorite = false if err := album.Update("AlbumFavorite", false); err != nil {
conf.Db().Save(&album) Abort(c, http.StatusInternalServerError, i18n.ErrSaveFailed)
return
}
UpdateClientConfig() UpdateClientConfig()
PublishAlbumEvent(EntityUpdated, id, c) PublishAlbumEvent(EntityUpdated, id, c)

View file

@ -31,6 +31,7 @@ type Album struct {
CoverUID string `gorm:"type:VARBINARY(42);" json:"CoverUID" yaml:"CoverUID,omitempty"` CoverUID string `gorm:"type:VARBINARY(42);" json:"CoverUID" yaml:"CoverUID,omitempty"`
FolderUID string `gorm:"type:VARBINARY(42);index;" json:"FolderUID" yaml:"FolderUID,omitempty"` FolderUID string `gorm:"type:VARBINARY(42);index;" json:"FolderUID" yaml:"FolderUID,omitempty"`
AlbumSlug string `gorm:"type:VARBINARY(255);index;" json:"Slug" yaml:"Slug"` AlbumSlug string `gorm:"type:VARBINARY(255);index;" json:"Slug" yaml:"Slug"`
AlbumPath string `gorm:"type:VARBINARY(768);index;" json:"Path" yaml:"-"`
AlbumType string `gorm:"type:VARBINARY(8);default:'album';" json:"Type" yaml:"Type,omitempty"` AlbumType string `gorm:"type:VARBINARY(8);default:'album';" json:"Type" yaml:"Type,omitempty"`
AlbumTitle string `gorm:"type:VARCHAR(255);" json:"Title" yaml:"Title"` AlbumTitle string `gorm:"type:VARCHAR(255);" json:"Title" yaml:"Title"`
AlbumLocation string `gorm:"type:VARCHAR(255);" json:"Location" yaml:"Location,omitempty"` AlbumLocation string `gorm:"type:VARCHAR(255);" json:"Location" yaml:"Location,omitempty"`
@ -41,7 +42,6 @@ type Album struct {
AlbumFilter string `gorm:"type:VARBINARY(1024);" json:"Filter" yaml:"Filter,omitempty"` AlbumFilter string `gorm:"type:VARBINARY(1024);" json:"Filter" yaml:"Filter,omitempty"`
AlbumOrder string `gorm:"type:VARBINARY(32);" json:"Order" yaml:"Order,omitempty"` AlbumOrder string `gorm:"type:VARBINARY(32);" json:"Order" yaml:"Order,omitempty"`
AlbumTemplate string `gorm:"type:VARBINARY(255);" json:"Template" yaml:"Template,omitempty"` AlbumTemplate string `gorm:"type:VARBINARY(255);" json:"Template" yaml:"Template,omitempty"`
AlbumPath string `gorm:"type:VARBINARY(768);" json:"Path" yaml:"-"`
AlbumCountry string `gorm:"type:VARBINARY(2);index:idx_albums_country_year_month;default:'zz'" json:"Country" yaml:"Country,omitempty"` AlbumCountry string `gorm:"type:VARBINARY(2);index:idx_albums_country_year_month;default:'zz'" json:"Country" yaml:"Country,omitempty"`
AlbumYear int `gorm:"index:idx_albums_country_year_month;" json:"Year" yaml:"Year,omitempty"` AlbumYear int `gorm:"index:idx_albums_country_year_month;" json:"Year" yaml:"Year,omitempty"`
AlbumMonth int `gorm:"index:idx_albums_country_year_month;" json:"Month" yaml:"Month,omitempty"` AlbumMonth int `gorm:"index:idx_albums_country_year_month;" json:"Month" yaml:"Month,omitempty"`
@ -212,10 +212,21 @@ func NewMonthAlbum(albumTitle, albumSlug string, year, month int) *Album {
} }
// FindAlbumBySlug finds a matching album or returns nil. // FindAlbumBySlug finds a matching album or returns nil.
func FindAlbumBySlug(slug, albumType string) *Album { func FindAlbumBySlug(albumSlug, albumType string) *Album {
result := Album{} result := Album{}
if err := UnscopedDb().Where("album_slug = ? AND album_type = ?", slug, albumType).First(&result).Error; err != nil { if err := UnscopedDb().Where("album_slug = ? AND album_type = ?", albumSlug, albumType).First(&result).Error; err != nil {
return nil
}
return &result
}
// FindFolderAlbum finds a matching folder album or returns nil.
func FindFolderAlbum(albumSlug, albumPath string) *Album {
result := Album{}
if err := UnscopedDb().Where("((album_slug <> '' AND album_slug = ?) OR album_path = ?) AND album_type = ?", albumSlug, albumPath, AlbumFolder).First(&result).Error; err != nil {
return nil return nil
} }
@ -309,6 +320,17 @@ func (m *Album) Update(attr string, value interface{}) error {
return UnscopedDb().Model(m).UpdateColumn(attr, value).Error return UnscopedDb().Model(m).UpdateColumn(attr, value).Error
} }
// UpdatePath sets a unique path for an albums.
func (m *Album) UpdatePath(albumPath string) error {
if err := m.Update("AlbumPath", albumPath); err != nil {
return err
} else if err := UnscopedDb().Exec("UPDATE albums SET album_path = NULL WHERE album_path = ? AND id <> ?", albumPath, m.ID).Error; err != nil {
return err
}
return nil
}
// Save updates the existing or inserts a new row. // Save updates the existing or inserts a new row.
func (m *Album) Save() error { func (m *Album) Save() error {
return Db().Save(m).Error return Db().Save(m).Error

View file

@ -126,7 +126,12 @@ func (m *Folder) SetValuesFromPath() {
// Slug returns a slug based on the folder title. // Slug returns a slug based on the folder title.
func (m *Folder) Slug() string { func (m *Folder) Slug() string {
return slug.Make(m.FolderTitle) return slug.Make(m.Path)
}
// RootPath returns the full folder path including root.
func (m *Folder) RootPath() string {
return path.Join(m.Root, m.Path)
} }
// Title returns a human readable folder title. // Title returns a human readable folder title.

View file

@ -127,8 +127,8 @@ func TestFolder_SetValuesFromPath(t *testing.T) {
func TestFolder_Slug(t *testing.T) { func TestFolder_Slug(t *testing.T) {
t.Run("/", func(t *testing.T) { t.Run("/", func(t *testing.T) {
folder := Folder{FolderTitle: "Beautiful beach"} folder := Folder{FolderTitle: "Beautiful beach", Root: "sidecar", Path: "ugly/beach"}
assert.Equal(t, "beautiful-beach", folder.Slug()) assert.Equal(t, "ugly-beach", folder.Slug())
}) })
} }

View file

@ -72,11 +72,11 @@ func (m *Moments) Start() (err error) {
Public: true, Public: true,
} }
if a := entity.FindAlbumBySlug(mom.Slug(), entity.AlbumFolder); a != nil { if a := entity.FindFolderAlbum(mom.Slug(), mom.Path); a != nil {
if a.DeletedAt != nil { if a.DeletedAt != nil {
// Nothing to do. // Nothing to do.
log.Tracef("moments: %s was deleted (%s)", txt.Quote(a.AlbumTitle), a.AlbumFilter) log.Tracef("moments: %s was deleted (%s)", txt.Quote(a.AlbumTitle), a.AlbumFilter)
} else if err := a.Update("AlbumPath", mom.Path); err != nil { } else if err := a.UpdatePath(mom.Path); err != nil {
log.Errorf("moments: %s (update folder album)", err.Error()) log.Errorf("moments: %s (update folder album)", err.Error())
} else { } else {
log.Tracef("moments: %s already exists (%s)", txt.Quote(a.AlbumTitle), a.AlbumFilter) log.Tracef("moments: %s already exists (%s)", txt.Quote(a.AlbumTitle), a.AlbumFilter)

View file

@ -113,7 +113,7 @@ func AlbumSearch(f form.AlbumSearch) (results AlbumResults, err error) {
Select("albums.*, cp.photo_count, cl.link_count"). Select("albums.*, cp.photo_count, cl.link_count").
Joins("LEFT JOIN (SELECT album_uid, count(photo_uid) AS photo_count FROM photos_albums WHERE hidden = 0 GROUP BY album_uid) AS cp ON cp.album_uid = albums.album_uid"). Joins("LEFT JOIN (SELECT album_uid, count(photo_uid) AS photo_count FROM photos_albums WHERE hidden = 0 GROUP BY album_uid) AS cp ON cp.album_uid = albums.album_uid").
Joins("LEFT JOIN (SELECT share_uid, count(share_uid) AS link_count FROM links GROUP BY share_uid) AS cl ON cl.share_uid = albums.album_uid"). Joins("LEFT JOIN (SELECT share_uid, count(share_uid) AS link_count FROM links GROUP BY share_uid) AS cl ON cl.share_uid = albums.album_uid").
Where("albums.album_type <> 'folder' OR albums.album_path IS NULL OR albums.album_path IN (SELECT photos.photo_path FROM photos WHERE photos.deleted_at IS NULL)"). Where("albums.album_type <> 'folder' OR albums.album_path IN (SELECT photos.photo_path FROM photos WHERE photos.deleted_at IS NULL)").
Where("albums.deleted_at IS NULL") Where("albums.deleted_at IS NULL")
if f.ID != "" { if f.ID != "" {