1
Fork 0

Improve notifications

This commit is contained in:
viktorstrate 2020-02-26 19:44:47 +01:00
parent ecd1447deb
commit 440814564c
6 changed files with 112 additions and 7 deletions

View File

@ -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))
}

View File

@ -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

View File

@ -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 {

View File

@ -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)

View File

@ -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: &notifyTimeout,
})
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")

View File

@ -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
)