Push updates: Add event types for photos & albums
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
11c3ed70e3
commit
ceb7d258fe
|
@ -262,21 +262,74 @@
|
|||
onCount() {
|
||||
this.dirty = true;
|
||||
},
|
||||
onPhotosUpdated(ev, data) {
|
||||
onPhotos(ev, data) {
|
||||
if (!data || !data.entities) {
|
||||
console.warn("onPhotosUpdated(): no entities found in event data");
|
||||
console.warn("onPhotos(): no entities found in event data");
|
||||
return
|
||||
}
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const values = data.entities[i];
|
||||
const model = this.results.find((m) => m.ID === values.ID);
|
||||
const type = ev.split('.')[1];
|
||||
|
||||
for (let key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
model[key] = values[key];
|
||||
console.log("onPhotos(): ", ev, type, data);
|
||||
|
||||
switch (type) {
|
||||
case 'updated':
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const values = data.entities[i];
|
||||
const model = this.results.find((m) => m.ID === values.ID);
|
||||
|
||||
for (let key in values) {
|
||||
if (values.hasOwnProperty(key)) {
|
||||
model[key] = values[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'restored':
|
||||
if(this.context === "archive") {
|
||||
this.dirty = false;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const uuid = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.PhotoUUID === uuid);
|
||||
if (index >= 0) {
|
||||
this.results.splice(index, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.dirty = true;
|
||||
}
|
||||
break;
|
||||
case 'archived':
|
||||
if(this.context === "photos") {
|
||||
this.dirty = false;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const uuid = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.PhotoUUID === uuid);
|
||||
if (index >= 0) {
|
||||
this.results.splice(index, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.dirty = true;
|
||||
}
|
||||
break;
|
||||
case 'created':
|
||||
if(this.order === "imported" && JSON.stringify(this.filter) === "{}") {
|
||||
this.dirty = false;
|
||||
|
||||
for (let i = 0; i < data.entities.length; i++) {
|
||||
const values = data.entities[i];
|
||||
const index = this.results.findIndex((m) => m.ID === values.ID);
|
||||
if(index === -1) {
|
||||
this.results.unshift(new Photo(values));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn("unexpected event type", ev);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -285,7 +338,7 @@
|
|||
|
||||
this.uploadSubId = Event.subscribe("import.completed", (ev, data) => this.onImportCompleted(ev, data));
|
||||
this.countSubId = Event.subscribe("count.photos", (ev, data) => this.onCount(ev, data));
|
||||
this.modelSubId = Event.subscribe("photos.updated", (ev, data) => this.onPhotosUpdated(ev, data));
|
||||
this.modelSubId = Event.subscribe("photos", (ev, data) => this.onPhotos(ev, data));
|
||||
},
|
||||
destroyed() {
|
||||
Event.unsubscribe(this.uploadSubId);
|
||||
|
|
|
@ -86,6 +86,7 @@ func CreateAlbum(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
q := query.New(conf.OriginalsPath(), conf.Db())
|
||||
m := entity.NewAlbum(f.AlbumName)
|
||||
m.AlbumFavorite = f.AlbumFavorite
|
||||
|
||||
|
@ -107,6 +108,8 @@ func CreateAlbum(router *gin.RouterGroup, conf *config.Config) {
|
|||
|
||||
event.Publish("config.updated", event.Data(conf.ClientConfig()))
|
||||
|
||||
PublishAlbumEvent(EntityCreated, m.AlbumUUID, c, q)
|
||||
|
||||
c.JSON(http.StatusOK, m)
|
||||
})
|
||||
}
|
||||
|
@ -142,6 +145,8 @@ func UpdateAlbum(router *gin.RouterGroup, conf *config.Config) {
|
|||
event.Publish("config.updated", event.Data(conf.ClientConfig()))
|
||||
event.Success(fmt.Sprintf("album \"%s\" saved", m.AlbumName))
|
||||
|
||||
PublishAlbumEvent(EntityUpdated, id, c, q)
|
||||
|
||||
c.JSON(http.StatusOK, m)
|
||||
})
|
||||
}
|
||||
|
@ -164,6 +169,8 @@ func DeleteAlbum(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
PublishAlbumEvent(EntityDeleted, id, c, q)
|
||||
|
||||
conf.Db().Delete(&m)
|
||||
|
||||
event.Publish("config.updated", event.Data(conf.ClientConfig()))
|
||||
|
@ -184,9 +191,10 @@ func LikeAlbum(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
id := c.Param("uuid")
|
||||
q := query.New(conf.OriginalsPath(), conf.Db())
|
||||
|
||||
album, err := q.FindAlbumByUUID(c.Param("uuid"))
|
||||
album, err := q.FindAlbumByUUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
|
@ -197,6 +205,7 @@ func LikeAlbum(router *gin.RouterGroup, conf *config.Config) {
|
|||
conf.Db().Save(&album)
|
||||
|
||||
event.Publish("config.updated", event.Data(conf.ClientConfig()))
|
||||
PublishAlbumEvent(EntityUpdated, id, c, q)
|
||||
|
||||
c.JSON(http.StatusOK, http.Response{})
|
||||
})
|
||||
|
@ -213,8 +222,9 @@ func DislikeAlbum(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
id := c.Param("uuid")
|
||||
q := query.New(conf.OriginalsPath(), conf.Db())
|
||||
album, err := q.FindAlbumByUUID(c.Param("uuid"))
|
||||
album, err := q.FindAlbumByUUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrAlbumNotFound)
|
||||
|
@ -225,6 +235,7 @@ func DislikeAlbum(router *gin.RouterGroup, conf *config.Config) {
|
|||
conf.Db().Save(&album)
|
||||
|
||||
event.Publish("config.updated", event.Data(conf.ClientConfig()))
|
||||
PublishAlbumEvent(EntityUpdated, id, c, q)
|
||||
|
||||
c.JSON(http.StatusOK, http.Response{})
|
||||
})
|
||||
|
|
|
@ -48,6 +48,10 @@ func BatchPhotosArchive(router *gin.RouterGroup, conf *config.Config) {
|
|||
|
||||
event.Publish("config.updated", event.Data(conf.ClientConfig()))
|
||||
|
||||
event.Publish("photos.archived", event.Data{
|
||||
"entities": f.Photos,
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("photos archived in %d s", elapsed)})
|
||||
})
|
||||
}
|
||||
|
@ -86,6 +90,10 @@ func BatchPhotosRestore(router *gin.RouterGroup, conf *config.Config) {
|
|||
|
||||
event.Publish("config.updated", event.Data(conf.ClientConfig()))
|
||||
|
||||
event.Publish("photos.restored", event.Data{
|
||||
"entities": f.Photos,
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("photos restored in %d s", elapsed)})
|
||||
})
|
||||
}
|
||||
|
@ -120,6 +128,10 @@ func BatchAlbumsDelete(router *gin.RouterGroup, conf *config.Config) {
|
|||
|
||||
event.Publish("config.updated", event.Data(conf.ClientConfig()))
|
||||
|
||||
event.Publish("albums.deleted", event.Data{
|
||||
"entities": f.Albums,
|
||||
})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("albums deleted")})
|
||||
})
|
||||
}
|
||||
|
|
63
internal/api/event.go
Normal file
63
internal/api/event.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
)
|
||||
|
||||
type EntityEvent string
|
||||
|
||||
const (
|
||||
EntityUpdated EntityEvent = "updated"
|
||||
EntityCreated EntityEvent = "created"
|
||||
EntityDeleted EntityEvent = "deleted"
|
||||
)
|
||||
|
||||
func PublishPhotoEvent(e EntityEvent, uuid string, c *gin.Context, q *query.Repo) {
|
||||
f := form.PhotoSearch{ID: uuid}
|
||||
result, err := q.Photos(f)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrUnexpectedError)
|
||||
return
|
||||
}
|
||||
|
||||
event.Publish("photos."+string(e), event.Data{
|
||||
"entities": result,
|
||||
})
|
||||
}
|
||||
|
||||
func PublishAlbumEvent(e EntityEvent, uuid string, c *gin.Context, q *query.Repo) {
|
||||
f := form.AlbumSearch{ID: uuid}
|
||||
result, err := q.Albums(f)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrUnexpectedError)
|
||||
return
|
||||
}
|
||||
|
||||
event.Publish("albums."+string(e), event.Data{
|
||||
"entities": result,
|
||||
})
|
||||
}
|
||||
|
||||
func PublishLabelEvent(e EntityEvent, uuid string, c *gin.Context, q *query.Repo) {
|
||||
f := form.LabelSearch{ID: uuid}
|
||||
result, err := q.Labels(f)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrUnexpectedError)
|
||||
return
|
||||
}
|
||||
|
||||
event.Publish("labels."+string(e), event.Data{
|
||||
"entities": result,
|
||||
})
|
||||
}
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
@ -15,21 +14,6 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func PublishPhotoUpdate(uuid string, c *gin.Context, q *query.Repo) {
|
||||
f := form.PhotoSearch{ID: uuid}
|
||||
result, err := q.Photos(f)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrUnexpectedError)
|
||||
return
|
||||
}
|
||||
|
||||
event.Publish("photos.updated", event.Data{
|
||||
"entities": result,
|
||||
})
|
||||
}
|
||||
|
||||
// GET /api/v1/photos/:uuid
|
||||
//
|
||||
// Parameters:
|
||||
|
@ -61,10 +45,10 @@ func UpdatePhoto(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
uuid := c.Param("uuid")
|
||||
id := c.Param("uuid")
|
||||
q := query.New(conf.OriginalsPath(), conf.Db())
|
||||
|
||||
m, err := q.FindPhotoByUUID(uuid)
|
||||
m, err := q.FindPhotoByUUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
|
@ -78,11 +62,11 @@ func UpdatePhoto(router *gin.RouterGroup, conf *config.Config) {
|
|||
|
||||
conf.Db().Save(&m)
|
||||
|
||||
PublishPhotoUpdate(uuid, c, q)
|
||||
PublishPhotoEvent(EntityUpdated, id, c, q)
|
||||
|
||||
event.Success("photo saved")
|
||||
|
||||
p, err := q.PreloadPhotoByUUID(uuid)
|
||||
p, err := q.PreloadPhotoByUUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
|
@ -138,9 +122,9 @@ func LikePhoto(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
uuid := c.Param("uuid")
|
||||
id := c.Param("uuid")
|
||||
q := query.New(conf.OriginalsPath(), conf.Db())
|
||||
m, err := q.FindPhotoByUUID(uuid)
|
||||
m, err := q.FindPhotoByUUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
|
@ -154,7 +138,7 @@ func LikePhoto(router *gin.RouterGroup, conf *config.Config) {
|
|||
"count": 1,
|
||||
})
|
||||
|
||||
PublishPhotoUpdate(uuid, c, q)
|
||||
PublishPhotoEvent(EntityUpdated, id, c, q)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"photo": m})
|
||||
})
|
||||
|
@ -171,9 +155,9 @@ func DislikePhoto(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
uuid := c.Param("uuid")
|
||||
id := c.Param("uuid")
|
||||
q := query.New(conf.OriginalsPath(), conf.Db())
|
||||
m, err := q.FindPhotoByUUID(uuid)
|
||||
m, err := q.FindPhotoByUUID(id)
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusNotFound, ErrPhotoNotFound)
|
||||
|
@ -187,7 +171,7 @@ func DislikePhoto(router *gin.RouterGroup, conf *config.Config) {
|
|||
"count": -1,
|
||||
})
|
||||
|
||||
PublishPhotoUpdate(uuid, c, q)
|
||||
PublishPhotoEvent(EntityUpdated, id, c, q)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"photo": m})
|
||||
})
|
||||
|
|
|
@ -73,7 +73,7 @@ func wsReader(ws *websocket.Conn, connId string, conf *config.Config) {
|
|||
|
||||
func wsWriter(ws *websocket.Conn, connId string) {
|
||||
pingTicker := time.NewTicker(15 * time.Second)
|
||||
s := event.Subscribe("log.*", "notify.*", "index.*", "upload.*", "import.*", "config.*", "count.*", "photos.*", "albums.*")
|
||||
s := event.Subscribe("log.*", "notify.*", "index.*", "upload.*", "import.*", "config.*", "count.*", "photos.*", "albums.*", "labels.*")
|
||||
|
||||
defer func() {
|
||||
pingTicker.Stop()
|
||||
|
|
|
@ -3,6 +3,7 @@ package form
|
|||
// AlbumSearch represents search form fields for "/api/v1/albums".
|
||||
type AlbumSearch struct {
|
||||
Query string `form:"q"`
|
||||
ID string `form:"id"`
|
||||
Slug string `form:"slug"`
|
||||
Name string `form:"name"`
|
||||
Favorites bool `form:"favorites"`
|
||||
|
|
|
@ -3,6 +3,7 @@ package form
|
|||
// PhotoSearch represents search form fields for "/api/v1/labels".
|
||||
type LabelSearch struct {
|
||||
Query string `form:"q"`
|
||||
ID string `form:"id"`
|
||||
Slug string `form:"slug"`
|
||||
Name string `form:"name"`
|
||||
All bool `form:"all"`
|
||||
|
|
|
@ -54,18 +54,26 @@ func (s *Repo) Albums(f form.AlbumSearch) (results []AlbumResult, err error) {
|
|||
return results, err
|
||||
}
|
||||
|
||||
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("search: %+v", f)))
|
||||
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("albums: %+v", f)))
|
||||
|
||||
q := s.db.NewScope(nil).DB()
|
||||
|
||||
// q.LogMode(true)
|
||||
|
||||
q = q.Table("albums").
|
||||
Select(`albums.*, COUNT(photos_albums.album_uuid) AS album_count`).
|
||||
Joins("LEFT JOIN photos_albums ON photos_albums.album_uuid = albums.album_uuid").
|
||||
Where("albums.deleted_at IS NULL").
|
||||
Group("albums.id")
|
||||
|
||||
if f.ID != "" {
|
||||
q = q.Where("albums.album_uuid = ?", f.ID)
|
||||
|
||||
if result := q.Scan(&results); result.Error != nil {
|
||||
return results, result.Error
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
if f.Query != "" {
|
||||
likeString := "%" + strings.ToLower(f.Query) + "%"
|
||||
q = q.Where("LOWER(albums.album_name) LIKE ?", likeString)
|
||||
|
|
|
@ -91,7 +91,7 @@ func (s *Repo) Labels(f form.LabelSearch) (results []LabelResult, err error) {
|
|||
return results, err
|
||||
}
|
||||
|
||||
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("search: %+v", f)))
|
||||
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("labels: %+v", f)))
|
||||
|
||||
q := s.db.NewScope(nil).DB()
|
||||
|
||||
|
@ -102,6 +102,16 @@ func (s *Repo) Labels(f form.LabelSearch) (results []LabelResult, err error) {
|
|||
Where("labels.deleted_at IS NULL").
|
||||
Group("labels.id")
|
||||
|
||||
if f.ID != "" {
|
||||
q = q.Where("labels.label_uuid = ?", f.ID)
|
||||
|
||||
if result := q.Scan(&results); result.Error != nil {
|
||||
return results, result.Error
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
if f.Query != "" {
|
||||
var labelIds []uint
|
||||
var categories []entity.Category
|
||||
|
|
|
@ -104,7 +104,7 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
|||
return results, err
|
||||
}
|
||||
|
||||
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("search: %+v", f)))
|
||||
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("photos: %+v", f)))
|
||||
|
||||
q := s.db.NewScope(nil).DB()
|
||||
|
||||
|
|
Loading…
Reference in a new issue