feat!: due date reminder notifications
This commit is contained in:
@ -277,6 +277,7 @@ type ComplexityRoot struct {
|
||||
DuplicateTaskGroup func(childComplexity int, input DuplicateTaskGroup) int
|
||||
InviteProjectMembers func(childComplexity int, input InviteProjectMembers) int
|
||||
LogoutUser func(childComplexity int, input LogoutUser) int
|
||||
NotificationMarkAllRead func(childComplexity int) int
|
||||
NotificationToggleRead func(childComplexity int, input NotificationToggleReadInput) int
|
||||
RemoveTaskLabel func(childComplexity int, input *RemoveTaskLabelInput) int
|
||||
SetTaskChecklistItemComplete func(childComplexity int, input SetTaskChecklistItemComplete) int
|
||||
@ -333,6 +334,10 @@ type ComplexityRoot struct {
|
||||
Value func(childComplexity int) int
|
||||
}
|
||||
|
||||
NotificationMarkAllAsReadResult struct {
|
||||
Success func(childComplexity int) int
|
||||
}
|
||||
|
||||
Notified struct {
|
||||
ID func(childComplexity int) int
|
||||
Notification func(childComplexity int) int
|
||||
@ -617,6 +622,7 @@ type LabelColorResolver interface {
|
||||
}
|
||||
type MutationResolver interface {
|
||||
NotificationToggleRead(ctx context.Context, input NotificationToggleReadInput) (*Notified, error)
|
||||
NotificationMarkAllRead(ctx context.Context) (*NotificationMarkAllAsReadResult, error)
|
||||
CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*db.ProjectLabel, error)
|
||||
DeleteProjectLabel(ctx context.Context, input DeleteProjectLabel) (*db.ProjectLabel, error)
|
||||
UpdateProjectLabel(ctx context.Context, input UpdateProjectLabel) (*db.ProjectLabel, error)
|
||||
@ -1755,6 +1761,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.Mutation.LogoutUser(childComplexity, args["input"].(LogoutUser)), true
|
||||
|
||||
case "Mutation.notificationMarkAllRead":
|
||||
if e.complexity.Mutation.NotificationMarkAllRead == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.NotificationMarkAllRead(childComplexity), true
|
||||
|
||||
case "Mutation.notificationToggleRead":
|
||||
if e.complexity.Mutation.NotificationToggleRead == nil {
|
||||
break
|
||||
@ -2199,6 +2212,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.NotificationData.Value(childComplexity), true
|
||||
|
||||
case "NotificationMarkAllAsReadResult.success":
|
||||
if e.complexity.NotificationMarkAllAsReadResult.Success == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.NotificationMarkAllAsReadResult.Success(childComplexity), true
|
||||
|
||||
case "Notified.id":
|
||||
if e.complexity.Notified.ID == nil {
|
||||
break
|
||||
@ -3417,6 +3437,10 @@ extend type Query {
|
||||
|
||||
extend type Mutation {
|
||||
notificationToggleRead(input: NotificationToggleReadInput!): Notified!
|
||||
notificationMarkAllRead: NotificationMarkAllAsReadResult!
|
||||
}
|
||||
type NotificationMarkAllAsReadResult {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type HasUnreadNotificationsResult {
|
||||
@ -3452,6 +3476,7 @@ enum ActionType {
|
||||
DUE_DATE_ADDED
|
||||
DUE_DATE_REMOVED
|
||||
DUE_DATE_CHANGED
|
||||
DUE_DATE_REMINDER
|
||||
TASK_ASSIGNED
|
||||
TASK_MOVED
|
||||
TASK_ARCHIVED
|
||||
@ -8535,6 +8560,41 @@ func (ec *executionContext) _Mutation_notificationToggleRead(ctx context.Context
|
||||
return ec.marshalNNotified2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotified(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_notificationMarkAllRead(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Mutation",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
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 ec.resolvers.Mutation().NotificationMarkAllRead(rctx)
|
||||
})
|
||||
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.(*NotificationMarkAllAsReadResult)
|
||||
fc.Result = res
|
||||
return ec.marshalNNotificationMarkAllAsReadResult2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationMarkAllAsReadResult(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_createProjectLabel(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -13267,6 +13327,41 @@ func (ec *executionContext) _NotificationData_value(ctx context.Context, field g
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _NotificationMarkAllAsReadResult_success(ctx context.Context, field graphql.CollectedField, obj *NotificationMarkAllAsReadResult) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "NotificationMarkAllAsReadResult",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
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.Success, 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.(bool)
|
||||
fc.Result = res
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Notified_id(ctx context.Context, field graphql.CollectedField, obj *Notified) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -23074,6 +23169,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "notificationMarkAllRead":
|
||||
out.Values[i] = ec._Mutation_notificationMarkAllRead(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "createProjectLabel":
|
||||
out.Values[i] = ec._Mutation_createProjectLabel(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
@ -23580,6 +23680,33 @@ func (ec *executionContext) _NotificationData(ctx context.Context, sel ast.Selec
|
||||
return out
|
||||
}
|
||||
|
||||
var notificationMarkAllAsReadResultImplementors = []string{"NotificationMarkAllAsReadResult"}
|
||||
|
||||
func (ec *executionContext) _NotificationMarkAllAsReadResult(ctx context.Context, sel ast.SelectionSet, obj *NotificationMarkAllAsReadResult) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, notificationMarkAllAsReadResultImplementors)
|
||||
|
||||
out := graphql.NewFieldSet(fields)
|
||||
var invalids uint32
|
||||
for i, field := range fields {
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("NotificationMarkAllAsReadResult")
|
||||
case "success":
|
||||
out.Values[i] = ec._NotificationMarkAllAsReadResult_success(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
}
|
||||
out.Dispatch()
|
||||
if invalids > 0 {
|
||||
return graphql.Null
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var notifiedImplementors = []string{"Notified"}
|
||||
|
||||
func (ec *executionContext) _Notified(ctx context.Context, sel ast.SelectionSet, obj *Notified) graphql.Marshaler {
|
||||
@ -27106,6 +27233,20 @@ func (ec *executionContext) marshalNNotificationFilter2githubᚗcomᚋjordanknot
|
||||
return v
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNNotificationMarkAllAsReadResult2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationMarkAllAsReadResult(ctx context.Context, sel ast.SelectionSet, v NotificationMarkAllAsReadResult) graphql.Marshaler {
|
||||
return ec._NotificationMarkAllAsReadResult(ctx, sel, &v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNNotificationMarkAllAsReadResult2ᚖgithubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationMarkAllAsReadResult(ctx context.Context, sel ast.SelectionSet, v *NotificationMarkAllAsReadResult) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._NotificationMarkAllAsReadResult(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNNotificationToggleReadInput2githubᚗcomᚋjordanknottᚋtaskcafeᚋinternalᚋgraphᚐNotificationToggleReadInput(ctx context.Context, v interface{}) (NotificationToggleReadInput, error) {
|
||||
res, err := ec.unmarshalInputNotificationToggleReadInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
|
@ -17,27 +17,38 @@ import (
|
||||
"github.com/99designs/gqlgen/graphql/handler/lru"
|
||||
"github.com/99designs/gqlgen/graphql/handler/transport"
|
||||
"github.com/99designs/gqlgen/graphql/playground"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jordanknott/taskcafe/internal/config"
|
||||
"github.com/jordanknott/taskcafe/internal/db"
|
||||
"github.com/jordanknott/taskcafe/internal/jobs"
|
||||
"github.com/jordanknott/taskcafe/internal/logger"
|
||||
"github.com/jordanknott/taskcafe/internal/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
)
|
||||
|
||||
type NotificationObservers struct {
|
||||
Mu sync.Mutex
|
||||
Subscribers map[string]map[string]chan *Notified
|
||||
}
|
||||
|
||||
// NewHandler returns a new graphql endpoint handler.
|
||||
func NewHandler(repo db.Repository, appConfig config.AppConfig) http.Handler {
|
||||
c := Config{
|
||||
Resolvers: &Resolver{
|
||||
Repository: repo,
|
||||
AppConfig: appConfig,
|
||||
Notifications: NotificationObservers{
|
||||
Mu: sync.Mutex{},
|
||||
Subscribers: make(map[string]map[string]chan *Notified),
|
||||
},
|
||||
func NewHandler(repo db.Repository, appConfig config.AppConfig, jobQueue jobs.JobQueue, redisClient *redis.Client) http.Handler {
|
||||
resolver := &Resolver{
|
||||
Repository: repo,
|
||||
Redis: redisClient,
|
||||
AppConfig: appConfig,
|
||||
Job: jobQueue,
|
||||
Notifications: &NotificationObservers{
|
||||
Mu: sync.Mutex{},
|
||||
Subscribers: make(map[string]map[string]chan *Notified),
|
||||
},
|
||||
}
|
||||
resolver.SubscribeRedis()
|
||||
c := Config{
|
||||
Resolvers: resolver,
|
||||
}
|
||||
c.Directives.HasRole = func(ctx context.Context, obj interface{}, next graphql.Resolver, roles []RoleLevel, level ActionLevel, typeArg ObjectType) (interface{}, error) {
|
||||
userID, ok := GetUser(ctx)
|
||||
if !ok {
|
||||
|
@ -402,6 +402,10 @@ type NotificationData struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type NotificationMarkAllAsReadResult struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
type NotificationToggleReadInput struct {
|
||||
NotifiedID uuid.UUID `json:"notifiedID"`
|
||||
}
|
||||
@ -749,6 +753,7 @@ const (
|
||||
ActionTypeDueDateAdded ActionType = "DUE_DATE_ADDED"
|
||||
ActionTypeDueDateRemoved ActionType = "DUE_DATE_REMOVED"
|
||||
ActionTypeDueDateChanged ActionType = "DUE_DATE_CHANGED"
|
||||
ActionTypeDueDateReminder ActionType = "DUE_DATE_REMINDER"
|
||||
ActionTypeTaskAssigned ActionType = "TASK_ASSIGNED"
|
||||
ActionTypeTaskMoved ActionType = "TASK_MOVED"
|
||||
ActionTypeTaskArchived ActionType = "TASK_ARCHIVED"
|
||||
@ -766,6 +771,7 @@ var AllActionType = []ActionType{
|
||||
ActionTypeDueDateAdded,
|
||||
ActionTypeDueDateRemoved,
|
||||
ActionTypeDueDateChanged,
|
||||
ActionTypeDueDateReminder,
|
||||
ActionTypeTaskAssigned,
|
||||
ActionTypeTaskMoved,
|
||||
ActionTypeTaskArchived,
|
||||
@ -776,7 +782,7 @@ var AllActionType = []ActionType{
|
||||
|
||||
func (e ActionType) IsValid() bool {
|
||||
switch e {
|
||||
case ActionTypeTeamAdded, ActionTypeTeamRemoved, ActionTypeProjectAdded, ActionTypeProjectRemoved, ActionTypeProjectArchived, ActionTypeDueDateAdded, ActionTypeDueDateRemoved, ActionTypeDueDateChanged, ActionTypeTaskAssigned, ActionTypeTaskMoved, ActionTypeTaskArchived, ActionTypeTaskAttachmentUploaded, ActionTypeCommentMentioned, ActionTypeCommentOther:
|
||||
case ActionTypeTeamAdded, ActionTypeTeamRemoved, ActionTypeProjectAdded, ActionTypeProjectRemoved, ActionTypeProjectArchived, ActionTypeDueDateAdded, ActionTypeDueDateRemoved, ActionTypeDueDateChanged, ActionTypeDueDateReminder, ActionTypeTaskAssigned, ActionTypeTaskMoved, ActionTypeTaskArchived, ActionTypeTaskAttachmentUploaded, ActionTypeCommentMentioned, ActionTypeCommentOther:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -70,6 +70,19 @@ func (r *mutationResolver) NotificationToggleRead(ctx context.Context, input Not
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) NotificationMarkAllRead(ctx context.Context) (*NotificationMarkAllAsReadResult, error) {
|
||||
userID, ok := GetUserID(ctx)
|
||||
if !ok {
|
||||
return &NotificationMarkAllAsReadResult{}, errors.New("invalid user ID")
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
err := r.Repository.MarkAllNotificationsRead(ctx, db.MarkAllNotificationsReadParams{UserID: userID, ReadAt: sql.NullTime{Valid: true, Time: now}})
|
||||
if err != nil {
|
||||
return &NotificationMarkAllAsReadResult{}, err
|
||||
}
|
||||
return &NotificationMarkAllAsReadResult{Success: false}, nil
|
||||
}
|
||||
|
||||
func (r *notificationResolver) ID(ctx context.Context, obj *db.Notification) (uuid.UUID, error) {
|
||||
return obj.NotificationID, nil
|
||||
}
|
||||
|
@ -4,20 +4,67 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jordanknott/taskcafe/internal/config"
|
||||
"github.com/jordanknott/taskcafe/internal/db"
|
||||
"github.com/jordanknott/taskcafe/internal/jobs"
|
||||
"github.com/jordanknott/taskcafe/internal/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type NotificationObservers struct {
|
||||
Subscribers map[string]map[string]chan *Notified
|
||||
Mu sync.Mutex
|
||||
}
|
||||
|
||||
// Resolver handles resolving GraphQL queries & mutations
|
||||
type Resolver struct {
|
||||
Repository db.Repository
|
||||
AppConfig config.AppConfig
|
||||
Notifications NotificationObservers
|
||||
Notifications *NotificationObservers
|
||||
Job jobs.JobQueue
|
||||
Redis *redis.Client
|
||||
}
|
||||
|
||||
func (r Resolver) SubscribeRedis() {
|
||||
ctx := context.Background()
|
||||
go func() {
|
||||
subscriber := r.Redis.Subscribe(ctx, "notification-created")
|
||||
log.Info("Stream starting...")
|
||||
for {
|
||||
|
||||
msg, err := subscriber.ReceiveMessage(ctx)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("while receiving message")
|
||||
panic(err)
|
||||
}
|
||||
var message utils.NotificationCreatedMessage
|
||||
|
||||
if err := json.Unmarshal([]byte(msg.Payload), &message); err != nil {
|
||||
log.WithError(err).Error("while unmarshalling notifiction created message")
|
||||
panic(err)
|
||||
}
|
||||
log.WithField("notID", message.NotifiedID).Info("received notification message")
|
||||
|
||||
notified, err := r.Repository.GetNotifiedByIDNoExtra(ctx, uuid.MustParse(message.NotifiedID))
|
||||
if err != nil {
|
||||
log.WithError(err).Error("while getting notified by id")
|
||||
panic(err)
|
||||
}
|
||||
notification, err := r.Repository.GetNotificationByID(ctx, uuid.MustParse(message.NotificationID))
|
||||
if err != nil {
|
||||
log.WithError(err).Error("while getting notified by id")
|
||||
panic(err)
|
||||
}
|
||||
for _, observers := range r.Notifications.Subscribers {
|
||||
for _, ochan := range observers {
|
||||
ochan <- &Notified{
|
||||
ID: notified.NotifiedID,
|
||||
Read: notified.Read,
|
||||
ReadAt: ¬ified.ReadAt.Time,
|
||||
Notification: ¬ification,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -10,6 +10,10 @@ extend type Query {
|
||||
|
||||
extend type Mutation {
|
||||
notificationToggleRead(input: NotificationToggleReadInput!): Notified!
|
||||
notificationMarkAllRead: NotificationMarkAllAsReadResult!
|
||||
}
|
||||
type NotificationMarkAllAsReadResult {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type HasUnreadNotificationsResult {
|
||||
@ -45,6 +49,7 @@ enum ActionType {
|
||||
DUE_DATE_ADDED
|
||||
DUE_DATE_REMOVED
|
||||
DUE_DATE_CHANGED
|
||||
DUE_DATE_REMINDER
|
||||
TASK_ASSIGNED
|
||||
TASK_MOVED
|
||||
TASK_ARCHIVED
|
||||
|
@ -10,6 +10,10 @@ extend type Query {
|
||||
|
||||
extend type Mutation {
|
||||
notificationToggleRead(input: NotificationToggleReadInput!): Notified!
|
||||
notificationMarkAllRead: NotificationMarkAllAsReadResult!
|
||||
}
|
||||
type NotificationMarkAllAsReadResult {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type HasUnreadNotificationsResult {
|
||||
@ -45,6 +49,7 @@ enum ActionType {
|
||||
DUE_DATE_ADDED
|
||||
DUE_DATE_REMOVED
|
||||
DUE_DATE_CHANGED
|
||||
DUE_DATE_REMINDER
|
||||
TASK_ASSIGNED
|
||||
TASK_MOVED
|
||||
TASK_ARCHIVED
|
||||
|
@ -11,7 +11,10 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
mTasks "github.com/RichardKnop/machinery/v1/tasks"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jinzhu/now"
|
||||
"github.com/jordanknott/taskcafe/internal/db"
|
||||
"github.com/jordanknott/taskcafe/internal/logger"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -539,6 +542,42 @@ func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTa
|
||||
DueDate: dueDate,
|
||||
HasTime: input.HasTime,
|
||||
})
|
||||
reminders, err := r.Repository.GetDueDateRemindersForTaskID(ctx, input.TaskID)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while getting due date reminders for task ID")
|
||||
return &db.Task{}, err
|
||||
}
|
||||
if input.DueDate != nil {
|
||||
for _, rem := range reminders {
|
||||
remindAt := now.With(*input.DueDate).BeginningOfDay()
|
||||
if input.HasTime {
|
||||
remindAt = *input.DueDate
|
||||
}
|
||||
switch rem.Duration {
|
||||
case "MINUTE":
|
||||
remindAt = remindAt.Add(time.Duration(-rem.Period) * time.Minute)
|
||||
break
|
||||
case "HOUR":
|
||||
remindAt = remindAt.Add(time.Duration(-rem.Period) * time.Hour)
|
||||
break
|
||||
case "DAY":
|
||||
remindAt = remindAt.AddDate(0, 0, int(-rem.Period))
|
||||
break
|
||||
case "WEEK":
|
||||
remindAt = remindAt.AddDate(0, 0, 7*int(-rem.Period))
|
||||
break
|
||||
}
|
||||
|
||||
_, err := r.Repository.UpdateDueDateReminderRemindAt(ctx, db.UpdateDueDateReminderRemindAtParams{
|
||||
DueDateReminderID: rem.DueDateReminderID,
|
||||
RemindAt: remindAt,
|
||||
})
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while updating due date reminder remind at")
|
||||
return &db.Task{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
createdAt := time.Now().UTC()
|
||||
d, _ := json.Marshal(data)
|
||||
if !isSame {
|
||||
@ -686,12 +725,55 @@ func (r *mutationResolver) UnassignTask(ctx context.Context, input *UnassignTask
|
||||
|
||||
func (r *mutationResolver) CreateTaskDueDateNotifications(ctx context.Context, input []CreateTaskDueDateNotification) (*CreateTaskDueDateNotificationsResult, error) {
|
||||
reminders := []DueDateNotification{}
|
||||
if len(input) == 0 {
|
||||
return &CreateTaskDueDateNotificationsResult{}, nil
|
||||
}
|
||||
task, err := r.Repository.GetTaskByID(ctx, input[0].TaskID)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while getting task by id")
|
||||
return &CreateTaskDueDateNotificationsResult{}, nil
|
||||
}
|
||||
for _, in := range input {
|
||||
remindAt := now.With(task.DueDate.Time).BeginningOfDay()
|
||||
if task.HasTime {
|
||||
remindAt = task.DueDate.Time
|
||||
}
|
||||
switch in.Duration {
|
||||
case "MINUTE":
|
||||
remindAt = remindAt.Add(time.Duration(-in.Period) * time.Minute)
|
||||
break
|
||||
case "HOUR":
|
||||
remindAt = remindAt.Add(time.Duration(-in.Period) * time.Hour)
|
||||
break
|
||||
case "DAY":
|
||||
remindAt = remindAt.AddDate(0, 0, int(-in.Period))
|
||||
break
|
||||
case "WEEK":
|
||||
remindAt = remindAt.AddDate(0, 0, 7*int(-in.Period))
|
||||
break
|
||||
}
|
||||
|
||||
log.Info("task not found, sending task")
|
||||
|
||||
n, err := r.Repository.CreateDueDateReminder(ctx, db.CreateDueDateReminderParams{
|
||||
TaskID: in.TaskID,
|
||||
Period: int32(in.Period),
|
||||
Duration: in.Duration.String(),
|
||||
RemindAt: remindAt,
|
||||
})
|
||||
signature := &mTasks.Signature{
|
||||
UUID: "due_date_reminder_" + n.DueDateReminderID.String(),
|
||||
Name: "dueDateNotification",
|
||||
ETA: &remindAt,
|
||||
Args: []mTasks.Arg{{
|
||||
Type: "string",
|
||||
Value: n.DueDateReminderID.String(),
|
||||
}, {
|
||||
Type: "string",
|
||||
Value: in.TaskID.String(),
|
||||
}},
|
||||
}
|
||||
r.Job.Server.SendTask(signature)
|
||||
if err != nil {
|
||||
return &CreateTaskDueDateNotificationsResult{}, err
|
||||
}
|
||||
@ -713,15 +795,71 @@ func (r *mutationResolver) CreateTaskDueDateNotifications(ctx context.Context, i
|
||||
|
||||
func (r *mutationResolver) UpdateTaskDueDateNotifications(ctx context.Context, input []UpdateTaskDueDateNotification) (*UpdateTaskDueDateNotificationsResult, error) {
|
||||
reminders := []DueDateNotification{}
|
||||
if len(input) == 0 {
|
||||
return &UpdateTaskDueDateNotificationsResult{}, nil
|
||||
}
|
||||
for _, in := range input {
|
||||
task, err := r.Repository.GetTaskForDueDateReminder(ctx, in.ID)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while getting task by id")
|
||||
return &UpdateTaskDueDateNotificationsResult{}, nil
|
||||
}
|
||||
current, err := r.Repository.GetDueDateReminderByID(ctx, in.ID)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while getting task by id")
|
||||
return &UpdateTaskDueDateNotificationsResult{}, nil
|
||||
}
|
||||
|
||||
remindAt := now.With(task.DueDate.Time).BeginningOfDay()
|
||||
if task.HasTime {
|
||||
remindAt = task.DueDate.Time
|
||||
}
|
||||
switch in.Duration {
|
||||
case "MINUTE":
|
||||
remindAt = remindAt.Add(time.Duration(-in.Period) * time.Minute)
|
||||
break
|
||||
case "HOUR":
|
||||
remindAt = remindAt.Add(time.Duration(-in.Period) * time.Hour)
|
||||
break
|
||||
case "DAY":
|
||||
remindAt = remindAt.AddDate(0, 0, int(-in.Period))
|
||||
break
|
||||
case "WEEK":
|
||||
remindAt = remindAt.AddDate(0, 0, 7*int(-in.Period))
|
||||
break
|
||||
}
|
||||
|
||||
n, err := r.Repository.UpdateDueDateReminder(ctx, db.UpdateDueDateReminderParams{
|
||||
DueDateReminderID: in.ID,
|
||||
Period: int32(in.Period),
|
||||
Duration: in.Duration.String(),
|
||||
RemindAt: remindAt,
|
||||
})
|
||||
if err != nil {
|
||||
return &UpdateTaskDueDateNotificationsResult{}, err
|
||||
}
|
||||
etaNano := strconv.FormatInt(current.RemindAt.UnixNano(), 10)
|
||||
result, err := r.Redis.ZRangeByScore(ctx, "delayed_tasks", &redis.ZRangeBy{Max: etaNano, Min: etaNano}).Result()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while getting due date reminder")
|
||||
}
|
||||
log.WithField("result", result).Info("result raw")
|
||||
if len(result) != 0 {
|
||||
r.Redis.ZRem(ctx, "delayed_tasks", result)
|
||||
}
|
||||
signature := &mTasks.Signature{
|
||||
UUID: "due_date_reminder_" + n.DueDateReminderID.String(),
|
||||
Name: "dueDateNotification",
|
||||
ETA: &remindAt,
|
||||
Args: []mTasks.Arg{{
|
||||
Type: "string",
|
||||
Value: n.DueDateReminderID.String(),
|
||||
}, {
|
||||
Type: "string",
|
||||
Value: task.TaskID.String(),
|
||||
}},
|
||||
}
|
||||
r.Job.Server.SendTask(signature)
|
||||
duration := DueDateNotificationDuration(n.Duration)
|
||||
if !duration.IsValid() {
|
||||
log.WithField("duration", n.Duration).Error("invalid duration found")
|
||||
@ -741,11 +879,21 @@ func (r *mutationResolver) UpdateTaskDueDateNotifications(ctx context.Context, i
|
||||
func (r *mutationResolver) DeleteTaskDueDateNotifications(ctx context.Context, input []DeleteTaskDueDateNotification) (*DeleteTaskDueDateNotificationsResult, error) {
|
||||
ids := []uuid.UUID{}
|
||||
for _, n := range input {
|
||||
err := r.Repository.DeleteDueDateReminder(ctx, n.ID)
|
||||
reminder, err := r.Repository.GetDueDateReminderByID(ctx, n.ID)
|
||||
err = r.Repository.DeleteDueDateReminder(ctx, n.ID)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while deleting task due date notification")
|
||||
return &DeleteTaskDueDateNotificationsResult{}, err
|
||||
}
|
||||
etaNano := strconv.FormatInt(reminder.RemindAt.UnixNano(), 10)
|
||||
result, err := r.Redis.ZRangeByScore(ctx, "delayed_tasks", &redis.ZRangeBy{Max: etaNano, Min: etaNano}).Result()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error while getting due date reminder")
|
||||
}
|
||||
log.WithField("result", result).Info("result raw")
|
||||
if len(result) != 0 {
|
||||
r.Redis.ZRem(ctx, "delayed_tasks", result)
|
||||
}
|
||||
ids = append(ids, n.ID)
|
||||
}
|
||||
return &DeleteTaskDueDateNotificationsResult{Notifications: ids}, nil
|
||||
|
Reference in New Issue
Block a user