Documentation
¶
Overview ¶
Package push runs the account's client side of push notifications: device-token persistence + registration with the push node, the subscription sync loop (desired-topic reconcile → SubscribeAll full replace), and a buffered notify queue so HTTP handlers never block on the push server. Structural twin of internal/indexer — per-account, SDK-consuming, background-looping; constructed by the engine only when config.Push.Active().
The SDK owns crypto + transport (space.PushAPI); this package owns the account policy: WHICH topics (space-level and per-chat modes, topics.go), WHEN to re-sync, WHAT survives a restart (the token file), and the chat notify hooks the HTTP handlers call after send/edit/read (chatpush.go — heart-interoperable payloads).
Index ¶
- Constants
- type Attachment
- type CaptureFunc
- type NewMessagePayload
- type Payload
- type Service
- func (s *Service) CaptureNotifications(fn CaptureFunc)
- func (s *Service) ChatMentionsBefore(ctx context.Context, sp space.Space, objectId, msgId string) (mentions []string, ok bool)
- func (s *Service) Close() error
- func (s *Service) Enqueue(spaceId string, topics []string, payload []byte, groupId string, silent bool)
- func (s *Service) Kick()
- func (s *Service) NotifyChatEdit(sp space.Space, objectId, msgId string, before []string)
- func (s *Service) NotifyChatMessage(sp space.Space, objectId, msgId string)
- func (s *Service) NotifyChatRead(spaceId, objectId string)
- func (s *Service) RevokeToken(ctx context.Context) error
- func (s *Service) SetToken(ctx context.Context, platform space.PushPlatform, token string) error
- func (s *Service) Start(ctx context.Context)
- func (s *Service) Subscriptions(ctx context.Context) ([]space.PushSubscription, error)
- func (s *Service) TokenStatus() (registered bool, platform string)
- type Type
Constants ¶
const ( ModeAll = "all" ModeMentions = "mentions" ModeNone = "none" )
notifyMode values. Heart parity: All is the default (also the fallback for an absent or invalid value).
const SettingNotifyMode = "notifyMode"
SettingNotifyMode is the per-space settings key the sync loop reads (SpaceInfo.Settings["notifyMode"], written via PATCH /v1/spaces/:spaceId/settings).
const TopicChats = "chats"
TopicChats is the space-wide "all messages" topic (heart's chats topic). The account identity doubles as the bulk mentions topic.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Attachment ¶
type Attachment struct {
Layout int `json:"layout"`
}
Attachment mirrors heart's per-attachment stub. Layout is heart's resolvedLayout from object details; any's chat attachments are {type, link} hints with no layout notion, so it stays 0.
type CaptureFunc ¶
CaptureFunc receives one would-be-enqueued notification. Test seam; see CaptureNotifications.
type NewMessagePayload ¶
type NewMessagePayload struct {
ChatId string `json:"chatId"`
MsgId string `json:"msgId"`
SpaceName string `json:"spaceName"`
ChatName string `json:"chatName"`
SenderName string `json:"senderName"`
Text string `json:"text"`
HasAttachments bool `json:"hasAttachments"`
Attachments []*Attachment `json:"attachments"`
}
NewMessagePayload carries the renderable message preview.
type Payload ¶
type Payload struct {
SpaceId string `json:"spaceId,omitempty"`
// SpaceUxType / SpaceType are heart's model.SpaceUxType /
// model.SpaceType enums, filled by heartSpaceKinds from the space's
// type: the payload is the only thing a receiver that has never
// seen the space can classify on (a 1-1 renders as a direct
// message, not a channel). Unmapped space types stay 0 (unknown).
SpaceUxType int `json:"spaceUxType"`
SpaceType int `json:"spaceType"`
SenderId string `json:"senderId"`
Type Type `json:"type"`
NewMessagePayload *NewMessagePayload `json:"newMessage,omitempty"`
}
Payload is the outer (pre-encryption) notification body.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the per-account push service. New → Start → Close; all exported methods are safe for concurrent use. The chat handler hooks (NotifyChatMessage / NotifyChatEdit / NotifyChatRead in chatpush.go) feed Enqueue.
func New ¶
func New(sdk *anysyncsdk.SDK, accountDir string) *Service
New constructs the service. accountDir is the per-account data dir (wallet.key, server.pid, …) where push-token.json lives. Call Start to begin background work; the token/notify surfaces are inert but safe before that.
func (*Service) CaptureNotifications ¶
func (s *Service) CaptureNotifications(fn CaptureFunc)
CaptureNotifications diverts every subsequent Enqueue into fn instead of the delivery queue — the test seam for asserting the chat-hook outputs (topics / payload / groupId) without a reachable push node. Pass nil to restore normal delivery.
func (*Service) ChatMentionsBefore ¶
func (s *Service) ChatMentionsBefore(ctx context.Context, sp space.Space, objectId, msgId string) (mentions []string, ok bool)
ChatMentionsBefore snapshots a message's server-derived mentions — the chatEdit hook's "before" set. SYNCHRONOUS by necessity (it must read the pre-edit record, so the handler calls it before the edit lands); one local indexed read, so the handler cost is negligible. ok=false means the read failed and the edit push should be skipped entirely — without a before set the diff would over-notify every existing mention on every edit.
func (*Service) Close ¶
Close stops the loops. Best-effort teardown: an in-flight RPC finishes or context-cancels; queued notifications are dropped. Idempotent.
func (*Service) Enqueue ¶
func (s *Service) Enqueue(spaceId string, topics []string, payload []byte, groupId string, silent bool)
Enqueue schedules one notification for buffered async delivery (notifyAttempts × notifyRetryDelay, break early when the server reports no valid topics). silent routes through NotifySilent (own-devices wakeup; topics ignored). Never blocks: on queue overflow the notification is dropped with a warning — push is a best-effort side channel, the message itself is already synced. Fed by the chat handler hooks in chatpush.go.
func (*Service) Kick ¶
func (s *Service) Kick()
Kick schedules a subscription re-sync (non-blocking, coalesced). SetToken uses it so a fresh token gets its subscriptions immediately instead of waiting for the tick.
func (*Service) NotifyChatEdit ¶
NotifyChatEdit is the chatEdit hook: re-read the message, diff its mentions against the pre-edit snapshot, and push only to the NEWLY-ADDED mentions — bare identity + per-chat mention topics only, never the "chats" / per-chat broadcast topics (an edit is not a new message for the room; only a fresh ping notifies, decision 1 in docs/20-push.md § Triggers). Asynchronous and non-blocking.
func (*Service) NotifyChatMessage ¶
NotifyChatMessage is the chatSend hook: read the just-written message back (for the server-derived `mentions` + text), then enqueue a loud notification on the full topic superset —
chats space-wide "all" chats/<sha256hex(chatId)> per-chat "all" chats/<sha256hex(chatId)>/<mention> per-chat mention <mention> bare identity (bulk mentions)
groupId = sha256hex(chatId). Asynchronous and non-blocking — safe to call on the handler goroutine after chat.Send succeeds.
func (*Service) NotifyChatRead ¶
NotifyChatRead is the chatRead / chatReadAll hook: a SILENT notification with the chat's groupId (topics nil — the server targets the caller's own-identity topic on the silent path), so the account's other devices wake and refresh their badges. Heart hooks its read RPCs the same way. Non-blocking (plain Enqueue).
func (*Service) RevokeToken ¶
RevokeToken forwards the revoke (best-effort — the token file is removed locally regardless, so a device that logged out stays logged out even if the push node was unreachable at that moment) and deletes the persisted file.
func (*Service) SetToken ¶
SetToken persists the device token and forwards it to the push node. The persist is the durable part; the forward is bounded by forwardBudget and, on transient failure, retried in the background by the sync loop — so a slow/unreachable push node never fails the call. Only ErrPushNotConfigured (and local disk errors) surface.
func (*Service) Start ¶
Start loads the persisted device token, subscribes to space-list changes, and spawns the sync + notify loops. Non-blocking; a persisted token is re-registered by the first sync round in the background (never fails boot). The passed ctx bounds all background work — cancel it (or call Close) to stop.
func (*Service) Subscriptions ¶
Subscriptions returns the account's server-held topic set (raw {spaceKey, topic} rows, unsigned) — SDK passthrough.
func (*Service) TokenStatus ¶
TokenStatus reports the LOCAL registration state — whether a token file exists on this device and for which platform. It does not round-trip the push node.