Skip to content

Commit c677929

Browse files
authored
Add Notifications service (#11)
* Add `Notifications` service * Fix missing export
1 parent a9d2ef3 commit c677929

File tree

3 files changed

+306
-1
lines changed

3 files changed

+306
-1
lines changed

src/AGS/Service/Mpris.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const disconnectMpris =
1010

1111
export const connectMpris =
1212
signal => callback => () =>
13-
M.connect(signal, callback)
13+
M.connect(signal, (_, ...args) => callback(...args))
1414

1515
export const players =
1616
() =>

src/AGS/Service/Notifications.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
const N = await Service.import('notifications')
2+
3+
// Notifications signals
4+
5+
export const connectNotifications =
6+
signal => callback => () =>
7+
N.connect(signal, (_, ...args) => callback(...args))
8+
9+
export const disconnectNotifications =
10+
handlerID => () =>
11+
N.disconnect(handlerID)
12+
13+
// Notifications bindings and props
14+
15+
export const bindNotifications =
16+
prop => () =>
17+
N.bind(prop)
18+
19+
export const getOptionsImpl =
20+
ks => () =>
21+
Object.fromEntries(ks.map(k => [k, N[k]]))
22+
23+
export const setOptionsImpl =
24+
opts => () =>
25+
Object.entries(opts).forEach(([k, v]) => N[k] = v)
26+
27+
export const notifications =
28+
() =>
29+
N.notifications
30+
31+
export const popups =
32+
() =>
33+
N.popups
34+
35+
// Notifications methods
36+
37+
export const clear = N.clear
38+
export const getNotificationImpl = N.getNotification
39+
export const getPopupImpl = N.getPopup
40+
41+
// Notification methods
42+
43+
export const dismiss =
44+
n => () =>
45+
n.dismiss()
46+
47+
export const close =
48+
n => () =>
49+
n.close()
50+
51+
export const invoke =
52+
n => action => () =>
53+
n.invoke(action.id)
54+

src/AGS/Service/Notifications.purs

+251
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
module AGS.Service.Notifications
2+
( Notifications
3+
, Notification
4+
, NotificationID(..)
5+
, NotificationsOptions
6+
, NotificationRecord
7+
, NotificationPropsF
8+
, Action
9+
, ActionID(..)
10+
, ActionLabel(..)
11+
, Urgency(..)
12+
, notifications
13+
, popups
14+
, getOptions
15+
, setOptions
16+
, getNotification
17+
, getPopup
18+
, fromNotification
19+
, dismiss
20+
, close
21+
, invoke
22+
, fromAction
23+
, disconnectNotifications
24+
) where
25+
26+
import Prelude
27+
28+
import AGS.Binding (Binding)
29+
import AGS.Service (class BindServiceProp, class ServiceConnect, Service)
30+
import AGS.Widget (EffectFn1)
31+
import Data.DateTime.Instant (Instant, instant)
32+
import Data.Enum (class Enum)
33+
import Data.Enum.Generic (genericPred, genericSucc)
34+
import Data.Generic.Rep (class Generic)
35+
import Data.Maybe (Maybe, fromJust)
36+
import Data.Newtype (class Newtype)
37+
import Data.Nullable (Nullable, toMaybe)
38+
import Data.Show.Generic (genericShow)
39+
import Data.Time.Duration (Milliseconds(..))
40+
import Effect (Effect)
41+
import Effect.Aff.Compat (runEffectFn1)
42+
import Effect.Uncurried (EffectFn2)
43+
import GObject
44+
( class GObjectSignal
45+
, HandlerID
46+
, unsafeConnect
47+
, unsafeCopyGObjectProps
48+
)
49+
import Partial.Unsafe (unsafePartial)
50+
import Record as Record
51+
import Record.Studio as RS
52+
import Type.Proxy (Proxy(..))
53+
import Unsafe.Coerce (unsafeCoerce)
54+
import Untagged.Union (UndefinedOr, uorToMaybe)
55+
56+
-- *** Notifications
57+
58+
foreign import data NotificationsService
59+
60+
-- * Signals
61+
62+
instance
63+
ServiceConnect Notifications "changed" (Effect Unit)
64+
where
65+
connectService = connectNotifications "changed"
66+
67+
instance
68+
ServiceConnect Notifications "dismissed" (EffectFn1 NotificationID Unit)
69+
where
70+
connectService = connectNotifications "dismissed"
71+
72+
instance
73+
ServiceConnect Notifications "notified" (EffectFn1 NotificationID Unit)
74+
where
75+
connectService = connectNotifications "notified"
76+
77+
instance
78+
ServiceConnect Notifications "closed" (EffectFn1 NotificationID Unit)
79+
where
80+
connectService = connectNotifications "closed"
81+
82+
foreign import disconnectNotifications
83+
HandlerID Notifications Effect Unit
84+
85+
foreign import connectNotifications
86+
f. String f Effect (HandlerID Notifications)
87+
88+
-- * Bindings and props
89+
90+
foreign import notifications Effect (Array Notification)
91+
foreign import popups Effect (Array Notification)
92+
93+
instance BindServiceProp Notifications "popups" (Array Notification) where
94+
bindServiceProp = bindNotifications "popups"
95+
96+
instance BindServiceProp Notifications "notifications" (Array Notification) where
97+
bindServiceProp = bindNotifications "notifications"
98+
99+
instance BindServiceProp Notifications "doNotDisturb" Boolean where
100+
bindServiceProp = bindNotifications "dnd"
101+
102+
foreign import bindNotifications a. String Effect (Binding a)
103+
104+
-- * Methods
105+
106+
getOptions Effect { | NotificationsOptions }
107+
getOptions = getOptionsImpl (RS.keys (Proxy @NotificationsOptions))
108+
109+
setOptions
110+
( { | NotificationsOptions }
111+
{ | NotificationsOptions }
112+
)
113+
Effect Unit
114+
setOptions f = setOptionsImpl <<< f =<< getOptions
115+
116+
foreign import setOptionsImpl { | NotificationsOptions } Effect Unit
117+
foreign import getOptionsImpl Array String Effect { | NotificationsOptions }
118+
119+
foreign import clear Effect Unit
120+
121+
getNotification NotificationID Effect (Maybe Notification)
122+
getNotification = map uorToMaybe <<< runEffectFn1 getNotificationImpl
123+
124+
foreign import getNotificationImpl
125+
EffectFn1 NotificationID (UndefinedOr Notification)
126+
127+
getPopup NotificationID Effect (Maybe Notification)
128+
getPopup = map toMaybe <<< runEffectFn1 getPopupImpl
129+
130+
foreign import getPopupImpl EffectFn1 NotificationID (Nullable Notification)
131+
132+
-- *** Notification
133+
134+
foreign import data NotificationType
135+
136+
-- | Convert a Notification to a record.
137+
fromNotification Notification NotificationRecord
138+
fromNotification =
139+
unsafeCopyGObjectProps @(NotificationPropsF UndefinedOr String Number)
140+
>>> RS.mapRecordKind uorToMaybe
141+
>>> Record.modify (Proxy @"urgency") unsafeParseUrgency
142+
>>> Record.modify (Proxy @"time") unsafeToInstant
143+
144+
where
145+
unsafeParseUrgency = unsafePartial case _ of
146+
"low"Low
147+
"normal"Normal
148+
"critical"Critical
149+
150+
unsafeToInstant ms = unsafePartial $ fromJust $ instant $ Milliseconds ms
151+
152+
-- * Signals
153+
154+
instance GObjectSignal "dismissed" Notification (EffectFn1 Notification Unit) where
155+
connect cb notification = unsafeConnect @"dismissed" cb notification
156+
157+
instance GObjectSignal "closed" Notification (EffectFn1 Notification Unit) where
158+
connect cb notification = unsafeConnect @"closed" cb notification
159+
160+
instance
161+
GObjectSignal "invoked" Notification (EffectFn2 Notification ActionID Unit) where
162+
connect cb notification = unsafeConnect @"invoked" cb notification
163+
164+
-- * Bindings and props
165+
166+
-- * Methods
167+
168+
foreign import dismiss Notification Effect Unit
169+
foreign import close Notification Effect Unit
170+
foreign import invoke Notification Action Effect Unit
171+
172+
-- *** Other types
173+
174+
newtype NotificationID = NotificationID Int
175+
176+
derive instance Newtype NotificationID _
177+
derive newtype instance Eq NotificationID
178+
derive newtype instance Ord NotificationID
179+
derive newtype instance Show NotificationID
180+
181+
type NotificationRecord =
182+
{ | NotificationPropsF Maybe Urgency Instant }
183+
184+
type NotificationPropsF f u t =
185+
( id NotificationID
186+
, appName String
187+
, appIcon String
188+
, summary String
189+
, body String
190+
, actions Array Action
191+
, urgency u
192+
, time t
193+
, image f String
194+
, appEntry f String
195+
, actionIcons f Boolean
196+
, category f String
197+
, resident f Boolean
198+
, soundFile f String
199+
, soundName f String
200+
, suppressSound f Boolean
201+
, transient f Boolean
202+
, x f Number
203+
, y f Number
204+
)
205+
206+
type NotificationsOptions =
207+
( popupTimeout Milliseconds
208+
, forceTimeout Boolean
209+
, cacheActions Boolean
210+
, clearDelay Milliseconds
211+
, dnd Boolean
212+
)
213+
214+
foreign import data ActionType
215+
216+
instance Show Action where
217+
show action = "(Action " <> show (fromAction action) <> ")"
218+
219+
newtype ActionID = ActionID String
220+
newtype ActionLabel = ActionLabel String
221+
222+
derive instance Newtype ActionID _
223+
derive newtype instance Show ActionID
224+
225+
derive instance Newtype ActionLabel _
226+
derive newtype instance Show ActionLabel
227+
228+
-- I don't think copying the props is necessary here
229+
fromAction Action { id ActionID, label ActionLabel }
230+
fromAction = unsafeCoerce
231+
232+
data Urgency
233+
= Low
234+
| Normal
235+
| Critical
236+
237+
derive instance Eq Urgency
238+
derive instance Ord Urgency
239+
derive instance Generic Urgency _
240+
241+
instance Show Urgency where
242+
show = genericShow
243+
244+
instance Bounded Urgency where
245+
top = Critical
246+
bottom = Low
247+
248+
instance Enum Urgency where
249+
succ = genericSucc
250+
pred = genericPred
251+

0 commit comments

Comments
 (0)