Files
Arjun Patel d262f734f0 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
2026-05-18 12:44:31 -07:00

67 lines
1.9 KiB
Go

package pushnotify
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
)
// Service stores per-device Expo push tokens and exposes the operations
// needed by both the HTTP handlers and the worker-side notifier.
type Service interface {
// Register returns ErrInvalidToken / ErrInvalidPlatform on bad input.
Register(ctx context.Context, humanID string, in RegisterInput) error
// Unregister is scoped to humanID so a user can't delete another user's
// token. Returns ErrNotFound if the token isn't owned by humanID.
Unregister(ctx context.Context, humanID, token string) error
// ListForHumans returns an empty slice when nothing matches.
ListForHumans(ctx context.Context, humanIDs []string) ([]*PushToken, error)
// DeleteByToken removes a token regardless of owner — used to prune after
// Expo reports DeviceNotRegistered.
DeleteByToken(ctx context.Context, token string) error
}
type RegisterInput struct {
Token string
Platform Platform
AppVersion string
}
type serviceImpl struct {
repo repository
}
func NewService(pool *pgxpool.Pool) Service {
return &serviceImpl{repo: newRepository(pool)}
}
func (s *serviceImpl) Register(ctx context.Context, humanID string, in RegisterInput) error {
if !in.Platform.Valid() {
return ErrInvalidPlatform
}
if !IsValidExpoToken(in.Token) {
return ErrInvalidToken
}
return s.repo.upsert(ctx, &PushToken{
Token: in.Token,
HumanID: humanID,
Platform: in.Platform,
AppVersion: in.AppVersion,
})
}
func (s *serviceImpl) Unregister(ctx context.Context, humanID, token string) error {
if token == "" {
return ErrInvalidToken
}
return s.repo.deleteForHuman(ctx, humanID, token)
}
func (s *serviceImpl) ListForHumans(ctx context.Context, humanIDs []string) ([]*PushToken, error) {
return s.repo.listForHumans(ctx, humanIDs)
}
func (s *serviceImpl) DeleteByToken(ctx context.Context, token string) error {
return s.repo.deleteByToken(ctx, token)
}