Skip to content
This repository was archived by the owner on Jul 19, 2022. It is now read-only.

Commit 33421d3

Browse files
committed
Add download button near perspective and modal
Add a new download button near the perspective details that when clicked shows a new download modal with `pull` instructions and a command copy field. This button only shows up on Unison Share. The copy field uses a webcomponent to reach out from Elm into JS and access the clipboard. The download command being generated also uncovered a problem with the way FQNs were being deserialized, such that they'd always include a . prefix. This lead to some strange URLs and didn't buy us anything and was removed.
1 parent f822c5f commit 33421d3

File tree

18 files changed

+398
-133
lines changed

18 files changed

+398
-133
lines changed

src/Api.elm

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@ type Endpoint
3333
= Endpoint (List String) (List QueryParameter)
3434

3535

36-
list : PerspectiveParams -> String -> Endpoint
36+
list : PerspectiveParams -> Maybe String -> Endpoint
3737
list perspectiveParams fqnOrHash =
38-
Endpoint [ "list" ] (string "namespace" fqnOrHash :: perspectiveParamsToQueryParams perspectiveParams)
38+
let
39+
namespace =
40+
Maybe.withDefault "." fqnOrHash
41+
in
42+
Endpoint [ "list" ] (string "namespace" namespace :: perspectiveParamsToQueryParams perspectiveParams)
3943

4044

4145
getDefinition : Perspective -> List String -> Endpoint

src/App.elm

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import CodebaseTree
77
import Definition.Reference exposing (Reference(..))
88
import Env as Env exposing (AppContext(..), Env, OperatingSystem(..))
99
import Finder
10-
import FullyQualifiedName as FQN
10+
import FullyQualifiedName as FQN exposing (FQN)
1111
import HashQualified exposing (HashQualified(..))
1212
import Html exposing (Html, a, aside, div, h1, h2, h3, header, nav, p, section, span, strong, text)
1313
import Html.Attributes exposing (class, classList, href, id, rel, target, title)
@@ -21,6 +21,7 @@ import RemoteData exposing (RemoteData(..))
2121
import Route exposing (Route)
2222
import UI
2323
import UI.Button as Button
24+
import UI.CopyField as CopyField
2425
import UI.Icon as Icon
2526
import UI.Modal as Modal
2627
import UI.Tooltip as Tooltip
@@ -38,6 +39,7 @@ type Modal
3839
| HelpModal
3940
| ReportBugModal
4041
| PublishModal
42+
| DownloadModal FQN
4143

4244

4345
type alias Model =
@@ -385,16 +387,30 @@ viewPerspective env =
385387

386388
back =
387389
Tooltip.tooltip
388-
(Button.icon (ChangePerspective (Codebase codebaseHash)) Icon.arrowLeftUp |> Button.small |> Button.view)
390+
(Button.icon (ChangePerspective (Codebase codebaseHash)) Icon.arrowLeftUp |> Button.small |> Button.uncontained |> Button.view)
389391
(Tooltip.Text ("You're currently viewing a subset of " ++ context ++ " (" ++ fqnText ++ "), click to view everything."))
390392
|> Tooltip.withArrow Tooltip.End
391393
|> Tooltip.view
394+
395+
download =
396+
case env.appContext of
397+
UnisonShare ->
398+
Button.iconThenLabel (ShowModal (DownloadModal fqn)) Icon.download "Download latest version"
399+
|> Button.small
400+
|> Button.view
401+
402+
Ucm ->
403+
UI.nothing
392404
in
393405
header
394406
[ class "perspective" ]
395-
[ div [ class "namespace-slug" ] []
396-
, h2 [] [ text fqnText ]
397-
, back
407+
[ div [ class "perspective-row" ]
408+
[ div [ class "namespace-slug" ] []
409+
, h2 [] [ text fqnText ]
410+
, back
411+
]
412+
, div [ class "perspective-row" ] [ download ]
413+
, UI.divider
398414
]
399415

400416

@@ -488,7 +504,10 @@ viewHelpModal os keyboardShortcut =
488504

489505
githubLinkButton : String -> Html msg
490506
githubLinkButton repo =
491-
Button.linkIconThenLabel ("https://github.com/" ++ repo) Icon.github repo |> Button.small |> Button.view
507+
Button.linkIconThenLabel ("https://github.com/" ++ repo) Icon.github repo
508+
|> Button.small
509+
|> Button.contained
510+
|> Button.view
492511

493512

494513
viewPublishModal : Html Msg
@@ -546,6 +565,33 @@ viewReportBugModal appContext =
546565
|> Modal.view
547566

548567

568+
viewDownloadModal : FQN -> Html Msg
569+
viewDownloadModal fqn =
570+
let
571+
prettyName =
572+
FQN.toString fqn
573+
574+
unqualified =
575+
FQN.unqualifiedName fqn
576+
577+
pullCommand =
578+
"pull [email protected]:unisonweb/share.git:." ++ prettyName ++ " ." ++ unqualified
579+
580+
content =
581+
Modal.Content
582+
(section
583+
[]
584+
[ p [] [ text "Download ", UI.bold prettyName, text " by pulling the namespace from Unison Share into a namespace in your local codebase:" ]
585+
, CopyField.copyField (\_ -> CloseModal) pullCommand |> CopyField.withPrefix ".>" |> CopyField.view
586+
, div [ class "hint" ] [ text "Copy and paste this command into UCM." ]
587+
]
588+
)
589+
in
590+
Modal.modal "download-modal" CloseModal content
591+
|> Modal.withHeader ("Download " ++ prettyName)
592+
|> Modal.view
593+
594+
549595
viewModal :
550596
{ m | env : Env, modal : Modal, keyboardShortcut : KeyboardShortcut.Model }
551597
-> Html Msg
@@ -566,6 +612,9 @@ viewModal model =
566612
ReportBugModal ->
567613
viewReportBugModal model.env.appContext
568614

615+
DownloadModal fqn ->
616+
viewDownloadModal fqn
617+
569618

570619
viewAppLoading : AppContext -> Html msg
571620
viewAppLoading appContext =

src/CodebaseTree.elm

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import CodebaseTree.NamespaceListing as NamespaceListing
88
, NamespaceListingChild(..)
99
, NamespaceListingContent
1010
)
11+
import Debug
1112
import Definition.Category as Category
1213
import Definition.Reference exposing (Reference(..))
1314
import Env exposing (Env)
@@ -144,21 +145,17 @@ update env msg model =
144145

145146
fetchRootNamespaceListing : Perspective -> ApiRequest NamespaceListing Msg
146147
fetchRootNamespaceListing perspective =
147-
let
148-
rootFqn =
149-
FQN.fromString "."
150-
in
151-
fetchNamespaceListing perspective rootFqn FetchRootNamespaceListingFinished
148+
fetchNamespaceListing perspective Nothing FetchRootNamespaceListingFinished
152149

153150

154151
fetchSubNamespaceListing : Perspective -> FQN -> ApiRequest NamespaceListing Msg
155152
fetchSubNamespaceListing perspective fqn =
156-
fetchNamespaceListing perspective fqn (FetchSubNamespaceListingFinished fqn)
153+
fetchNamespaceListing perspective (Just fqn) (FetchSubNamespaceListingFinished fqn)
157154

158155

159-
fetchNamespaceListing : Perspective -> FQN -> (Result Http.Error NamespaceListing -> msg) -> ApiRequest NamespaceListing msg
156+
fetchNamespaceListing : Perspective -> Maybe FQN -> (Result Http.Error NamespaceListing -> msg) -> ApiRequest NamespaceListing msg
160157
fetchNamespaceListing perspective fqn toMsg =
161-
Api.list (Perspective.toParams perspective) (FQN.toString fqn)
158+
Api.list (Perspective.toParams perspective) (Maybe.map FQN.toString fqn)
162159
|> Api.toRequest (NamespaceListing.decode fqn) toMsg
163160

164161

@@ -266,6 +263,9 @@ viewNamespaceListing expandedNamespaceListings (NamespaceListing _ fqn content)
266263

267264
fullName =
268265
FQN.toString fqn
266+
267+
log =
268+
Debug.log "fqn" fqn
269269
in
270270
div [ class "subtree" ]
271271
[ a

src/CodebaseTree/NamespaceListing.elm

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ module CodebaseTree.NamespaceListing exposing
88
, map
99
)
1010

11-
import Definition.AbilityConstructor as AbilityConstructor
1211
import Definition.Category as Category exposing (Category)
13-
import Definition.DataConstructor as DataConstructor
1412
import Definition.Term as Term exposing (TermCategory(..))
1513
import Definition.Type as Type exposing (TypeCategory(..))
1614
import FullyQualifiedName as FQN exposing (FQN)
@@ -80,7 +78,7 @@ contentFetched (NamespaceListing _ fqn content) needleFqn =
8078
-- JSON DECODE
8179

8280

83-
decode : FQN -> Decode.Decoder NamespaceListing
81+
decode : Maybe FQN -> Decode.Decoder NamespaceListing
8482
decode listingFqn =
8583
Decode.map3
8684
NamespaceListing
@@ -99,24 +97,34 @@ decode listingFqn =
9997
-- JSON Decode Helpers
10098

10199

100+
decodeFromParent : Maybe FQN -> Decode.Decoder FQN
101+
decodeFromParent parentFqn =
102+
parentFqn
103+
|> Maybe.map FQN.decodeFromParent
104+
|> Maybe.withDefault FQN.decode
105+
106+
102107
{-| Decoding specific intermediate type |
103108
-}
104-
decodeSubNamespace : FQN -> Decode.Decoder NamespaceListingChild
109+
decodeSubNamespace : Maybe FQN -> Decode.Decoder NamespaceListingChild
105110
decodeSubNamespace parentFqn =
106111
Decode.map SubNamespace
107112
(Decode.map3 NamespaceListing
108113
(field "namespaceHash" Hash.decode)
109-
(field "namespaceName" (FQN.decodeFromParent parentFqn))
114+
(field "namespaceName" (decodeFromParent parentFqn))
110115
(Decode.succeed NotAsked)
111116
)
112117

113118

114-
decodeContent : FQN -> Decode.Decoder NamespaceListingContent
119+
decodeContent : Maybe FQN -> Decode.Decoder NamespaceListingContent
115120
decodeContent parentFqn =
116121
let
117122
decodeTag =
118123
field "tag" Decode.string
119124

125+
decodeFqn =
126+
decodeFromParent parentFqn
127+
120128
termTypeByHash hash =
121129
if Hash.isAbilityConstructorHash hash then
122130
"AbilityConstructor"
@@ -134,29 +142,29 @@ decodeContent parentFqn =
134142
Decode.map SubDefinition
135143
(Decode.map2 AbilityConstructorListing
136144
(field "termHash" Hash.decode)
137-
(field "termName" (FQN.decodeFromParent parentFqn))
145+
(field "termName" decodeFqn)
138146
)
139147

140148
decodeDataConstructorListing =
141149
Decode.map SubDefinition
142150
(Decode.map2 DataConstructorListing
143151
(field "termHash" Hash.decode)
144-
(field "termName" (FQN.decodeFromParent parentFqn))
152+
(field "termName" decodeFqn)
145153
)
146154

147155
decodeTypeListing =
148156
Decode.map SubDefinition
149157
(Decode.map3 TypeListing
150158
(field "typeHash" Hash.decode)
151-
(field "typeName" (FQN.decodeFromParent parentFqn))
159+
(field "typeName" decodeFqn)
152160
(Decode.map Category.Type (Type.decodeTypeCategory [ "typeTag" ]))
153161
)
154162

155163
decodeTermListing =
156164
Decode.map SubDefinition
157165
(Decode.map3 TermListing
158166
(field "termHash" Hash.decode)
159-
(field "termName" (FQN.decodeFromParent parentFqn))
167+
(field "termName" decodeFqn)
160168
(Decode.map Category.Term (Term.decodeTermCategory [ "termTag" ]))
161169
)
162170

src/PreApp.elm

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ import App
55
import Browser
66
import Browser.Navigation as Nav
77
import Env exposing (AppContext(..), Flags, OperatingSystem(..))
8-
import Html exposing (div, p, text)
9-
import Html.Attributes exposing (class, id, title)
8+
import Html
109
import Http
1110
import Perspective exposing (CodebasePerspectiveParam(..), Perspective(..), PerspectiveParams(..))
1211
import Route exposing (Route)
13-
import UI.Icon as Icon
1412
import Url exposing (Url)
1513

1614

@@ -65,7 +63,7 @@ init flags url navKey =
6563

6664
fetchPerspective : PreEnv -> ApiRequest Perspective Msg
6765
fetchPerspective preEnv =
68-
Api.list (ByCodebase Relative) "."
66+
Api.list (ByCodebase Relative) (Just ".")
6967
|> Api.toRequest (Perspective.decode preEnv.perspectiveParams) (FetchPerspectiveFinished preEnv)
7068

7169

src/UI.elm

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
module UI exposing (..)
22

3-
import Html exposing (Attribute, Html, code, div, hr, pre, span, text)
3+
import Html exposing (Attribute, Html, code, div, hr, pre, span, strong, text)
44
import Html.Attributes exposing (class)
55

66

7+
bold : String -> Html msg
8+
bold text_ =
9+
strong [] [ text text_ ]
10+
11+
712
codeBlock : List (Attribute msg) -> Html msg -> Html msg
813
codeBlock attrs code_ =
914
pre attrs [ code [] [ code_ ] ]

src/UI/Button.elm

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -129,30 +129,21 @@ view { content, type_, color, action, size } =
129129

130130
Label l ->
131131
( "content-label", [ text l ] )
132+
133+
attrs =
134+
[ class "button"
135+
, class (typeToClassName type_)
136+
, class (colorToClassName color)
137+
, class (sizeToClassName size)
138+
, class contentType
139+
]
132140
in
133141
case action of
134142
OnClick clickMsg ->
135-
Html.button
136-
[ class "button"
137-
, class (typeToClassName type_)
138-
, class (colorToClassName color)
139-
, class (sizeToClassName size)
140-
, class contentType
141-
, onClick clickMsg
142-
]
143-
content_
143+
Html.button (onClick clickMsg :: attrs) content_
144144

145145
Href url ->
146-
a
147-
[ class "button"
148-
, class (typeToClassName type_)
149-
, class (sizeToClassName size)
150-
, class contentType
151-
, href url
152-
, rel "noopener"
153-
, target "_blank"
154-
]
155-
content_
146+
a (attrs ++ [ href url, rel "noopener", target "_blank" ]) content_
156147

157148

158149

0 commit comments

Comments
 (0)