Skip to content

Commit 3f6280e

Browse files
committed
Add webhook configuration UI to project settings
The notification system supports webhooks. Add a way to configure then for each project on the project settings page. ! This does not yet add API integration
1 parent 8da3a98 commit 3f6280e

File tree

7 files changed

+513
-17
lines changed

7 files changed

+513
-17
lines changed

elm-git.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"git-dependencies": {
33
"direct": {
4-
"https://github.com/unisonweb/ui-core": "aedb50adb6e1ecbba981c1bea6c074cfdf1fffae"
4+
"https://github.com/unisonweb/ui-core": "caf8eaffac685566d964c1d3e4b63deea8e7462f"
55
},
66
"indirect": {}
77
}
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
module UnisonShare.AddProjectWebhookModal exposing (..)
2+
3+
import Html exposing (Html, div, text)
4+
import Html.Attributes exposing (class, classList)
5+
import Http
6+
import Json.Decode as Decode
7+
import Json.Decode.Extra exposing (when)
8+
import Lib.Decode.Helpers exposing (tag)
9+
import Lib.HttpApi as HttpApi exposing (HttpResult)
10+
import Lib.Search as Search exposing (Search)
11+
import Lib.SearchResults as SearchResults
12+
import List.Extra as ListE
13+
import List.Nonempty as NEL
14+
import Maybe.Extra as MaybeE
15+
import UI
16+
import UI.Button as Button
17+
import UI.Click as Click
18+
import UI.Divider as Divider
19+
import UI.Form.CheckboxField as CheckboxField
20+
import UI.Form.RadioField as RadioField
21+
import UI.Form.TextField as TextField
22+
import UI.Icon as Icon
23+
import UI.Modal as Modal
24+
import UI.ProfileSnippet as ProfileSnippet
25+
import UI.StatusBanner as StatusBanner
26+
import UnisonShare.Api as ShareApi
27+
import UnisonShare.AppContext exposing (AppContext)
28+
import UnisonShare.Link as Link
29+
import UnisonShare.Project.ProjectRef exposing (ProjectRef)
30+
import UnisonShare.ProjectRole as ProjectRole exposing (ProjectRole(..))
31+
import UnisonShare.ProjectWebhook exposing (ProjectWebhook)
32+
import UnisonShare.Session as Session
33+
import UnisonShare.User as User exposing (UserSummaryWithId)
34+
35+
36+
type NotificationEventType
37+
= ProjectContributionCreated
38+
| ProjectContributionUpdated
39+
| ProjectContributionComment
40+
| ProjectTicketCreated
41+
| ProjectTicketUpdated
42+
| ProjectTicketComment
43+
| ProjectBranchUpdated
44+
| ProjectReleaseCreated
45+
46+
47+
type WebhookEvents
48+
= AllEvents
49+
| SpecificEvents (List NotificationEventType)
50+
51+
52+
type alias Form =
53+
{ url : String
54+
, events : WebhookEvents
55+
, isActive : Bool
56+
}
57+
58+
59+
type Model
60+
= Edit Form
61+
| Saving Form
62+
| Failure Http.Error Form
63+
| Success ProjectWebhook
64+
65+
66+
init : Model
67+
init =
68+
Edit { url = "", events = AllEvents, isActive = True }
69+
70+
71+
72+
-- UPDATE
73+
74+
75+
type Msg
76+
= CloseModal
77+
| UpdateUrl String
78+
| SetWebhookEvents WebhookEvents
79+
| ToggleEvent NotificationEventType
80+
| ToggleIsActive
81+
| AddWebhook
82+
| AddWebhookFinished (HttpResult ())
83+
84+
85+
type OutMsg
86+
= NoOutMsg
87+
| RequestCloseModal
88+
| AddedWebhook ProjectWebhook
89+
90+
91+
update : AppContext -> ProjectRef -> Msg -> Model -> ( Model, Cmd Msg, OutMsg )
92+
update appContext projectRef msg model =
93+
let
94+
x =
95+
Debug.log "model" model
96+
in
97+
case ( msg, model ) of
98+
( UpdateUrl url, Edit f ) ->
99+
( Edit { f | url = url }, Cmd.none, NoOutMsg )
100+
101+
( SetWebhookEvents events, Edit f ) ->
102+
( Edit { f | events = events }, Cmd.none, NoOutMsg )
103+
104+
( ToggleEvent eventType, Edit f ) ->
105+
let
106+
events =
107+
case f.events of
108+
AllEvents ->
109+
SpecificEvents [ eventType ]
110+
111+
SpecificEvents evts ->
112+
if List.member eventType evts then
113+
SpecificEvents (ListE.remove eventType evts)
114+
115+
else
116+
SpecificEvents (evts ++ [ eventType ])
117+
in
118+
( Edit { f | events = events }, Cmd.none, NoOutMsg )
119+
120+
( ToggleIsActive, Edit f ) ->
121+
( Edit { f | isActive = not f.isActive }, Cmd.none, NoOutMsg )
122+
123+
( AddWebhook, _ ) ->
124+
( model, Cmd.none, NoOutMsg )
125+
126+
( AddWebhookFinished res, _ ) ->
127+
( model, Cmd.none, NoOutMsg )
128+
129+
( CloseModal, _ ) ->
130+
( model, Cmd.none, RequestCloseModal )
131+
132+
_ ->
133+
( model, Cmd.none, NoOutMsg )
134+
135+
136+
137+
-- EFFECTS
138+
-- VIEW
139+
140+
141+
viewUser : UserSummaryWithId -> Html msg
142+
viewUser user =
143+
ProfileSnippet.profileSnippet user |> ProfileSnippet.view
144+
145+
146+
divider : Html msg
147+
divider =
148+
Divider.divider |> Divider.small |> Divider.view
149+
150+
151+
viewEventSelection : List NotificationEventType -> Html Msg
152+
viewEventSelection selected =
153+
let
154+
isSelected event =
155+
List.member event selected
156+
157+
checkbox title event =
158+
CheckboxField.field title (ToggleEvent event) (isSelected event)
159+
|> CheckboxField.view
160+
161+
contributionEvents =
162+
[ checkbox "Contribution created" ProjectContributionCreated
163+
, checkbox "Contribution updated" ProjectContributionUpdated
164+
, checkbox "Contribution comment" ProjectContributionComment
165+
]
166+
167+
ticketEvents =
168+
[ checkbox "Ticket created" ProjectTicketCreated
169+
, checkbox "Ticket updated" ProjectTicketUpdated
170+
, checkbox "Ticket comment" ProjectTicketComment
171+
]
172+
173+
eventSelectionCheckboxes =
174+
div [ class "event-selection_groups" ]
175+
[ div []
176+
[ div [ class "checkboxes" ]
177+
[ checkbox "Branch updated" ProjectBranchUpdated
178+
, checkbox "Release created" ProjectReleaseCreated
179+
]
180+
]
181+
, div []
182+
[ div [ class "checkboxes" ] contributionEvents
183+
]
184+
, div []
185+
[ div [ class "checkboxes" ] ticketEvents
186+
]
187+
]
188+
in
189+
div [ class "event-selection" ] [ divider, eventSelectionCheckboxes ]
190+
191+
192+
view : Model -> Html Msg
193+
view model =
194+
let
195+
modal_ c =
196+
Modal.content c
197+
|> Modal.modal "add-project-webhook-modal" CloseModal
198+
|> Modal.withHeader "Add Webhook"
199+
200+
modal =
201+
case model of
202+
Edit form ->
203+
let
204+
specificEventsOption =
205+
case form.events of
206+
AllEvents ->
207+
SpecificEvents []
208+
209+
_ ->
210+
form.events
211+
212+
options =
213+
NEL.singleton (RadioField.option "Select specific events" "The webhook is only called on selected events" specificEventsOption)
214+
|> NEL.cons (RadioField.option "All events" "The webhook is called on all project events (including future additions)" AllEvents)
215+
216+
eventSelection =
217+
case form.events of
218+
AllEvents ->
219+
UI.nothing
220+
221+
SpecificEvents selected ->
222+
viewEventSelection selected
223+
in
224+
modal_
225+
(div []
226+
[ TextField.field UpdateUrl "Webhook URL" form.url
227+
|> TextField.withIcon Icon.wireframeGlobe
228+
|> TextField.withHelpText "This URL will be called when the selected events are triggered."
229+
|> TextField.view
230+
, divider
231+
, RadioField.field "Events" SetWebhookEvents options form.events |> RadioField.view
232+
, eventSelection
233+
, divider
234+
, CheckboxField.field "Active" ToggleIsActive form.isActive
235+
|> CheckboxField.withHelpText "Actively call the Webhook URL when selected events are triggered."
236+
|> CheckboxField.view
237+
]
238+
)
239+
|> Modal.withActions
240+
[ Button.button CloseModal "Cancel"
241+
|> Button.subdued
242+
, Button.button AddWebhook "Add Webhook"
243+
|> Button.emphasized
244+
]
245+
|> Modal.withLeftSideFooter
246+
[ Button.iconThenLabel_ Link.docs Icon.docs "Webhook request format docs"
247+
|> Button.small
248+
|> Button.outlined
249+
|> Button.view
250+
]
251+
252+
Saving form ->
253+
modal_ (StatusBanner.working "Adding Webhook...")
254+
255+
Failure _ form ->
256+
modal_ (StatusBanner.bad "Failed to add Webhook")
257+
258+
Success webhook ->
259+
modal_ (StatusBanner.good "Successfully added Webhook")
260+
in
261+
Modal.view modal

0 commit comments

Comments
 (0)