Mobile notifications for iOS (#210)
* mobile: wire notification registration and listener
* implement backend components for push notifications
* refactor: agentic comment cleanup
* docs: use proper module name for particle processor
* set required env variables for push notifications
* bump version
* fix: always upsert push token on mobile start
* Revert "fix: always upsert push token on mobile start"
This reverts commit 90ff18a788.
* send push notifications regardless of online status
This commit was merged in pull request #210.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
package pushnotify
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type repository interface {
|
||||
upsert(ctx context.Context, t *PushToken) error
|
||||
deleteForHuman(ctx context.Context, humanID, token string) error
|
||||
deleteByToken(ctx context.Context, token string) error
|
||||
listForHumans(ctx context.Context, humanIDs []string) ([]*PushToken, error)
|
||||
}
|
||||
|
||||
type repositoryImpl struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func newRepository(pool *pgxpool.Pool) repository {
|
||||
return &repositoryImpl{pool: pool}
|
||||
}
|
||||
|
||||
func (r *repositoryImpl) upsert(ctx context.Context, t *PushToken) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`INSERT INTO push_tokens (token, human_id, platform, app_version)
|
||||
VALUES ($1, $2, $3, NULLIF($4, ''))
|
||||
ON CONFLICT (token) DO UPDATE SET
|
||||
human_id = EXCLUDED.human_id,
|
||||
platform = EXCLUDED.platform,
|
||||
app_version = EXCLUDED.app_version,
|
||||
last_seen_at = NOW()`,
|
||||
t.Token, t.HumanID, string(t.Platform), t.AppVersion,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *repositoryImpl) deleteForHuman(ctx context.Context, humanID, token string) error {
|
||||
result, err := r.pool.Exec(ctx,
|
||||
`DELETE FROM push_tokens WHERE human_id = $1 AND token = $2`,
|
||||
humanID, token,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.RowsAffected() == 0 {
|
||||
return ErrNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *repositoryImpl) deleteByToken(ctx context.Context, token string) error {
|
||||
_, err := r.pool.Exec(ctx,
|
||||
`DELETE FROM push_tokens WHERE token = $1`,
|
||||
token,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *repositoryImpl) listForHumans(ctx context.Context, humanIDs []string) ([]*PushToken, error) {
|
||||
if len(humanIDs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
rows, err := r.pool.Query(ctx,
|
||||
`SELECT token, human_id, platform, app_version, created_at, last_seen_at
|
||||
FROM push_tokens
|
||||
WHERE human_id = ANY($1)`,
|
||||
humanIDs,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var tokens []*PushToken
|
||||
for rows.Next() {
|
||||
var t PushToken
|
||||
var appVersion *string
|
||||
var platform string
|
||||
if err := rows.Scan(&t.Token, &t.HumanID, &platform, &appVersion, &t.CreatedAt, &t.LastSeenAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.Platform = Platform(platform)
|
||||
if appVersion != nil {
|
||||
t.AppVersion = *appVersion
|
||||
}
|
||||
tokens = append(tokens, &t)
|
||||
}
|
||||
return tokens, rows.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user