@@ -3,12 +3,14 @@ package push
33import (
44 "context"
55 "slices"
6+ "strings"
67
78 "github.com/sirupsen/logrus"
89
910 "github.com/code-payments/code-server/pkg/code/common"
1011 code_data "github.com/code-payments/code-server/pkg/code/data"
1112 push_data "github.com/code-payments/code-server/pkg/code/data/push"
13+ "github.com/code-payments/code-server/pkg/pointer"
1214 push_lib "github.com/code-payments/code-server/pkg/push"
1315)
1416
@@ -105,38 +107,72 @@ func sendMutableNotificationToOwner(
105107 }
106108
107109 log .WithField ("tokens" , pushTokenRecords ).Info ("Found push tokens" )
110+
111+ // We reverse sort the tokens based on creation date to prioritze
112+ // recenty tokens. Furthermore, this means that in the case of a
113+ // detected dedupe, we use the most recent token.
108114 slices .SortFunc (pushTokenRecords , func (a , b * push_data.Record ) int {
109- return a .CreatedAt .Compare (b .CreatedAt )
115+ return b .CreatedAt .Compare (a .CreatedAt )
110116 })
111117
112- pushTokenRecord := pushTokenRecords [ len ( pushTokenRecords ) - 1 ]
113- log = log . WithField ( "push_token" , pushTokenRecord . PushToken )
118+ pushedTokens := make ( map [ string ] struct {})
119+ pushedDevices := make ( map [ string ] struct {} )
114120
115- // Try push
116- switch pushTokenRecord .TokenType {
117- case push_data .TokenTypeFcmApns :
118- log .Info ("Sending mutable push" )
119- err = pusher .SendMutableAPNSPush (
120- ctx ,
121- pushTokenRecord .PushToken ,
122- titleKey ,
123- string (notificationType ),
124- titleKey , // All mutable pushes have a thread ID that's the title
125- kvs ,
126- )
127- case push_data .TokenTypeFcmAndroid :
128- // todo: anything special required for Android?
129- log .Info ("Sending data push" )
130- err = pusher .SendDataPush (
131- ctx ,
132- pushTokenRecord .PushToken ,
133- kvs ,
134- )
135- }
121+ for _ , pushTokenRecord := range pushTokenRecords {
122+ if _ , ok := pushedTokens [pushTokenRecord .PushToken ]; ok {
123+ continue
124+ }
136125
137- if err != nil {
138- log .WithError (err ).Warn ("failure sending push notification" )
139- onPushError (ctx , data , pusher , pushTokenRecord )
126+ // In the case of apple, we attempt to dedupe based on app install id
127+ var appInstallId string
128+ if pushTokenRecord .TokenType == push_data .TokenTypeFcmApns {
129+ appInstallId = * pointer .StringOrDefault (pushTokenRecord .AppInstallId , "" )
130+ if appInstallId == "" {
131+ // Legacy token: Attempt to take it from the token itself
132+ parts := strings .Split (pushTokenRecord .PushToken , ":" )
133+ if len (parts ) == 2 {
134+ appInstallId = parts [0 ]
135+ }
136+ }
137+
138+ if appInstallId != "" {
139+ if _ , ok := pushedDevices [appInstallId ]; ok {
140+ continue
141+ }
142+ }
143+ }
144+
145+ // Try push
146+ switch pushTokenRecord .TokenType {
147+ case push_data .TokenTypeFcmApns :
148+ log .Info ("Sending mutable push" )
149+ err = pusher .SendMutableAPNSPush (
150+ ctx ,
151+ pushTokenRecord .PushToken ,
152+ titleKey ,
153+ string (notificationType ),
154+ titleKey , // All mutable pushes have a thread ID that's the title
155+ kvs ,
156+ )
157+ case push_data .TokenTypeFcmAndroid :
158+ // todo: anything special required for Android?
159+ log .Info ("Sending data push" )
160+ err = pusher .SendDataPush (
161+ ctx ,
162+ pushTokenRecord .PushToken ,
163+ kvs ,
164+ )
165+ }
166+
167+ if err != nil {
168+ log .WithError (err ).Warn ("failure sending push notification" )
169+ onPushError (ctx , data , pusher , pushTokenRecord )
170+ } else {
171+ pushedTokens [pushTokenRecord .PushToken ] = struct {}{}
172+ if appInstallId != "" {
173+ pushedDevices [appInstallId ] = struct {}{}
174+ }
175+ }
140176 }
141177
142178 return nil
0 commit comments