Improve notifications
This commit is contained in:
parent
ecd1447deb
commit
440814564c
|
@ -90,6 +90,7 @@ type ComplexityRoot struct {
|
|||
Negative func(childComplexity int) int
|
||||
Positive func(childComplexity int) int
|
||||
Progress func(childComplexity int) int
|
||||
Timeout func(childComplexity int) int
|
||||
Type func(childComplexity int) int
|
||||
}
|
||||
|
||||
|
@ -504,6 +505,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Notification.Progress(childComplexity), true
|
||||
|
||||
case "Notification.timeout":
|
||||
if e.complexity.Notification.Timeout == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Notification.Timeout(childComplexity), true
|
||||
|
||||
case "Notification.type":
|
||||
if e.complexity.Notification.Type == nil {
|
||||
break
|
||||
|
@ -1091,6 +1099,8 @@ type Subscription {
|
|||
enum NotificationType {
|
||||
Message
|
||||
Progress
|
||||
"Close a notification with a given key"
|
||||
Close
|
||||
}
|
||||
|
||||
type Notification {
|
||||
|
@ -1101,6 +1111,8 @@ type Notification {
|
|||
progress: Float
|
||||
positive: Boolean!
|
||||
negative: Boolean!
|
||||
"Time in milliseconds before the notification will close"
|
||||
timeout: Int
|
||||
}
|
||||
|
||||
type AuthorizeResult {
|
||||
|
@ -2921,6 +2933,40 @@ func (ec *executionContext) _Notification_negative(ctx context.Context, field gr
|
|||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Notification_timeout(ctx context.Context, field graphql.CollectedField, obj *models.Notification) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
ec.Tracer.EndFieldExecution(ctx)
|
||||
}()
|
||||
rctx := &graphql.ResolverContext{
|
||||
Object: "Notification",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
}
|
||||
ctx = graphql.WithResolverContext(ctx, rctx)
|
||||
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Timeout, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*int)
|
||||
rctx.Result = res
|
||||
ctx = ec.Tracer.StartFieldChildExecution(ctx)
|
||||
return ec.marshalOInt2ᚖint(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Photo_id(ctx context.Context, field graphql.CollectedField, obj *models.Photo) (ret graphql.Marshaler) {
|
||||
ctx = ec.Tracer.StartFieldExecution(ctx, field)
|
||||
defer func() {
|
||||
|
@ -6404,6 +6450,8 @@ func (ec *executionContext) _Notification(ctx context.Context, sel ast.Selection
|
|||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "timeout":
|
||||
out.Values[i] = ec._Notification_timeout(ctx, field, obj)
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ type Notification struct {
|
|||
Progress *float64 `json:"progress"`
|
||||
Positive bool `json:"positive"`
|
||||
Negative bool `json:"negative"`
|
||||
// Time in milliseconds before the notification will close
|
||||
Timeout *int `json:"timeout"`
|
||||
}
|
||||
|
||||
type PhotoDownload struct {
|
||||
|
@ -55,16 +57,19 @@ type NotificationType string
|
|||
const (
|
||||
NotificationTypeMessage NotificationType = "Message"
|
||||
NotificationTypeProgress NotificationType = "Progress"
|
||||
// Close a notification with a given key
|
||||
NotificationTypeClose NotificationType = "Close"
|
||||
)
|
||||
|
||||
var AllNotificationType = []NotificationType{
|
||||
NotificationTypeMessage,
|
||||
NotificationTypeProgress,
|
||||
NotificationTypeClose,
|
||||
}
|
||||
|
||||
func (e NotificationType) IsValid() bool {
|
||||
switch e {
|
||||
case NotificationTypeMessage, NotificationTypeProgress:
|
||||
case NotificationTypeMessage, NotificationTypeProgress, NotificationTypeClose:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -93,6 +93,8 @@ type Subscription {
|
|||
enum NotificationType {
|
||||
Message
|
||||
Progress
|
||||
"Close a notification with a given key"
|
||||
Close
|
||||
}
|
||||
|
||||
type Notification {
|
||||
|
@ -103,6 +105,8 @@ type Notification {
|
|||
progress: Float
|
||||
positive: Boolean!
|
||||
negative: Boolean!
|
||||
"Time in milliseconds before the notification will close"
|
||||
timeout: Int
|
||||
}
|
||||
|
||||
type AuthorizeResult {
|
||||
|
|
|
@ -79,6 +79,7 @@ func ScanUser(database *sql.DB, userId int) error {
|
|||
func scan(database *sql.DB, user *models.User) {
|
||||
|
||||
notifyKey := utils.GenerateToken()
|
||||
processKey := utils.GenerateToken()
|
||||
|
||||
notification.BroadcastNotification(&models.Notification{
|
||||
Key: notifyKey,
|
||||
|
@ -166,7 +167,7 @@ func scan(database *sql.DB, user *models.User) {
|
|||
continue
|
||||
}
|
||||
|
||||
if err := ScanPhoto(tx, photoPath, albumId, content_type); err != nil {
|
||||
if err := ScanPhoto(tx, photoPath, albumId, content_type, processKey); err != nil {
|
||||
ScannerError("processing image %s: %s", photoPath, err)
|
||||
tx.Rollback()
|
||||
continue
|
||||
|
@ -199,6 +200,11 @@ func scan(database *sql.DB, user *models.User) {
|
|||
Positive: true,
|
||||
})
|
||||
|
||||
notification.BroadcastNotification(&models.Notification{
|
||||
Key: processKey,
|
||||
Type: models.NotificationTypeClose,
|
||||
})
|
||||
|
||||
log.Println("Done scanning")
|
||||
}
|
||||
|
||||
|
@ -317,20 +323,27 @@ func cleanupCache(database *sql.DB, scanned_albums []interface{}, user *models.U
|
|||
var album_id int
|
||||
rows.Scan(&album_id)
|
||||
deleted_ids = append(deleted_ids, album_id)
|
||||
deleted_albums++
|
||||
cache_path := path.Join("./image-cache", strconv.Itoa(album_id))
|
||||
err := os.RemoveAll(cache_path)
|
||||
if err != nil {
|
||||
ScannerError("Could not delete unused cache folder: %s\n%s\n", cache_path, err)
|
||||
} else {
|
||||
deleted_albums++
|
||||
}
|
||||
}
|
||||
|
||||
if len(deleted_ids) > 0 {
|
||||
albums_questions = strings.Repeat("?,", len(deleted_ids))[:len(deleted_ids)*2-1]
|
||||
|
||||
if _, err := database.Exec("DELETE FROM album WHERE album_id IN ("+albums_questions+")", deleted_ids...); err != nil {
|
||||
log.Printf("ERROR: Could not delete old albums from database:\n%s\n", err)
|
||||
ScannerError("Could not delete old albums from database:\n%s\n", err)
|
||||
}
|
||||
|
||||
notification.BroadcastNotification(&models.Notification{
|
||||
Key: utils.GenerateToken(),
|
||||
Type: models.NotificationTypeMessage,
|
||||
Header: "Deleted old albums",
|
||||
Content: fmt.Sprintf("Deleted %d albums, that was not found", len(deleted_ids)),
|
||||
})
|
||||
}
|
||||
|
||||
log.Printf("Deleted %d unused albums from cache", deleted_albums)
|
||||
|
|
|
@ -2,12 +2,15 @@ package scanner
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/viktorstrate/photoview/api/graphql/models"
|
||||
"fmt"
|
||||
"log"
|
||||
"path"
|
||||
|
||||
"github.com/viktorstrate/photoview/api/graphql/models"
|
||||
"github.com/viktorstrate/photoview/api/graphql/notification"
|
||||
)
|
||||
|
||||
func ScanPhoto(tx *sql.Tx, photoPath string, albumId int, content_type *string) error {
|
||||
func ScanPhoto(tx *sql.Tx, photoPath string, albumId int, content_type *string, notificationKey string) error {
|
||||
|
||||
log.Printf("Scanning image: %s\n", photoPath)
|
||||
|
||||
|
@ -25,6 +28,15 @@ func ScanPhoto(tx *sql.Tx, photoPath string, albumId int, content_type *string)
|
|||
}
|
||||
}
|
||||
|
||||
notifyTimeout := 5000
|
||||
notification.BroadcastNotification(&models.Notification{
|
||||
Key: notificationKey,
|
||||
Type: models.NotificationTypeMessage,
|
||||
Header: "Scanning photo",
|
||||
Content: fmt.Sprintf("Scanning image at %s", photoPath),
|
||||
Timeout: ¬ifyTimeout,
|
||||
})
|
||||
|
||||
result, err := tx.Exec("INSERT INTO photo (title, path, album_id) VALUES (?, ?, ?)", photoName, photoPath, albumId)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: Could not insert photo into database")
|
||||
|
|
|
@ -13,10 +13,13 @@ const notificationSubscription = gql`
|
|||
progress
|
||||
positive
|
||||
negative
|
||||
timeout
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
let messageTimeoutHandles = new Map()
|
||||
|
||||
const SubscriptionsHook = ({ messages, setMessages }) => {
|
||||
if (!localStorage.getItem('token')) {
|
||||
return null
|
||||
|
@ -46,9 +49,15 @@ const SubscriptionsHook = ({ messages, setMessages }) => {
|
|||
|
||||
const msg = data.notification
|
||||
|
||||
if (msg.type == 'Close') {
|
||||
setMessages(messages => messages.filter(m => m.key != msg.key))
|
||||
return
|
||||
}
|
||||
|
||||
const newNotification = {
|
||||
key: msg.key,
|
||||
type: msg.type.toLowerCase(),
|
||||
timeout: msg.timeout,
|
||||
props: {
|
||||
header: msg.header,
|
||||
content: msg.content,
|
||||
|
@ -57,6 +66,20 @@ const SubscriptionsHook = ({ messages, setMessages }) => {
|
|||
},
|
||||
}
|
||||
|
||||
if (msg.timeout) {
|
||||
// Clear old timeout, to replace it with the new one
|
||||
if (messageTimeoutHandles.get(msg.key)) {
|
||||
const timeoutHandle = messageTimeoutHandles.get(msg.key)
|
||||
clearTimeout(timeoutHandle)
|
||||
}
|
||||
|
||||
const timeoutHandle = setTimeout(() => {
|
||||
setMessages(messages => messages.filter(m => m.key != msg.key))
|
||||
}, msg.timeout)
|
||||
|
||||
messageTimeoutHandles.set(msg.key, timeoutHandle)
|
||||
}
|
||||
|
||||
const notifyIndex = newMessages.findIndex(
|
||||
msg => msg.key == newNotification.key
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue