WIP: Work on new timeline

This commit is contained in:
viktorstrate
2021-09-18 20:17:24 +02:00
parent bb613345ee
commit 56dfedd522
41 changed files with 7931 additions and 952 deletions

View File

@@ -38,6 +38,8 @@ models:
resolver: true
type:
resolver: true
album:
resolver: true
MediaURL:
model: github.com/photoview/photoview/api/graphql/models.MediaURL
MediaEXIF:

View File

@@ -98,6 +98,7 @@ type ComplexityRoot struct {
Media struct {
Album func(childComplexity int) int
Date func(childComplexity int) int
Downloads func(childComplexity int) int
Exif func(childComplexity int) int
Faces func(childComplexity int) int
@@ -186,7 +187,7 @@ type ComplexityRoot struct {
MyFaceGroups func(childComplexity int, paginate *models.Pagination) int
MyMedia func(childComplexity int, order *models.Ordering, paginate *models.Pagination) int
MyMediaGeoJSON func(childComplexity int) int
MyTimeline func(childComplexity int, paginate *models.Pagination, onlyFavorites *bool) int
MyTimeline func(childComplexity int, paginate *models.Pagination, onlyFavorites *bool, fromDate *time.Time) int
MyUser func(childComplexity int) int
MyUserPreferences func(childComplexity int) int
Search func(childComplexity int, query string, limitMedia *int, limitAlbums *int) int
@@ -285,11 +286,12 @@ type MediaResolver interface {
Thumbnail(ctx context.Context, obj *models.Media) (*models.MediaURL, error)
HighRes(ctx context.Context, obj *models.Media) (*models.MediaURL, error)
VideoWeb(ctx context.Context, obj *models.Media) (*models.MediaURL, error)
Album(ctx context.Context, obj *models.Media) (*models.Album, error)
Exif(ctx context.Context, obj *models.Media) (*models.MediaEXIF, error)
Favorite(ctx context.Context, obj *models.Media) (bool, error)
Type(ctx context.Context, obj *models.Media) (models.MediaType, error)
Shares(ctx context.Context, obj *models.Media) ([]*models.ShareToken, error)
Downloads(ctx context.Context, obj *models.Media) ([]*models.MediaDownload, error)
Faces(ctx context.Context, obj *models.Media) ([]*models.ImageFace, error)
@@ -328,7 +330,7 @@ type QueryResolver interface {
MyMedia(ctx context.Context, order *models.Ordering, paginate *models.Pagination) ([]*models.Media, error)
Media(ctx context.Context, id int, tokenCredentials *models.ShareTokenCredentials) (*models.Media, error)
MediaList(ctx context.Context, ids []int) ([]*models.Media, error)
MyTimeline(ctx context.Context, paginate *models.Pagination, onlyFavorites *bool) ([]*models.TimelineGroup, error)
MyTimeline(ctx context.Context, paginate *models.Pagination, onlyFavorites *bool, fromDate *time.Time) ([]*models.Media, error)
MyMediaGeoJSON(ctx context.Context) (interface{}, error)
MapboxToken(ctx context.Context) (*string, error)
ShareToken(ctx context.Context, credentials models.ShareTokenCredentials) (*models.ShareToken, error)
@@ -563,6 +565,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Media.Album(childComplexity), true
case "Media.date":
if e.complexity.Media.Date == nil {
break
}
return e.complexity.Media.Date(childComplexity), true
case "Media.downloads":
if e.complexity.Media.Downloads == nil {
break
@@ -1198,7 +1207,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false
}
return e.complexity.Query.MyTimeline(childComplexity, args["paginate"].(*models.Pagination), args["onlyFavorites"].(*bool)), true
return e.complexity.Query.MyTimeline(childComplexity, args["paginate"].(*models.Pagination), args["onlyFavorites"].(*bool), args["fromDate"].(*time.Time)), true
case "Query.myUser":
if e.complexity.Query.MyUser == nil {
@@ -1695,12 +1704,20 @@ type Query {
"Get a list of media by their ids, user must own the media or be admin"
mediaList(ids: [ID!]!): [Media!]!
myTimeline(paginate: Pagination, onlyFavorites: Boolean): [TimelineGroup!]! @isAuthorized
"""
Get a list of media, ordered first by day, then by album if multiple media was found for the same day.
"""
myTimeline(
paginate: Pagination,
onlyFavorites: Boolean,
"Only fetch media that is older than this date"
fromDate: Time
): [Media!]! @isAuthorized
"Get media owned by the logged in user, returned in GeoJson format"
myMediaGeoJson: Any! @isAuthorized
"Get the mapbox api token, returns null if mapbox is not enabled"
mapboxToken: String
mapboxToken: String @isAuthorized
shareToken(credentials: ShareTokenCredentials!): ShareToken!
shareTokenValidatePassword(credentials: ShareTokenCredentials!): Boolean!
@@ -1945,6 +1962,8 @@ type Media {
videoMetadata: VideoMetadata
favorite: Boolean!
type: MediaType!
"The date the image was shot or the date it was imported as a fallback"
date: Time!
shares: [ShareToken!]!
downloads: [MediaDownload!]!
@@ -2778,6 +2797,15 @@ func (ec *executionContext) field_Query_myTimeline_args(ctx context.Context, raw
}
}
args["onlyFavorites"] = arg1
var arg2 *time.Time
if tmp, ok := rawArgs["fromDate"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("fromDate"))
arg2, err = ec.unmarshalOTime2ᚖtimeᚐTime(ctx, tmp)
if err != nil {
return nil, err
}
}
args["fromDate"] = arg2
return args, nil
}
@@ -4002,14 +4030,14 @@ func (ec *executionContext) _Media_album(ctx context.Context, field graphql.Coll
Object: "Media",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Album, nil
return ec.resolvers.Media().Album(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
@@ -4021,9 +4049,9 @@ func (ec *executionContext) _Media_album(ctx context.Context, field graphql.Coll
}
return graphql.Null
}
res := resTmp.(models.Album)
res := resTmp.(*models.Album)
fc.Result = res
return ec.marshalNAlbum2githubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx, field.Selections, res)
return ec.marshalNAlbum2githubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbum(ctx, field.Selections, res)
}
func (ec *executionContext) _Media_exif(ctx context.Context, field graphql.CollectedField, obj *models.Media) (ret graphql.Marshaler) {
@@ -4160,6 +4188,41 @@ func (ec *executionContext) _Media_type(ctx context.Context, field graphql.Colle
return ec.marshalNMediaType2githubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMediaType(ctx, field.Selections, res)
}
func (ec *executionContext) _Media_date(ctx context.Context, field graphql.CollectedField, obj *models.Media) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Media",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Date(), nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(time.Time)
fc.Result = res
return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res)
}
func (ec *executionContext) _Media_shares(ctx context.Context, field graphql.CollectedField, obj *models.Media) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@@ -6923,7 +6986,7 @@ func (ec *executionContext) _Query_myTimeline(ctx context.Context, field graphql
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().MyTimeline(rctx, args["paginate"].(*models.Pagination), args["onlyFavorites"].(*bool))
return ec.resolvers.Query().MyTimeline(rctx, args["paginate"].(*models.Pagination), args["onlyFavorites"].(*bool), args["fromDate"].(*time.Time))
}
directive1 := func(ctx context.Context) (interface{}, error) {
if ec.directives.IsAuthorized == nil {
@@ -6939,10 +7002,10 @@ func (ec *executionContext) _Query_myTimeline(ctx context.Context, field graphql
if tmp == nil {
return nil, nil
}
if data, ok := tmp.([]*models.TimelineGroup); ok {
if data, ok := tmp.([]*models.Media); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be []*github.com/photoview/photoview/api/graphql/models.TimelineGroup`, tmp)
return nil, fmt.Errorf(`unexpected type %T from directive, should be []*github.com/photoview/photoview/api/graphql/models.Media`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@@ -6954,9 +7017,9 @@ func (ec *executionContext) _Query_myTimeline(ctx context.Context, field graphql
}
return graphql.Null
}
res := resTmp.([]*models.TimelineGroup)
res := resTmp.([]*models.Media)
fc.Result = res
return ec.marshalNTimelineGroup2ᚕᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐTimelineGroup(ctx, field.Selections, res)
return ec.marshalNMedia2ᚕᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐMedia(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_myMediaGeoJson(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
@@ -7031,8 +7094,28 @@ func (ec *executionContext) _Query_mapboxToken(ctx context.Context, field graphq
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().MapboxToken(rctx)
directive0 := func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().MapboxToken(rctx)
}
directive1 := func(ctx context.Context) (interface{}, error) {
if ec.directives.IsAuthorized == nil {
return nil, errors.New("directive isAuthorized is not implemented")
}
return ec.directives.IsAuthorized(ctx, nil, directive0)
}
tmp, err := directive1(rctx)
if err != nil {
return nil, graphql.ErrorOnPath(ctx, err)
}
if tmp == nil {
return nil, nil
}
if data, ok := tmp.(*string); ok {
return data, nil
}
return nil, fmt.Errorf(`unexpected type %T from directive, should be *string`, tmp)
})
if err != nil {
ec.Error(ctx, err)
@@ -10367,10 +10450,19 @@ func (ec *executionContext) _Media(ctx context.Context, sel ast.SelectionSet, ob
return res
})
case "album":
out.Values[i] = ec._Media_album(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Media_album(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "exif":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -10412,6 +10504,11 @@ func (ec *executionContext) _Media(ctx context.Context, sel ast.SelectionSet, ob
}
return res
})
case "date":
out.Values[i] = ec._Media_date(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
case "shares":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
@@ -12247,53 +12344,6 @@ func (ec *executionContext) marshalNTime2timeᚐTime(ctx context.Context, sel as
return res
}
func (ec *executionContext) marshalNTimelineGroup2ᚕᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐTimelineGroupᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.TimelineGroup) graphql.Marshaler {
ret := make(graphql.Array, len(v))
var wg sync.WaitGroup
isLen1 := len(v) == 1
if !isLen1 {
wg.Add(len(v))
}
for i := range v {
i := i
fc := &graphql.FieldContext{
Index: &i,
Result: &v[i],
}
ctx := graphql.WithFieldContext(ctx, fc)
f := func(i int) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = nil
}
}()
if !isLen1 {
defer wg.Done()
}
ret[i] = ec.marshalNTimelineGroup2ᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐTimelineGroup(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
return ret
}
func (ec *executionContext) marshalNTimelineGroup2ᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐTimelineGroup(ctx context.Context, sel ast.SelectionSet, v *models.TimelineGroup) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._TimelineGroup(ctx, sel, v)
}
func (ec *executionContext) marshalNUser2githubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐUser(ctx context.Context, sel ast.SelectionSet, v models.User) graphql.Marshaler {
return ec._User(ctx, sel, &v)
}

View File

@@ -45,6 +45,10 @@ func (m *Media) BeforeSave(tx *gorm.DB) error {
return nil
}
func (m *Media) Date() time.Time {
return m.DateShot
}
type MediaType string
const (

View File

@@ -115,6 +115,15 @@ func (r *mediaResolver) Type(ctx context.Context, media *models.Media) (models.M
return formattedType, nil
}
func (r *mediaResolver) Album(ctx context.Context, obj *models.Media) (*models.Album, error) {
var album models.Album
err := r.Database.Find(&album, obj.AlbumID).Error
if err != nil {
return nil, err
}
return &album, nil
}
func (r *mediaResolver) Shares(ctx context.Context, media *models.Media) ([]*models.ShareToken, error) {
var shareTokens []*models.ShareToken
if err := r.Database.Where("media_id = ?", media.ID).Find(&shareTokens).Error; err != nil {

View File

@@ -2,135 +2,160 @@ package resolvers
import (
"context"
"fmt"
"time"
"github.com/photoview/photoview/api/database"
"github.com/photoview/photoview/api/graphql/auth"
"github.com/photoview/photoview/api/graphql/models"
"gorm.io/gorm"
)
func (r *queryResolver) MyTimeline(ctx context.Context, paginate *models.Pagination, onlyFavorites *bool) ([]*models.TimelineGroup, error) {
func (r *queryResolver) MyTimeline(ctx context.Context, paginate *models.Pagination, onlyFavorites *bool, fromDate *time.Time) ([]*models.Media, error) {
user := auth.UserFromContext(ctx)
if user == nil {
return nil, auth.ErrUnauthorized
}
var timelineGroups []*models.TimelineGroup
query := r.Database.
Joins("JOIN albums ON media.album_id = albums.id").
Where("albums.id IN (?)", r.Database.Table("user_albums").Select("user_albums.album_id").Where("user_id = ?", user.ID)).
Order("YEAR(media.date_shot) DESC").
Order("MONTH(media.date_shot) DESC").
Order("DAY(media.date_shot) DESC").
Order("albums.title ASC")
transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
// album_id, year, month, day
daysQuery := tx.Select(
"albums.id AS album_id",
fmt.Sprintf("%s AS year", database.DateExtract(tx, database.DateCompYear, "media.date_shot")),
fmt.Sprintf("%s AS month", database.DateExtract(tx, database.DateCompMonth, "media.date_shot")),
fmt.Sprintf("%s AS day", database.DateExtract(tx, database.DateCompDay, "media.date_shot")),
).
Table("media").
Joins("JOIN albums ON media.album_id = albums.id").
Where("albums.id IN (?)", tx.Table("user_albums").Select("user_albums.album_id").Where("user_id = ?", user.ID))
if onlyFavorites != nil && *onlyFavorites == true {
daysQuery.Where("media.id IN (?)", tx.Table("user_media_data").Select("user_media_data.media_id").Where("user_media_data.user_id = ?", user.ID).Where("user_media_data.favorite = 1"))
}
if paginate != nil {
if paginate.Limit != nil {
daysQuery.Limit(*paginate.Limit)
}
if paginate.Offset != nil {
daysQuery.Offset(*paginate.Offset)
}
}
rows, err := daysQuery.Group("albums.id").Group(
fmt.Sprintf("%s, %s, %s",
database.DateExtract(tx, database.DateCompYear, "media.date_shot"),
database.DateExtract(tx, database.DateCompMonth, "media.date_shot"),
database.DateExtract(tx, database.DateCompDay, "media.date_shot")),
).
Order(
fmt.Sprintf("%s DESC, %s DESC, %s DESC",
database.DateExtract(tx, database.DateCompYear, "media.date_shot"),
database.DateExtract(tx, database.DateCompMonth, "media.date_shot"),
database.DateExtract(tx, database.DateCompDay, "media.date_shot")),
).Rows()
defer rows.Close()
if err != nil {
return err
}
type group struct {
albumID int
year int
month int
day int
}
dbGroups := make([]group, 0)
for rows.Next() {
var g group
rows.Scan(&g.albumID, &g.year, &g.month, &g.day)
dbGroups = append(dbGroups, g)
}
timelineGroups = make([]*models.TimelineGroup, len(dbGroups))
for i, group := range dbGroups {
// Fill album
var groupAlbum models.Album
if err := tx.First(&groupAlbum, group.albumID).Error; err != nil {
return err
}
// Fill media
var groupMedia []*models.Media
mediaQuery := tx.Model(&models.Media{}).
Where("album_id = ?", group.albumID).
Where(fmt.Sprintf("%s = ?", database.DateExtract(tx, database.DateCompYear, "media.date_shot")), group.year).
Where(fmt.Sprintf("%s = ?", database.DateExtract(tx, database.DateCompMonth, "media.date_shot")), group.month).
Where(fmt.Sprintf("%s = ?", database.DateExtract(tx, database.DateCompDay, "media.date_shot")), group.day).
Order("date_shot DESC")
if onlyFavorites != nil && *onlyFavorites == true {
mediaQuery.Where("media.id IN (?)", tx.Table("user_media_data").Select("user_media_data.media_id").Where("user_media_data.user_id = ?", user.ID).Where("user_media_data.favorite = 1"))
}
if err := mediaQuery.Limit(5).Find(&groupMedia).Error; err != nil {
return err
}
// Get total media count
var totalMedia int64
if err := mediaQuery.Count(&totalMedia).Error; err != nil {
return err
}
var date time.Time = groupMedia[0].DateShot
date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
timelineGroup := models.TimelineGroup{
Album: &groupAlbum,
Media: groupMedia,
MediaTotal: int(totalMedia),
Date: date,
}
timelineGroups[i] = &timelineGroup
}
return nil
})
if transactionError != nil {
return nil, transactionError
if fromDate != nil {
query = query.Where("media.date_shot < ?", fromDate)
}
return timelineGroups, nil
query = models.FormatSQL(query, nil, paginate)
var media []*models.Media
if err := query.Find(&media).Error; err != nil {
return nil, err
}
return media, nil
}
// func (r *queryResolver) MyTimeline(ctx context.Context, paginate *models.Pagination, onlyFavorites *bool) ([]*models.TimelineGroup, error) {
// user := auth.UserFromContext(ctx)
// if user == nil {
// return nil, auth.ErrUnauthorized
// }
// var timelineGroups []*models.TimelineGroup
// transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
// // album_id, year, month, day
// daysQuery := tx.Select(
// "albums.id AS album_id",
// fmt.Sprintf("%s AS year", database.DateExtract(tx, database.DateCompYear, "media.date_shot")),
// fmt.Sprintf("%s AS month", database.DateExtract(tx, database.DateCompMonth, "media.date_shot")),
// fmt.Sprintf("%s AS day", database.DateExtract(tx, database.DateCompDay, "media.date_shot")),
// ).
// Table("media").
// Joins("JOIN albums ON media.album_id = albums.id").
// Where("albums.id IN (?)", tx.Table("user_albums").Select("user_albums.album_id").Where("user_id = ?", user.ID))
// if onlyFavorites != nil && *onlyFavorites == true {
// daysQuery.Where("media.id IN (?)", tx.Table("user_media_data").Select("user_media_data.media_id").Where("user_media_data.user_id = ?", user.ID).Where("user_media_data.favorite = 1"))
// }
// if paginate != nil {
// if paginate.Limit != nil {
// daysQuery.Limit(*paginate.Limit)
// }
// if paginate.Offset != nil {
// daysQuery.Offset(*paginate.Offset)
// }
// }
// rows, err := daysQuery.Group("albums.id").Group(
// fmt.Sprintf("%s, %s, %s",
// database.DateExtract(tx, database.DateCompYear, "media.date_shot"),
// database.DateExtract(tx, database.DateCompMonth, "media.date_shot"),
// database.DateExtract(tx, database.DateCompDay, "media.date_shot")),
// ).
// Order(
// fmt.Sprintf("%s DESC, %s DESC, %s DESC",
// database.DateExtract(tx, database.DateCompYear, "media.date_shot"),
// database.DateExtract(tx, database.DateCompMonth, "media.date_shot"),
// database.DateExtract(tx, database.DateCompDay, "media.date_shot")),
// ).Rows()
// defer rows.Close()
// if err != nil {
// return err
// }
// type group struct {
// albumID int
// year int
// month int
// day int
// }
// dbGroups := make([]group, 0)
// for rows.Next() {
// var g group
// rows.Scan(&g.albumID, &g.year, &g.month, &g.day)
// dbGroups = append(dbGroups, g)
// }
// timelineGroups = make([]*models.TimelineGroup, len(dbGroups))
// for i, group := range dbGroups {
// // Fill album
// var groupAlbum models.Album
// if err := tx.First(&groupAlbum, group.albumID).Error; err != nil {
// return err
// }
// // Fill media
// var groupMedia []*models.Media
// mediaQuery := tx.Model(&models.Media{}).
// Where("album_id = ?", group.albumID).
// Where(fmt.Sprintf("%s = ?", database.DateExtract(tx, database.DateCompYear, "media.date_shot")), group.year).
// Where(fmt.Sprintf("%s = ?", database.DateExtract(tx, database.DateCompMonth, "media.date_shot")), group.month).
// Where(fmt.Sprintf("%s = ?", database.DateExtract(tx, database.DateCompDay, "media.date_shot")), group.day).
// Order("date_shot DESC")
// if onlyFavorites != nil && *onlyFavorites == true {
// mediaQuery.Where("media.id IN (?)", tx.Table("user_media_data").Select("user_media_data.media_id").Where("user_media_data.user_id = ?", user.ID).Where("user_media_data.favorite = 1"))
// }
// if err := mediaQuery.Limit(5).Find(&groupMedia).Error; err != nil {
// return err
// }
// // Get total media count
// var totalMedia int64
// if err := mediaQuery.Count(&totalMedia).Error; err != nil {
// return err
// }
// var date time.Time = groupMedia[0].DateShot
// date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location())
// timelineGroup := models.TimelineGroup{
// Album: &groupAlbum,
// Media: groupMedia,
// MediaTotal: int(totalMedia),
// Date: date,
// }
// timelineGroups[i] = &timelineGroup
// }
// return nil
// })
// if transactionError != nil {
// return nil, transactionError
// }
// return timelineGroups, nil
// }

View File

@@ -63,7 +63,15 @@ type Query {
"Get a list of media by their ids, user must own the media or be admin"
mediaList(ids: [ID!]!): [Media!]!
myTimeline(paginate: Pagination, onlyFavorites: Boolean): [TimelineGroup!]! @isAuthorized
"""
Get a list of media, ordered first by day, then by album if multiple media was found for the same day.
"""
myTimeline(
paginate: Pagination,
onlyFavorites: Boolean,
"Only fetch media that is older than this date"
fromDate: Time
): [Media!]! @isAuthorized
"Get media owned by the logged in user, returned in GeoJson format"
myMediaGeoJson: Any! @isAuthorized
@@ -313,6 +321,8 @@ type Media {
videoMetadata: VideoMetadata
favorite: Boolean!
type: MediaType!
"The date the image was shot or the date it was imported as a fallback"
date: Time!
shares: [ShareToken!]!
downloads: [MediaDownload!]!

6896
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -63,7 +63,7 @@
"lint:types": "tsc --noemit",
"jest": "craco test --setupFilesAfterEnv ./testing/setupTests.ts",
"jest:ci": "CI=true craco test --setupFilesAfterEnv ./testing/setupTests.ts --verbose --ci --coverage",
"genSchemaTypes": "npx apollo client:codegen --target=typescript --globalTypesFile=src/__generated__/globalTypes.ts",
"genSchemaTypes": "apollo client:codegen --target=typescript --globalTypesFile=src/__generated__/globalTypes.ts",
"extractTranslations": "i18next -c i18next-parser.config.js",
"prepare": "(cd .. && npx husky install)"
},
@@ -74,7 +74,9 @@
"husky": "^6.0.0",
"i18next-parser": "^4.2.0",
"lint-staged": "^11.0.1",
"tsc-files": "^1.1.2"
"tsc-files": "^1.1.2",
"apollo": "2.33.4",
"apollo-language-server": "1.26.3"
},
"prettier": {
"trailingComma": "es5",

View File

@@ -3,101 +3,101 @@
// @generated
// This file was automatically generated and should not be edited.
import { OrderDirection, MediaType } from './../../../__generated__/globalTypes'
import { OrderDirection, MediaType } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: albumQuery
// ====================================================
export interface albumQuery_album_subAlbums_thumbnail_thumbnail {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
}
export interface albumQuery_album_subAlbums_thumbnail {
__typename: 'Media'
__typename: "Media";
/**
* URL to display the media in a smaller resolution
*/
thumbnail: albumQuery_album_subAlbums_thumbnail_thumbnail | null
thumbnail: albumQuery_album_subAlbums_thumbnail_thumbnail | null;
}
export interface albumQuery_album_subAlbums {
__typename: 'Album'
id: string
title: string
__typename: "Album";
id: string;
title: string;
/**
* An image in this album used for previewing this album
*/
thumbnail: albumQuery_album_subAlbums_thumbnail | null
thumbnail: albumQuery_album_subAlbums_thumbnail | null;
}
export interface albumQuery_album_media_thumbnail {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface albumQuery_album_media_highRes {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
}
export interface albumQuery_album_media_videoWeb {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
}
export interface albumQuery_album_media {
__typename: 'Media'
id: string
type: MediaType
__typename: "Media";
id: string;
type: MediaType;
/**
* URL to display the media in a smaller resolution
*/
thumbnail: albumQuery_album_media_thumbnail | null
thumbnail: albumQuery_album_media_thumbnail | null;
/**
* URL to display the photo in full resolution, will be null for videos
*/
highRes: albumQuery_album_media_highRes | null
highRes: albumQuery_album_media_highRes | null;
/**
* URL to get the video in a web format that can be played in the browser, will be null for photos
*/
videoWeb: albumQuery_album_media_videoWeb | null
favorite: boolean
videoWeb: albumQuery_album_media_videoWeb | null;
favorite: boolean;
}
export interface albumQuery_album {
__typename: 'Album'
id: string
title: string
__typename: "Album";
id: string;
title: string;
/**
* The albums contained in this album
*/
subAlbums: albumQuery_album_subAlbums[]
subAlbums: albumQuery_album_subAlbums[];
/**
* The media inside this album
*/
media: albumQuery_album_media[]
media: albumQuery_album_media[];
}
export interface albumQuery {
@@ -105,14 +105,14 @@ export interface albumQuery {
* Get album by id, user must own the album or be admin
* If valid tokenCredentials are provided, the album may be retrived without further authentication
*/
album: albumQuery_album
album: albumQuery_album;
}
export interface albumQueryVariables {
id: string
onlyFavorites?: boolean | null
mediaOrderBy?: string | null
mediaOrderDirection?: OrderDirection | null
limit?: number | null
offset?: number | null
id: string;
onlyFavorites?: boolean | null;
mediaOrderBy?: string | null;
mediaOrderDirection?: OrderDirection | null;
limit?: number | null;
offset?: number | null;
}

View File

@@ -9,6 +9,9 @@
export interface CheckInitialSetup_siteInfo {
__typename: "SiteInfo";
/**
* Whether or not the initial setup wizard should be shown
*/
initialSetup: boolean;
}

View File

@@ -3,80 +3,80 @@
// @generated
// This file was automatically generated and should not be edited.
import { MediaType } from './../../../../__generated__/globalTypes'
import { MediaType } from "./../../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: singleFaceGroup
// ====================================================
export interface singleFaceGroup_faceGroup_imageFaces_rectangle {
__typename: 'FaceRectangle'
minX: number
maxX: number
minY: number
maxY: number
__typename: "FaceRectangle";
minX: number;
maxX: number;
minY: number;
maxY: number;
}
export interface singleFaceGroup_faceGroup_imageFaces_media_thumbnail {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface singleFaceGroup_faceGroup_imageFaces_media_highRes {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
}
export interface singleFaceGroup_faceGroup_imageFaces_media {
__typename: 'Media'
id: string
type: MediaType
title: string
__typename: "Media";
id: string;
type: MediaType;
title: string;
/**
* URL to display the media in a smaller resolution
*/
thumbnail: singleFaceGroup_faceGroup_imageFaces_media_thumbnail | null
thumbnail: singleFaceGroup_faceGroup_imageFaces_media_thumbnail | null;
/**
* URL to display the photo in full resolution, will be null for videos
*/
highRes: singleFaceGroup_faceGroup_imageFaces_media_highRes | null
favorite: boolean
highRes: singleFaceGroup_faceGroup_imageFaces_media_highRes | null;
favorite: boolean;
}
export interface singleFaceGroup_faceGroup_imageFaces {
__typename: 'ImageFace'
id: string
rectangle: singleFaceGroup_faceGroup_imageFaces_rectangle
media: singleFaceGroup_faceGroup_imageFaces_media
__typename: "ImageFace";
id: string;
rectangle: singleFaceGroup_faceGroup_imageFaces_rectangle;
media: singleFaceGroup_faceGroup_imageFaces_media;
}
export interface singleFaceGroup_faceGroup {
__typename: 'FaceGroup'
id: string
label: string | null
imageFaces: singleFaceGroup_faceGroup_imageFaces[]
__typename: "FaceGroup";
id: string;
label: string | null;
imageFaces: singleFaceGroup_faceGroup_imageFaces[];
}
export interface singleFaceGroup {
faceGroup: singleFaceGroup_faceGroup
faceGroup: singleFaceGroup_faceGroup;
}
export interface singleFaceGroupVariables {
id: string
limit: number
offset: number
id: string;
limit: number;
offset: number;
}

View File

@@ -8,59 +8,59 @@
// ====================================================
export interface myFaces_myFaceGroups_imageFaces_rectangle {
__typename: 'FaceRectangle'
minX: number
maxX: number
minY: number
maxY: number
__typename: "FaceRectangle";
minX: number;
maxX: number;
minY: number;
maxY: number;
}
export interface myFaces_myFaceGroups_imageFaces_media_thumbnail {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface myFaces_myFaceGroups_imageFaces_media {
__typename: 'Media'
id: string
title: string
__typename: "Media";
id: string;
title: string;
/**
* URL to display the media in a smaller resolution
*/
thumbnail: myFaces_myFaceGroups_imageFaces_media_thumbnail | null
thumbnail: myFaces_myFaceGroups_imageFaces_media_thumbnail | null;
}
export interface myFaces_myFaceGroups_imageFaces {
__typename: 'ImageFace'
id: string
rectangle: myFaces_myFaceGroups_imageFaces_rectangle
media: myFaces_myFaceGroups_imageFaces_media
__typename: "ImageFace";
id: string;
rectangle: myFaces_myFaceGroups_imageFaces_rectangle;
media: myFaces_myFaceGroups_imageFaces_media;
}
export interface myFaces_myFaceGroups {
__typename: 'FaceGroup'
id: string
label: string | null
imageFaceCount: number
imageFaces: myFaces_myFaceGroups_imageFaces[]
__typename: "FaceGroup";
id: string;
label: string | null;
imageFaceCount: number;
imageFaces: myFaces_myFaceGroups_imageFaces[];
}
export interface myFaces {
myFaceGroups: myFaces_myFaceGroups[]
myFaceGroups: myFaces_myFaceGroups[];
}
export interface myFacesVariables {
limit?: number | null
offset?: number | null
limit?: number | null;
offset?: number | null;
}

View File

@@ -1,6 +1,5 @@
import React from 'react'
import Layout from '../../components/layout/Layout'
import TimelineGallery from '../../components/timelineGallery/TimelineGallery'
import { useTranslation } from 'react-i18next'
const PhotosPage = () => {
@@ -9,7 +8,7 @@ const PhotosPage = () => {
return (
<>
<Layout title={t('photos_page.title', 'Photos')}>
<TimelineGallery />
{/* <TimelineGallery /> */}
</Layout>
</>
)

View File

@@ -3,86 +3,86 @@
// @generated
// This file was automatically generated and should not be edited.
import { MediaType } from './../../../__generated__/globalTypes'
import { MediaType } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: placePageQueryMedia
// ====================================================
export interface placePageQueryMedia_mediaList_thumbnail {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface placePageQueryMedia_mediaList_highRes {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface placePageQueryMedia_mediaList_videoWeb {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface placePageQueryMedia_mediaList {
__typename: 'Media'
id: string
title: string
__typename: "Media";
id: string;
title: string;
/**
* URL to display the media in a smaller resolution
*/
thumbnail: placePageQueryMedia_mediaList_thumbnail | null
thumbnail: placePageQueryMedia_mediaList_thumbnail | null;
/**
* URL to display the photo in full resolution, will be null for videos
*/
highRes: placePageQueryMedia_mediaList_highRes | null
highRes: placePageQueryMedia_mediaList_highRes | null;
/**
* URL to get the video in a web format that can be played in the browser, will be null for photos
*/
videoWeb: placePageQueryMedia_mediaList_videoWeb | null
type: MediaType
videoWeb: placePageQueryMedia_mediaList_videoWeb | null;
type: MediaType;
}
export interface placePageQueryMedia {
/**
* Get a list of media by their ids, user must own the media or be admin
*/
mediaList: placePageQueryMedia_mediaList[]
mediaList: placePageQueryMedia_mediaList[];
}
export interface placePageQueryMediaVariables {
mediaIDs: string[]
mediaIDs: string[];
}

View File

@@ -8,15 +8,15 @@
// ====================================================
export interface changeUserPassword_updateUser {
__typename: 'User'
id: string
__typename: "User";
id: string;
}
export interface changeUserPassword {
updateUser: changeUserPassword_updateUser
updateUser: changeUserPassword_updateUser;
}
export interface changeUserPasswordVariables {
userId: string
password: string
userId: string;
password: string;
}

View File

@@ -8,17 +8,17 @@
// ====================================================
export interface createUser_createUser {
__typename: 'User'
id: string
username: string
admin: boolean
__typename: "User";
id: string;
username: string;
admin: boolean;
}
export interface createUser {
createUser: createUser_createUser
createUser: createUser_createUser;
}
export interface createUserVariables {
username: string
admin: boolean
username: string;
admin: boolean;
}

View File

@@ -8,15 +8,15 @@
// ====================================================
export interface deleteUser_deleteUser {
__typename: 'User'
id: string
username: string
__typename: "User";
id: string;
username: string;
}
export interface deleteUser {
deleteUser: deleteUser_deleteUser
deleteUser: deleteUser_deleteUser;
}
export interface deleteUserVariables {
id: string
id: string;
}

View File

@@ -8,18 +8,18 @@
// ====================================================
export interface updateUser_updateUser {
__typename: 'User'
id: string
username: string
admin: boolean
__typename: "User";
id: string;
username: string;
admin: boolean;
}
export interface updateUser {
updateUser: updateUser_updateUser
updateUser: updateUser_updateUser;
}
export interface updateUserVariables {
id: string
username?: string | null
admin?: boolean | null
id: string;
username?: string | null;
admin?: boolean | null;
}

View File

@@ -3,22 +3,22 @@
// @generated
// This file was automatically generated and should not be edited.
import { LanguageTranslation } from './../../../__generated__/globalTypes'
import { LanguageTranslation } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL mutation operation: changeUserPreferences
// ====================================================
export interface changeUserPreferences_changeUserPreferences {
__typename: 'UserPreferences'
id: string
language: LanguageTranslation | null
__typename: "UserPreferences";
id: string;
language: LanguageTranslation | null;
}
export interface changeUserPreferences {
changeUserPreferences: changeUserPreferences_changeUserPreferences
changeUserPreferences: changeUserPreferences_changeUserPreferences;
}
export interface changeUserPreferencesVariables {
language?: string | null
language?: string | null;
}

View File

@@ -3,18 +3,18 @@
// @generated
// This file was automatically generated and should not be edited.
import { LanguageTranslation } from './../../../__generated__/globalTypes'
import { LanguageTranslation } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: myUserPreferences
// ====================================================
export interface myUserPreferences_myUserPreferences {
__typename: 'UserPreferences'
id: string
language: LanguageTranslation | null
__typename: "UserPreferences";
id: string;
language: LanguageTranslation | null;
}
export interface myUserPreferences {
myUserPreferences: myUserPreferences_myUserPreferences
myUserPreferences: myUserPreferences_myUserPreferences;
}

View File

@@ -3,172 +3,172 @@
// @generated
// This file was automatically generated and should not be edited.
import { MediaType } from './../../../__generated__/globalTypes'
import { MediaType } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: SharePageToken
// ====================================================
export interface SharePageToken_shareToken_album {
__typename: 'Album'
id: string
__typename: "Album";
id: string;
}
export interface SharePageToken_shareToken_media_thumbnail {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface SharePageToken_shareToken_media_downloads_mediaUrl {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
/**
* The file size of the resource in bytes
*/
fileSize: number
fileSize: number;
}
export interface SharePageToken_shareToken_media_downloads {
__typename: 'MediaDownload'
title: string
mediaUrl: SharePageToken_shareToken_media_downloads_mediaUrl
__typename: "MediaDownload";
title: string;
mediaUrl: SharePageToken_shareToken_media_downloads_mediaUrl;
}
export interface SharePageToken_shareToken_media_highRes {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface SharePageToken_shareToken_media_videoWeb {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface SharePageToken_shareToken_media_exif {
__typename: 'MediaEXIF'
id: string
__typename: "MediaEXIF";
id: string;
/**
* The model name of the camera
*/
camera: string | null
camera: string | null;
/**
* The maker of the camera
*/
maker: string | null
maker: string | null;
/**
* The name of the lens
*/
lens: string | null
dateShot: any | null
lens: string | null;
dateShot: any | null;
/**
* The exposure time of the image
*/
exposure: number | null
exposure: number | null;
/**
* The aperature stops of the image
*/
aperture: number | null
aperture: number | null;
/**
* The ISO setting of the image
*/
iso: number | null
iso: number | null;
/**
* The focal length of the lens, when the image was taken
*/
focalLength: number | null
focalLength: number | null;
/**
* A formatted description of the flash settings, when the image was taken
*/
flash: number | null
flash: number | null;
/**
* An index describing the mode for adjusting the exposure of the image
*/
exposureProgram: number | null
exposureProgram: number | null;
}
export interface SharePageToken_shareToken_media {
__typename: 'Media'
id: string
title: string
type: MediaType
__typename: "Media";
id: string;
title: string;
type: MediaType;
/**
* URL to display the media in a smaller resolution
*/
thumbnail: SharePageToken_shareToken_media_thumbnail | null
downloads: SharePageToken_shareToken_media_downloads[]
thumbnail: SharePageToken_shareToken_media_thumbnail | null;
downloads: SharePageToken_shareToken_media_downloads[];
/**
* URL to display the photo in full resolution, will be null for videos
*/
highRes: SharePageToken_shareToken_media_highRes | null
highRes: SharePageToken_shareToken_media_highRes | null;
/**
* URL to get the video in a web format that can be played in the browser, will be null for photos
*/
videoWeb: SharePageToken_shareToken_media_videoWeb | null
exif: SharePageToken_shareToken_media_exif | null
videoWeb: SharePageToken_shareToken_media_videoWeb | null;
exif: SharePageToken_shareToken_media_exif | null;
}
export interface SharePageToken_shareToken {
__typename: 'ShareToken'
token: string
__typename: "ShareToken";
token: string;
/**
* The album this token shares
*/
album: SharePageToken_shareToken_album | null
album: SharePageToken_shareToken_album | null;
/**
* The media this token shares
*/
media: SharePageToken_shareToken_media | null
media: SharePageToken_shareToken_media | null;
}
export interface SharePageToken {
shareToken: SharePageToken_shareToken
shareToken: SharePageToken_shareToken;
}
export interface SharePageTokenVariables {
token: string
password?: string | null
token: string;
password?: string | null;
}

View File

@@ -3,178 +3,178 @@
// @generated
// This file was automatically generated and should not be edited.
import { OrderDirection, MediaType } from './../../../__generated__/globalTypes'
import { OrderDirection, MediaType } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: shareAlbumQuery
// ====================================================
export interface shareAlbumQuery_album_subAlbums_thumbnail_thumbnail {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
}
export interface shareAlbumQuery_album_subAlbums_thumbnail {
__typename: 'Media'
__typename: "Media";
/**
* URL to display the media in a smaller resolution
*/
thumbnail: shareAlbumQuery_album_subAlbums_thumbnail_thumbnail | null
thumbnail: shareAlbumQuery_album_subAlbums_thumbnail_thumbnail | null;
}
export interface shareAlbumQuery_album_subAlbums {
__typename: 'Album'
id: string
title: string
__typename: "Album";
id: string;
title: string;
/**
* An image in this album used for previewing this album
*/
thumbnail: shareAlbumQuery_album_subAlbums_thumbnail | null
thumbnail: shareAlbumQuery_album_subAlbums_thumbnail | null;
}
export interface shareAlbumQuery_album_media_thumbnail {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface shareAlbumQuery_album_media_downloads_mediaUrl {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
/**
* The file size of the resource in bytes
*/
fileSize: number
fileSize: number;
}
export interface shareAlbumQuery_album_media_downloads {
__typename: 'MediaDownload'
title: string
mediaUrl: shareAlbumQuery_album_media_downloads_mediaUrl
__typename: "MediaDownload";
title: string;
mediaUrl: shareAlbumQuery_album_media_downloads_mediaUrl;
}
export interface shareAlbumQuery_album_media_highRes {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface shareAlbumQuery_album_media_videoWeb {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
}
export interface shareAlbumQuery_album_media_exif {
__typename: 'MediaEXIF'
__typename: "MediaEXIF";
/**
* The model name of the camera
*/
camera: string | null
camera: string | null;
/**
* The maker of the camera
*/
maker: string | null
maker: string | null;
/**
* The name of the lens
*/
lens: string | null
dateShot: any | null
lens: string | null;
dateShot: any | null;
/**
* The exposure time of the image
*/
exposure: number | null
exposure: number | null;
/**
* The aperature stops of the image
*/
aperture: number | null
aperture: number | null;
/**
* The ISO setting of the image
*/
iso: number | null
iso: number | null;
/**
* The focal length of the lens, when the image was taken
*/
focalLength: number | null
focalLength: number | null;
/**
* A formatted description of the flash settings, when the image was taken
*/
flash: number | null
flash: number | null;
/**
* An index describing the mode for adjusting the exposure of the image
*/
exposureProgram: number | null
exposureProgram: number | null;
}
export interface shareAlbumQuery_album_media {
__typename: 'Media'
id: string
title: string
type: MediaType
__typename: "Media";
id: string;
title: string;
type: MediaType;
/**
* URL to display the media in a smaller resolution
*/
thumbnail: shareAlbumQuery_album_media_thumbnail | null
downloads: shareAlbumQuery_album_media_downloads[]
thumbnail: shareAlbumQuery_album_media_thumbnail | null;
downloads: shareAlbumQuery_album_media_downloads[];
/**
* URL to display the photo in full resolution, will be null for videos
*/
highRes: shareAlbumQuery_album_media_highRes | null
highRes: shareAlbumQuery_album_media_highRes | null;
/**
* URL to get the video in a web format that can be played in the browser, will be null for photos
*/
videoWeb: shareAlbumQuery_album_media_videoWeb | null
exif: shareAlbumQuery_album_media_exif | null
videoWeb: shareAlbumQuery_album_media_videoWeb | null;
exif: shareAlbumQuery_album_media_exif | null;
}
export interface shareAlbumQuery_album {
__typename: 'Album'
id: string
title: string
__typename: "Album";
id: string;
title: string;
/**
* The albums contained in this album
*/
subAlbums: shareAlbumQuery_album_subAlbums[]
subAlbums: shareAlbumQuery_album_subAlbums[];
/**
* The media inside this album
*/
media: shareAlbumQuery_album_media[]
media: shareAlbumQuery_album_media[];
}
export interface shareAlbumQuery {
@@ -182,15 +182,15 @@ export interface shareAlbumQuery {
* Get album by id, user must own the album or be admin
* If valid tokenCredentials are provided, the album may be retrived without further authentication
*/
album: shareAlbumQuery_album
album: shareAlbumQuery_album;
}
export interface shareAlbumQueryVariables {
id: string
token: string
password?: string | null
mediaOrderBy?: string | null
mediaOrderDirection?: OrderDirection | null
limit?: number | null
offset?: number | null
id: string;
token: string;
password?: string | null;
mediaOrderBy?: string | null;
mediaOrderDirection?: OrderDirection | null;
limit?: number | null;
offset?: number | null;
}

View File

@@ -8,34 +8,34 @@
//==============================================================
export enum LanguageTranslation {
Danish = 'Danish',
English = 'English',
French = 'French',
German = 'German',
Italian = 'Italian',
Polish = 'Polish',
Russian = 'Russian',
Spanish = 'Spanish',
Swedish = 'Swedish',
TraditionalChinese = 'TraditionalChinese',
SimplifiedChinese = 'SimplifiedChinese',
Portuguese = 'Portuguese',
Danish = "Danish",
English = "English",
French = "French",
German = "German",
Italian = "Italian",
Polish = "Polish",
Portuguese = "Portuguese",
Russian = "Russian",
SimplifiedChinese = "SimplifiedChinese",
Spanish = "Spanish",
Swedish = "Swedish",
TraditionalChinese = "TraditionalChinese",
}
export enum MediaType {
Photo = 'Photo',
Video = 'Video',
Photo = "Photo",
Video = "Video",
}
export enum NotificationType {
Close = 'Close',
Message = 'Message',
Progress = 'Progress',
Close = "Close",
Message = "Message",
Progress = "Progress",
}
export enum OrderDirection {
ASC = 'ASC',
DESC = 'DESC',
ASC = "ASC",
DESC = "DESC",
}
//==============================================================

View File

@@ -3,18 +3,18 @@
// @generated
// This file was automatically generated and should not be edited.
import { LanguageTranslation } from './globalTypes'
import { LanguageTranslation } from "./globalTypes";
// ====================================================
// GraphQL query operation: siteTranslation
// ====================================================
export interface siteTranslation_myUserPreferences {
__typename: 'UserPreferences'
id: string
language: LanguageTranslation | null
__typename: "UserPreferences";
id: string;
language: LanguageTranslation | null;
}
export interface siteTranslation {
myUserPreferences: siteTranslation_myUserPreferences
myUserPreferences: siteTranslation_myUserPreferences;
}

View File

@@ -8,15 +8,15 @@
// ====================================================
export interface albumPathQuery_album_path {
__typename: 'Album'
id: string
title: string
__typename: "Album";
id: string;
title: string;
}
export interface albumPathQuery_album {
__typename: 'Album'
id: string
path: albumPathQuery_album_path[]
__typename: "Album";
id: string;
path: albumPathQuery_album_path[];
}
export interface albumPathQuery {
@@ -24,9 +24,9 @@ export interface albumPathQuery {
* Get album by id, user must own the album or be admin
* If valid tokenCredentials are provided, the album may be retrived without further authentication
*/
album: albumPathQuery_album
album: albumPathQuery_album;
}
export interface albumPathQueryVariables {
id: string
id: string;
}

View File

@@ -8,13 +8,13 @@
// ====================================================
export interface adminQuery_myUser {
__typename: 'User'
admin: boolean
__typename: "User";
admin: boolean;
}
export interface adminQuery {
/**
* Information about the currently logged in user
*/
myUser: adminQuery_myUser
myUser: adminQuery_myUser;
}

View File

@@ -0,0 +1,20 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: faceDetectionEnabled
// ====================================================
export interface faceDetectionEnabled_siteInfo {
__typename: "SiteInfo";
/**
* Whether or not face detection is enabled and working
*/
faceDetectionEnabled: boolean;
}
export interface faceDetectionEnabled {
siteInfo: faceDetectionEnabled_siteInfo;
}

View File

@@ -11,5 +11,5 @@ export interface mapboxEnabledQuery {
/**
* Get the mapbox api token, returns null if mapbox is not enabled
*/
mapboxToken: string | null
mapboxToken: string | null;
}

View File

@@ -3,27 +3,27 @@
// @generated
// This file was automatically generated and should not be edited.
import { NotificationType } from './../../../__generated__/globalTypes'
import { NotificationType } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL subscription operation: notificationSubscription
// ====================================================
export interface notificationSubscription_notification {
__typename: 'Notification'
key: string
type: NotificationType
header: string
content: string
progress: number | null
positive: boolean
negative: boolean
__typename: "Notification";
key: string;
type: NotificationType;
header: string;
content: string;
progress: number | null;
positive: boolean;
negative: boolean;
/**
* Time in milliseconds before the notification will close
*/
timeout: number | null
timeout: number | null;
}
export interface notificationSubscription {
notification: notificationSubscription_notification
notification: notificationSubscription_notification;
}

View File

@@ -8,19 +8,19 @@
// ====================================================
export interface markMediaFavorite_favoriteMedia {
__typename: 'Media'
id: string
favorite: boolean
__typename: "Media";
id: string;
favorite: boolean;
}
export interface markMediaFavorite {
/**
* Mark or unmark a media as being a favorite
*/
favoriteMedia: markMediaFavorite_favoriteMedia
favoriteMedia: markMediaFavorite_favoriteMedia;
}
export interface markMediaFavoriteVariables {
mediaId: string
favorite: boolean
mediaId: string;
favorite: boolean;
}

View File

@@ -8,19 +8,19 @@
// ====================================================
export interface sidebarAlbumAddShare_shareAlbum {
__typename: 'ShareToken'
token: string
__typename: "ShareToken";
token: string;
}
export interface sidebarAlbumAddShare {
/**
* Generate share token for album
*/
shareAlbum: sidebarAlbumAddShare_shareAlbum
shareAlbum: sidebarAlbumAddShare_shareAlbum;
}
export interface sidebarAlbumAddShareVariables {
id: string
password?: string | null
expire?: any | null
id: string;
password?: string | null;
expire?: any | null;
}

View File

@@ -8,19 +8,19 @@
// ====================================================
export interface sidebarGetAlbumShares_album_shares {
__typename: 'ShareToken'
id: string
token: string
__typename: "ShareToken";
id: string;
token: string;
/**
* Whether or not a password is needed to access the share
*/
hasPassword: boolean
hasPassword: boolean;
}
export interface sidebarGetAlbumShares_album {
__typename: 'Album'
id: string
shares: sidebarGetAlbumShares_album_shares[]
__typename: "Album";
id: string;
shares: sidebarGetAlbumShares_album_shares[];
}
export interface sidebarGetAlbumShares {
@@ -28,9 +28,9 @@ export interface sidebarGetAlbumShares {
* Get album by id, user must own the album or be admin
* If valid tokenCredentials are provided, the album may be retrived without further authentication
*/
album: sidebarGetAlbumShares_album
album: sidebarGetAlbumShares_album;
}
export interface sidebarGetAlbumSharesVariables {
id: string
id: string;
}

View File

@@ -8,19 +8,19 @@
// ====================================================
export interface sidebarGetPhotoShares_media_shares {
__typename: 'ShareToken'
id: string
token: string
__typename: "ShareToken";
id: string;
token: string;
/**
* Whether or not a password is needed to access the share
*/
hasPassword: boolean
hasPassword: boolean;
}
export interface sidebarGetPhotoShares_media {
__typename: 'Media'
id: string
shares: sidebarGetPhotoShares_media_shares[]
__typename: "Media";
id: string;
shares: sidebarGetPhotoShares_media_shares[];
}
export interface sidebarGetPhotoShares {
@@ -28,9 +28,9 @@ export interface sidebarGetPhotoShares {
* Get media by id, user must own the media or be admin.
* If valid tokenCredentials are provided, the media may be retrived without further authentication
*/
media: sidebarGetPhotoShares_media
media: sidebarGetPhotoShares_media;
}
export interface sidebarGetPhotoSharesVariables {
id: string
id: string;
}

View File

@@ -3,155 +3,155 @@
// @generated
// This file was automatically generated and should not be edited.
import { MediaType } from './../../../__generated__/globalTypes'
import { MediaType } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: sidebarPhoto
// ====================================================
export interface sidebarPhoto_media_highRes {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface sidebarPhoto_media_thumbnail {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface sidebarPhoto_media_videoWeb {
__typename: 'MediaURL'
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string
url: string;
/**
* Width of the image in pixels
*/
width: number
width: number;
/**
* Height of the image in pixels
*/
height: number
height: number;
}
export interface sidebarPhoto_media_videoMetadata {
__typename: 'VideoMetadata'
id: string
width: number
height: number
duration: number
codec: string | null
framerate: number | null
bitrate: string | null
colorProfile: string | null
audio: string | null
__typename: "VideoMetadata";
id: string;
width: number;
height: number;
duration: number;
codec: string | null;
framerate: number | null;
bitrate: string | null;
colorProfile: string | null;
audio: string | null;
}
export interface sidebarPhoto_media_exif {
__typename: 'MediaEXIF'
id: string
__typename: "MediaEXIF";
id: string;
/**
* The model name of the camera
*/
camera: string | null
camera: string | null;
/**
* The maker of the camera
*/
maker: string | null
maker: string | null;
/**
* The name of the lens
*/
lens: string | null
dateShot: any | null
lens: string | null;
dateShot: any | null;
/**
* The exposure time of the image
*/
exposure: number | null
exposure: number | null;
/**
* The aperature stops of the image
*/
aperture: number | null
aperture: number | null;
/**
* The ISO setting of the image
*/
iso: number | null
iso: number | null;
/**
* The focal length of the lens, when the image was taken
*/
focalLength: number | null
focalLength: number | null;
/**
* A formatted description of the flash settings, when the image was taken
*/
flash: number | null
flash: number | null;
/**
* An index describing the mode for adjusting the exposure of the image
*/
exposureProgram: number | null
exposureProgram: number | null;
}
export interface sidebarPhoto_media_faces_rectangle {
__typename: 'FaceRectangle'
minX: number
maxX: number
minY: number
maxY: number
__typename: "FaceRectangle";
minX: number;
maxX: number;
minY: number;
maxY: number;
}
export interface sidebarPhoto_media_faces_faceGroup {
__typename: 'FaceGroup'
id: string
__typename: "FaceGroup";
id: string;
}
export interface sidebarPhoto_media_faces {
__typename: 'ImageFace'
id: string
rectangle: sidebarPhoto_media_faces_rectangle
faceGroup: sidebarPhoto_media_faces_faceGroup
__typename: "ImageFace";
id: string;
rectangle: sidebarPhoto_media_faces_rectangle;
faceGroup: sidebarPhoto_media_faces_faceGroup;
}
export interface sidebarPhoto_media {
__typename: 'Media'
id: string
title: string
type: MediaType
__typename: "Media";
id: string;
title: string;
type: MediaType;
/**
* URL to display the photo in full resolution, will be null for videos
*/
highRes: sidebarPhoto_media_highRes | null
highRes: sidebarPhoto_media_highRes | null;
/**
* URL to display the media in a smaller resolution
*/
thumbnail: sidebarPhoto_media_thumbnail | null
thumbnail: sidebarPhoto_media_thumbnail | null;
/**
* URL to get the video in a web format that can be played in the browser, will be null for photos
*/
videoWeb: sidebarPhoto_media_videoWeb | null
videoMetadata: sidebarPhoto_media_videoMetadata | null
exif: sidebarPhoto_media_exif | null
faces: sidebarPhoto_media_faces[]
videoWeb: sidebarPhoto_media_videoWeb | null;
videoMetadata: sidebarPhoto_media_videoMetadata | null;
exif: sidebarPhoto_media_exif | null;
faces: sidebarPhoto_media_faces[];
}
export interface sidebarPhoto {
@@ -159,9 +159,9 @@ export interface sidebarPhoto {
* Get media by id, user must own the media or be admin.
* If valid tokenCredentials are provided, the media may be retrived without further authentication
*/
media: sidebarPhoto_media
media: sidebarPhoto_media;
}
export interface sidebarPhotoVariables {
id: string
id: string;
}

View File

@@ -8,19 +8,19 @@
// ====================================================
export interface sidebarPhotoAddShare_shareMedia {
__typename: 'ShareToken'
token: string
__typename: "ShareToken";
token: string;
}
export interface sidebarPhotoAddShare {
/**
* Generate share token for media
*/
shareMedia: sidebarPhotoAddShare_shareMedia
shareMedia: sidebarPhotoAddShare_shareMedia;
}
export interface sidebarPhotoAddShareVariables {
id: string
password?: string | null
expire?: any | null
id: string;
password?: string | null;
expire?: any | null;
}

View File

@@ -8,22 +8,22 @@
// ====================================================
export interface sidebarProtectShare_protectShareToken {
__typename: 'ShareToken'
token: string
__typename: "ShareToken";
token: string;
/**
* Whether or not a password is needed to access the share
*/
hasPassword: boolean
hasPassword: boolean;
}
export interface sidebarProtectShare {
/**
* Set a password for a token, if null is passed for the password argument, the password will be cleared
*/
protectShareToken: sidebarProtectShare_protectShareToken
protectShareToken: sidebarProtectShare_protectShareToken;
}
export interface sidebarProtectShareVariables {
token: string
password?: string | null
token: string;
password?: string | null;
}

View File

@@ -8,17 +8,17 @@
// ====================================================
export interface sidebareDeleteShare_deleteShareToken {
__typename: 'ShareToken'
token: string
__typename: "ShareToken";
token: string;
}
export interface sidebareDeleteShare {
/**
* Delete a share token by it's token value
*/
deleteShareToken: sidebareDeleteShare_deleteShareToken
deleteShareToken: sidebareDeleteShare_deleteShareToken;
}
export interface sidebareDeleteShareVariables {
token: string
token: string;
}

View File

@@ -27,30 +27,27 @@ const MY_TIMELINE_QUERY = gql`
onlyFavorites: $onlyFavorites
paginate: { limit: $limit, offset: $offset }
) {
id
title
type
thumbnail {
url
width
height
}
highRes {
url
width
height
}
videoWeb {
url
}
favorite
album {
id
title
}
media {
id
title
type
thumbnail {
url
width
height
}
highRes {
url
width
height
}
videoWeb {
url
}
favorite
}
mediaTotal
date
}
}
@@ -63,7 +60,13 @@ export type TimelineActiveIndex = {
export type TimelineGroup = {
date: string
groups: myTimeline_myTimeline[]
albums: TimelineGroupAlbum[]
}
export type TimelineGroupAlbum = {
id: string
title: string
media: myTimeline_myTimeline[]
}
const TimelineGallery = () => {
@@ -95,7 +98,7 @@ const TimelineGallery = () => {
variables: {
onlyFavorites,
offset: 0,
limit: 50,
limit: 200,
},
})

View File

@@ -3,92 +3,95 @@
// @generated
// This file was automatically generated and should not be edited.
import { MediaType } from './../../../__generated__/globalTypes'
import { MediaType } from "./../../../__generated__/globalTypes";
// ====================================================
// GraphQL query operation: myTimeline
// ====================================================
export interface myTimeline_myTimeline_thumbnail {
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string;
/**
* Width of the image in pixels
*/
width: number;
/**
* Height of the image in pixels
*/
height: number;
}
export interface myTimeline_myTimeline_highRes {
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string;
/**
* Width of the image in pixels
*/
width: number;
/**
* Height of the image in pixels
*/
height: number;
}
export interface myTimeline_myTimeline_videoWeb {
__typename: "MediaURL";
/**
* URL for previewing the image
*/
url: string;
}
export interface myTimeline_myTimeline_album {
__typename: 'Album'
id: string
title: string
}
export interface myTimeline_myTimeline_media_thumbnail {
__typename: 'MediaURL'
/**
* URL for previewing the image
*/
url: string
/**
* Width of the image in pixels
*/
width: number
/**
* Height of the image in pixels
*/
height: number
}
export interface myTimeline_myTimeline_media_highRes {
__typename: 'MediaURL'
/**
* URL for previewing the image
*/
url: string
/**
* Width of the image in pixels
*/
width: number
/**
* Height of the image in pixels
*/
height: number
}
export interface myTimeline_myTimeline_media_videoWeb {
__typename: 'MediaURL'
/**
* URL for previewing the image
*/
url: string
}
export interface myTimeline_myTimeline_media {
__typename: 'Media'
id: string
title: string
type: MediaType
/**
* URL to display the media in a smaller resolution
*/
thumbnail: myTimeline_myTimeline_media_thumbnail | null
/**
* URL to display the photo in full resolution, will be null for videos
*/
highRes: myTimeline_myTimeline_media_highRes | null
/**
* URL to get the video in a web format that can be played in the browser, will be null for photos
*/
videoWeb: myTimeline_myTimeline_media_videoWeb | null
favorite: boolean
__typename: "Album";
id: string;
title: string;
}
export interface myTimeline_myTimeline {
__typename: 'TimelineGroup'
album: myTimeline_myTimeline_album
media: myTimeline_myTimeline_media[]
mediaTotal: number
date: any
__typename: "Media";
id: string;
title: string;
type: MediaType;
/**
* URL to display the media in a smaller resolution
*/
thumbnail: myTimeline_myTimeline_thumbnail | null;
/**
* URL to display the photo in full resolution, will be null for videos
*/
highRes: myTimeline_myTimeline_highRes | null;
/**
* URL to get the video in a web format that can be played in the browser, will be null for photos
*/
videoWeb: myTimeline_myTimeline_videoWeb | null;
favorite: boolean;
/**
* The album that holds the media
*/
album: myTimeline_myTimeline_album;
/**
* The date the image was shot or the date it was imported as a fallback
*/
date: any;
}
export interface myTimeline {
myTimeline: myTimeline_myTimeline[]
/**
* Get a list of media, ordered first by day, then by album if multiple media was found for the same day.
*/
myTimeline: myTimeline_myTimeline[];
}
export interface myTimelineVariables {
onlyFavorites?: boolean | null
limit?: number | null
offset?: number | null
onlyFavorites?: boolean | null;
limit?: number | null;
offset?: number | null;
}

View File

@@ -19,120 +19,114 @@ describe('timeline gallery reducer', () => {
const timelineData: myTimeline_myTimeline[] = [
{
album: {
id: '5',
title: 'first album',
__typename: 'Album',
__typename: 'Media',
id: '1058',
title: '122A2876.jpg',
type: MediaType.Photo,
thumbnail: {
__typename: 'MediaURL',
url: 'http://localhost:4001/photo/thumbnail_122A2876_jpg_Kp1U80vD.jpg',
width: 1024,
height: 682,
},
media: [
{
id: '165',
title: '3666760020.jpg',
type: MediaType.Photo,
thumbnail: {
url: 'http://localhost:4001/photo/thumbnail_3666760020_jpg_x76GG5pS.jpg',
width: 768,
height: 1024,
__typename: 'MediaURL',
},
highRes: {
url: 'http://localhost:4001/photo/3666760020_wijGDNZ2.jpg',
width: 3024,
height: 4032,
__typename: 'MediaURL',
},
videoWeb: null,
favorite: false,
__typename: 'Media',
},
{
id: '184',
title: '7414455077.jpg',
type: MediaType.Photo,
thumbnail: {
url: 'http://localhost:4001/photo/thumbnail_7414455077_jpg_9JYHHYh6.jpg',
width: 768,
height: 1024,
__typename: 'MediaURL',
},
highRes: {
url: 'http://localhost:4001/photo/7414455077_0ejDBiKr.jpg',
width: 3024,
height: 4032,
__typename: 'MediaURL',
},
videoWeb: null,
favorite: false,
__typename: 'Media',
},
],
mediaTotal: 5,
date: '2019-09-21T00:00:00Z',
__typename: 'TimelineGroup',
highRes: {
__typename: 'MediaURL',
url: 'http://localhost:4001/photo/122A2876_5cSPMiKL.jpg',
width: 6720,
height: 4480,
},
videoWeb: null,
favorite: false,
album: { __typename: 'Album', id: '522', title: 'random' },
date: '2020-12-13T18:03:40Z',
},
{
album: {
id: '5',
title: 'another album',
__typename: 'Album',
__typename: 'Media',
id: '1059',
title: '122A2630-Edit.jpg',
type: MediaType.Photo,
thumbnail: {
__typename: 'MediaURL',
url: 'http://localhost:4001/photo/thumbnail_122A2630-Edit_jpg_pwjtMkpy.jpg',
width: 1024,
height: 682,
},
media: [
{
id: '165',
title: '3666760020.jpg',
type: MediaType.Photo,
thumbnail: {
url: 'http://localhost:4001/photo/thumbnail_3666760020_jpg_x76GG5pS.jpg',
width: 768,
height: 1024,
__typename: 'MediaURL',
},
highRes: {
url: 'http://localhost:4001/photo/3666760020_wijGDNZ2.jpg',
width: 3024,
height: 4032,
__typename: 'MediaURL',
},
videoWeb: null,
favorite: false,
__typename: 'Media',
},
],
mediaTotal: 7,
date: '2019-09-21T00:00:00Z',
__typename: 'TimelineGroup',
highRes: {
__typename: 'MediaURL',
url: 'http://localhost:4001/photo/122A2630-Edit_ySQWFAgE.jpg',
width: 6177,
height: 4118,
},
videoWeb: null,
favorite: false,
album: { __typename: 'Album', id: '523', title: 'another_album' },
date: '2020-11-25T16:14:33Z',
},
{
__typename: 'TimelineGroup',
album: {
__typename: 'Album',
id: '5',
title: 'album on another day',
__typename: 'Media',
id: '1060',
title: '122A2785-2.jpg',
type: MediaType.Photo,
thumbnail: {
__typename: 'MediaURL',
url: 'http://localhost:4001/photo/thumbnail_122A2785-2_jpg_CevmxEXf.jpg',
width: 1024,
height: 1024,
},
date: '2019-09-13T00:00:00Z',
mediaTotal: 1,
media: [
{
__typename: 'Media',
favorite: false,
videoWeb: null,
thumbnail: {
url: 'http://localhost:4001/photo/thumbnail_3666760020_jpg_x76GG5pS.jpg',
width: 768,
height: 1024,
__typename: 'MediaURL',
},
highRes: {
url: 'http://localhost:4001/photo/3666760020_wijGDNZ2.jpg',
width: 3024,
height: 4032,
__typename: 'MediaURL',
},
id: '321',
title: 'asdfimg.jpg',
type: MediaType.Photo,
},
],
highRes: {
__typename: 'MediaURL',
url: 'http://localhost:4001/photo/122A2785-2_mCnWjLdb.jpg',
width: 884,
height: 884,
},
videoWeb: null,
favorite: false,
album: { __typename: 'Album', id: '523', title: 'another_album' },
date: '2020-11-25T16:43:59Z',
},
{
__typename: 'Media',
id: '1056',
title: '122A2630-Edit.jpg',
type: MediaType.Photo,
thumbnail: {
__typename: 'MediaURL',
url: 'http://localhost:4001/photo/thumbnail_122A2630-Edit_jpg_aJPCSDDl.jpg',
width: 1024,
height: 682,
},
highRes: {
__typename: 'MediaURL',
url: 'http://localhost:4001/photo/122A2630-Edit_em9g89qg.jpg',
width: 6177,
height: 4118,
},
videoWeb: null,
favorite: false,
album: { __typename: 'Album', id: '522', title: 'random' },
date: '2020-11-25T16:14:33Z',
},
{
__typename: 'Media',
id: '1054',
title: '122A2559.jpg',
type: MediaType.Photo,
thumbnail: {
__typename: 'MediaURL',
url: 'http://localhost:4001/photo/thumbnail_122A2559_jpg_MsOJtPi8.jpg',
width: 1024,
height: 712,
},
highRes: {
__typename: 'MediaURL',
url: 'http://localhost:4001/photo/122A2559_FDsQHuBN.jpg',
width: 6246,
height: 4346,
},
videoWeb: null,
favorite: false,
album: { __typename: 'Album', id: '522', title: 'random' },
date: '2020-11-09T15:38:09Z',
},
]
@@ -150,72 +144,102 @@ describe('timeline gallery reducer', () => {
media: -1,
},
timelineGroups: [
{
date: '2019-09-21T00:00:00Z',
groups: [
{
album: {
id: '5',
title: 'first album',
},
date: '2019-09-21T00:00:00Z',
media: [
{
favorite: false,
highRes: {},
id: '165',
thumbnail: {},
title: '3666760020.jpg',
type: 'Photo',
},
{
highRes: {},
id: '184',
thumbnail: {},
title: '7414455077.jpg',
type: 'Photo',
},
],
mediaTotal: 5,
},
{
album: {
id: '5',
title: 'another album',
},
date: '2019-09-21T00:00:00Z',
media: [
{
id: '165',
},
],
mediaTotal: 7,
},
],
},
{
date: '2019-09-13T00:00:00Z',
groups: [
{
album: {
id: '5',
title: 'album on another day',
},
date: '2019-09-13T00:00:00Z',
media: [
{
favorite: false,
highRes: {},
id: '321',
thumbnail: {},
title: 'asdfimg.jpg',
type: 'Photo',
},
],
mediaTotal: 1,
},
],
},
// {
// date: '2020-12-13T00:00:00Z',
// albums: [
// {
// id: '522',
// title: 'random',
// media: [
// {
// __typename: 'Media',
// id: '1058',
// title: '122A2876.jpg',
// type: MediaType.Photo,
// thumbnail: {
// __typename: 'MediaURL',
// url: 'http://localhost:4001/photo/thumbnail_122A2876_jpg_Kp1U80vD.jpg',
// width: 1024,
// height: 682,
// },
// highRes: {
// __typename: 'MediaURL',
// url: 'http://localhost:4001/photo/122A2876_5cSPMiKL.jpg',
// width: 6720,
// height: 4480,
// },
// videoWeb: null,
// favorite: false,
// album: { __typename: 'Album', id: '522', title: 'random' },
// date: '2020-12-13T18:03:40Z',
// },
// ],
// },
// ],
// },
// {
// date: '2020-11-25T00:00:00Z',
// albums: [
// {
// id: '523',
// title: 'another_album',
// media: [
// {
// __typename: 'Media',
// id: '1059',
// title: '122A2630-Edit.jpg',
// type: MediaType.Photo,
// thumbnail: {
// __typename: 'MediaURL',
// url: 'http://localhost:4001/photo/thumbnail_122A2630-Edit_jpg_pwjtMkpy.jpg',
// width: 1024,
// height: 682,
// },
// highRes: {
// __typename: 'MediaURL',
// url: 'http://localhost:4001/photo/122A2630-Edit_ySQWFAgE.jpg',
// width: 6177,
// height: 4118,
// },
// videoWeb: null,
// favorite: false,
// album: {
// __typename: 'Album',
// id: '523',
// title: 'another_album',
// },
// date: '2020-11-25T16:14:33Z',
// },
// {
// __typename: 'Media',
// id: '1060',
// title: '122A2785-2.jpg',
// type: MediaType.Photo,
// thumbnail: {
// __typename: 'MediaURL',
// url: 'http://localhost:4001/photo/thumbnail_122A2785-2_jpg_CevmxEXf.jpg',
// width: 1024,
// height: 1024,
// },
// highRes: {
// __typename: 'MediaURL',
// url: 'http://localhost:4001/photo/122A2785-2_mCnWjLdb.jpg',
// width: 884,
// height: 884,
// },
// videoWeb: null,
// favorite: false,
// album: {
// __typename: 'Album',
// id: '523',
// title: 'another_album',
// },
// date: '2020-11-25T16:43:59Z',
// },
// ],
// },
// ],
// },
],
})
})

View File

@@ -1,9 +1,6 @@
import React from 'react'
import {
myTimeline_myTimeline,
myTimeline_myTimeline_media,
} from './__generated__/myTimeline'
import { TimelineGroup } from './TimelineGallery'
import { myTimeline_myTimeline } from './__generated__/myTimeline'
import { TimelineGroup, TimelineGroupAlbum } from './TimelineGallery'
import { GalleryAction } from '../photoGallery/photoGalleryReducer'
export interface TimelineMediaIndex {
@@ -30,18 +27,7 @@ export function timelineGalleryReducer(
): TimelineGalleryState {
switch (action.type) {
case 'replaceTimelineGroups': {
const dateGroupedAlbums = action.timeline.reduce((acc, val) => {
if (acc.length == 0 || acc[acc.length - 1].date != val.date) {
acc.push({
date: val.date,
groups: [val],
})
} else {
acc[acc.length - 1].groups.push(val)
}
return acc
}, [] as TimelineGroup[])
const timelineGroups = convertMediaToTimelineGroups(action.timeline)
return {
...state,
@@ -50,7 +36,7 @@ export function timelineGalleryReducer(
date: -1,
media: -1,
},
timelineGroups: dateGroupedAlbums,
timelineGroups,
}
}
case 'nextImage': {
@@ -64,7 +50,7 @@ export function timelineGalleryReducer(
return state
}
const albumGroups = timelineGroups[activeIndex.date].groups
const albumGroups = timelineGroups[activeIndex.date].albums
const albumMedia = albumGroups[activeIndex.album].media
if (activeIndex.media < albumMedia.length - 1) {
@@ -124,7 +110,7 @@ export function timelineGalleryReducer(
}
if (activeIndex.album > 0) {
const albumGroups = state.timelineGroups[activeIndex.date].groups
const albumGroups = state.timelineGroups[activeIndex.date].albums
const albumMedia = albumGroups[activeIndex.album - 1].media
return {
@@ -138,7 +124,7 @@ export function timelineGalleryReducer(
}
if (activeIndex.date > 0) {
const albumGroups = state.timelineGroups[activeIndex.date - 1].groups
const albumGroups = state.timelineGroups[activeIndex.date - 1].albums
const albumMedia = albumGroups[albumGroups.length - 1].media
return {
@@ -181,9 +167,9 @@ export const getTimelineImage = ({
}: {
mediaState: TimelineGalleryState
index: TimelineMediaIndex
}): myTimeline_myTimeline_media => {
}): myTimeline_myTimeline => {
const { date, album, media } = index
return mediaState.timelineGroups[date].groups[album].media[media]
return mediaState.timelineGroups[date].albums[album].media[media]
}
export const getActiveTimelineImage = ({
@@ -203,6 +189,65 @@ export const getActiveTimelineImage = ({
return getTimelineImage({ mediaState, index: mediaState.activeIndex })
}
function convertMediaToTimelineGroups(
timelineMedia: myTimeline_myTimeline[]
): TimelineGroup[] {
const timelineGroups: TimelineGroup[] = []
let albums: TimelineGroupAlbum[] = []
let nextAlbum: TimelineGroupAlbum | null = null
const sameDay = (a: string, b: string) => {
return (
a.replace(/\d{2}:\d{2}:\d{2}/, '00:00:00') ==
b.replace(/\d{2}:\d{2}:\d{2}/, '00:00:00')
)
}
for (const media of timelineMedia) {
if (nextAlbum == null) {
nextAlbum = {
id: media.album.id,
title: media.album.title,
media: [media],
}
continue
}
// if date changes
if (sameDay(nextAlbum.media[0].date, media.date)) {
albums.push(nextAlbum)
timelineGroups.push({
date: albums[0].media[0].date.replace(/\d{2}:\d{2}:\d{2}/, '00:00:00'),
albums: albums,
})
albums = []
nextAlbum = {
id: media.album.id,
title: media.album.title,
media: [media],
}
continue
}
// if album changes
if (nextAlbum.id != media.album.id) {
albums.push(nextAlbum)
nextAlbum = {
id: media.album.id,
title: media.album.title,
media: [media],
}
continue
}
// same album and date
nextAlbum.media.push(media)
}
return timelineGroups
}
export const openTimelinePresentMode = ({
dispatchMedia,
activeIndex,