From b73669d86338626153a1e962b675c2b930f7747c Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 18 Jan 2021 13:44:49 +0300 Subject: [PATCH 01/91] Add initial markup --- src/elm/Icons.elm | 15 +++++++ src/elm/Session/LoggedIn.elm | 76 ++++++++++++++++++++++++++++++++++-- tailwind.config.js | 4 ++ 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/src/elm/Icons.elm b/src/elm/Icons.elm index 34e911c82..8793272dc 100644 --- a/src/elm/Icons.elm +++ b/src/elm/Icons.elm @@ -4,6 +4,7 @@ module Icons exposing , back , camera , claims + , clock , close , communities , dashboard @@ -274,3 +275,17 @@ claims class_ = , Svg.path [ d "M11.3333 14.6667C14.2789 14.6667 16.6667 12.2789 16.6667 9.33333C16.6667 6.38781 14.2789 4 11.3333 4C8.38781 4 6 6.38781 6 9.33333C6 12.2789 8.38781 14.6667 11.3333 14.6667Z", stroke "black", strokeWidth "2", strokeLinecap "round", strokeLinejoin "round" ] [] , Svg.path [ d "M22.6667 14.6667L25.3333 17.3333L30.6667 12", stroke "black", strokeWidth "2", strokeLinecap "round", strokeLinejoin "round" ] [] ] + + +clock : String -> Html msg +clock class_ = + svg + [ fill "none" + , height "20" + , viewBox "0 0 20 20" + , width "20" + , class class_ + ] + [ Svg.path [ d "m19.4141 9.41404c-.3236 0-.5859.26235-.5859.58594 0 4.86782-3.9603 8.82812-8.8281 8.82812-4.86779 0-8.82804-3.9603-8.82804-8.82812 0-4.86783 3.96025-8.82808 8.82804-8.82808 1.4844 0 2.9282.36856 4.2172 1.07082l-.7326.73253c-.1676.16758-.2177.41957-.127.63855.0907.21895.3043.36172.5413.36172h2.8036c.3236 0 .586-.26234.586-.58593v-2.803619c0-.236991-.1428-.450623-.3617-.5413252-.2191-.0907417-.4711-.04058572-.6386.1270302l-1.2116 1.211594c-1.5347-.906127-3.2796-1.38319428-5.0766-1.38319428-2.67108 0-5.18232 1.04018428-7.07106 2.92888428-1.88874 1.88878-2.92888741 4.39998-2.92888741 7.07104 0 2.6711 1.04014741 5.1823 2.92888741 7.071 1.88878 1.8888 4.39998 2.929 7.07106 2.929 2.6711 0 5.1823-1.0402 7.071-2.929 1.8888-1.8887 2.9289-4.3999 2.9289-7.071.0001-.32361-.2623-.58596-.5859-.58596z" ] [] + , Svg.path [ d "m13.7656 9.41403h-3.1796v-3.17966c0-.32359-.2623-.58593-.5859-.58593-.32363 0-.58598.26234-.58598.58593v3.76559c0 .32364.26235.58594.58598.58594h3.7655c.3236 0 .586-.2623.586-.58594 0-.32359-.2624-.58593-.586-.58593z" ] [] + ] diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 4e3e2ec53..bbedf59e0 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -43,9 +43,9 @@ import Graphql.Document import Graphql.Http import Graphql.Operation exposing (RootSubscription) import Graphql.SelectionSet exposing (SelectionSet) -import Html exposing (Html, a, button, div, footer, img, nav, p, span, text) -import Html.Attributes exposing (class, classList, src, style, type_) -import Html.Events exposing (onClick, onMouseEnter) +import Html exposing (Html, a, button, div, footer, img, input, li, nav, p, span, text, ul) +import Html.Attributes exposing (class, classList, placeholder, src, style, type_) +import Html.Events exposing (onBlur, onClick, onFocus, onInput, onMouseEnter) import Http import I18Next exposing (Delims(..), Translations, t) import Icons @@ -144,6 +144,7 @@ type alias Model = , hasShop : FeatureStatus , hasObjectives : FeatureStatus , hasKyc : FeatureStatus + , searchState : SearchState } @@ -172,6 +173,7 @@ initModel shared authModel accountName selectedCommunity = , hasShop = FeatureLoading , hasObjectives = FeatureLoading , hasKyc = FeatureLoading + , searchState = Inactive } @@ -388,6 +390,65 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = ] +type SearchState + = Inactive + | Active String + | ResultsShowed + + +viewSearch model = + let + iconColor = + case model.searchState of + Inactive -> + "fill-gray" + + _ -> + "fill-indigo" + + viewRecentSearches = + case model.searchState of + Active _ -> + let + viewQuery q = + div [ class "leading-10" ] + [ Icons.clock "fill-gray inline-block align-middle mr-3" + , span [ class "inline align-middle" ] [ text q ] + ] + in + div [ class "fixed bg-white w-full left-0 px-2 py-4 border-2" ] + [ span [ class "font-bold" ] [ text "Recently searched" ] + , ul [] + [ li [] + [ viewQuery "English Class" + , viewQuery "Organic bag" + , viewQuery "Pura vida" + ] + ] + ] + + _ -> + text "" + in + div [ class "w-full" ] + [ Html.form + [ class "w-full relative block mt-2" + ] + [ input + [ type_ "search" + , class "w-full form-input rounded-full bg-gray-100 pl-10 m-0 block" + , placeholder "Find friends and communities" + , onFocus (SearchStateChanged (Active "")) + , onBlur (SearchStateChanged Inactive) + , onInput (\query -> SearchStateChanged (Active query)) + ] + [] + , Icons.search <| "absolute top-0 left-0 mt-2 ml-2" ++ " " ++ iconColor + ] + , viewRecentSearches + ] + + viewHeader : Model -> Profile.Model -> Html Msg viewHeader ({ shared } as model) profile_ = let @@ -494,6 +555,7 @@ viewHeader ({ shared } as model) profile_ = ] ] ] + , viewSearch model ] @@ -717,6 +779,7 @@ type Msg | CloseCommunitySelector | SelectCommunity Symbol (Cmd Msg) | HideFeedbackLocal + | SearchStateChanged SearchState update : Msg -> Model -> UpdateResult @@ -746,6 +809,10 @@ update msg model = Ignored -> UR.init model + SearchStateChanged state -> + { model | searchState = state } + |> UR.init + CompletedLoadTranslation lang (Ok transl) -> case model.profile of Loaded _ -> @@ -1051,6 +1118,9 @@ msgToString msg = Ignored -> [ "Ignored" ] + SearchStateChanged _ -> + [ "SearchStateChanged" ] + CompletedLoadTranslation _ r -> [ "CompletedLoadTranslation", UR.resultToString r ] diff --git a/tailwind.config.js b/tailwind.config.js index c238fd526..a005a29f7 100755 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -26,6 +26,10 @@ module.exports = { } } }), + fill: { + gray: '#DADADA', + indigo: '#45469B' + }, // Colors used on the 'Design System' colors: { black: '#000000', From bee31c9dacd56f85ff97d11b468cf6a7e861c465 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 20 Jan 2021 19:42:19 +0300 Subject: [PATCH 02/91] Add ports to store/retrieve recent searches --- src/elm/Ports.elm | 24 ++++++++++++++ src/elm/Session/LoggedIn.elm | 62 +++++++++++++++++++++++++++++------- src/index.js | 13 ++++++++ 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/src/elm/Ports.elm b/src/elm/Ports.elm index 6775aae73..ff7080b36 100755 --- a/src/elm/Ports.elm +++ b/src/elm/Ports.elm @@ -1,11 +1,14 @@ port module Ports exposing ( JavascriptOut , JavascriptOutModel + , askForRecentSearches + , gotRecentSearches , javascriptInPort , javascriptOut , javascriptOutCmd , mapAddress , storeLanguage + , storeRecentSearches ) import Json.Encode as Encode exposing (Value) @@ -59,3 +62,24 @@ port javascriptInPort : (Value -> msg) -> Sub msg port storeLanguage : String -> Cmd msg + + +{-| Store recent searches to the `localStorage`. +-} +port storeRecentSearches : String -> Cmd msg + + +{-| Ping JS to send back the recent searches from the `localStorage`. +-} +port askForRecentSearches : () -> Cmd msg + + + +-- +-- Subscriptions +-- + + +{-| Receive recent searches from JS. +-} +port gotRecentSearches : (String -> msg) -> Sub msg diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index bbedf59e0..c8fcf8f31 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -21,6 +21,7 @@ module Session.LoggedIn exposing , msgToString , profile , readAllNotifications + , searchFor , subscriptions , update , view @@ -45,11 +46,11 @@ import Graphql.Operation exposing (RootSubscription) import Graphql.SelectionSet exposing (SelectionSet) import Html exposing (Html, a, button, div, footer, img, input, li, nav, p, span, text, ul) import Html.Attributes exposing (class, classList, placeholder, src, style, type_) -import Html.Events exposing (onBlur, onClick, onFocus, onInput, onMouseEnter) +import Html.Events exposing (onBlur, onClick, onFocus, onInput, onMouseEnter, onSubmit) import Http import I18Next exposing (Delims(..), Translations, t) import Icons -import Json.Decode as Decode exposing (Value) +import Json.Decode as Decode exposing (Value, list, string) import Json.Encode as Encode exposing (Value) import List.Extra as List import Notification exposing (Notification) @@ -118,6 +119,7 @@ subscriptions model = Sub.batch [ Sub.map GotAuthMsg (Auth.subscriptions model.auth) , Sub.map KeyDown (Browser.Events.onKeyDown (Decode.field "key" Decode.string)) + , Ports.gotRecentSearches GotRecentSearches ] @@ -145,6 +147,7 @@ type alias Model = , hasObjectives : FeatureStatus , hasKyc : FeatureStatus , searchState : SearchState + , recentSearches : List String } @@ -174,6 +177,7 @@ initModel shared authModel accountName selectedCommunity = , hasObjectives = FeatureLoading , hasKyc = FeatureLoading , searchState = Inactive + , recentSearches = [] } @@ -420,10 +424,7 @@ viewSearch model = [ span [ class "font-bold" ] [ text "Recently searched" ] , ul [] [ li [] - [ viewQuery "English Class" - , viewQuery "Organic bag" - , viewQuery "Pura vida" - ] + (List.map viewQuery model.recentSearches) ] ] @@ -433,6 +434,7 @@ viewSearch model = div [ class "w-full" ] [ Html.form [ class "w-full relative block mt-2" + , onSubmit (SubmittedSearch model.searchState) ] [ input [ type_ "search" @@ -765,7 +767,7 @@ type Msg | ClickedTryAgainProfile Eos.Name | ClickedLogout | EnteredSearch String - | SubmitedSearch + | SubmittedSearch SearchState | ShowNotificationModal Bool | ShowUserNav Bool | ShowMainNav Bool @@ -780,6 +782,13 @@ type Msg | SelectCommunity Symbol (Cmd Msg) | HideFeedbackLocal | SearchStateChanged SearchState + | GotRecentSearches String + + +searchFor : String -> Cmd msg +searchFor query = + -- TODO: send GraphQl query + Cmd.none update : Msg -> Model -> UpdateResult @@ -809,9 +818,20 @@ update msg model = Ignored -> UR.init model + GotRecentSearches queries -> + case Decode.decodeString (list string) queries of + Ok queryList -> + { model | recentSearches = queryList } + |> UR.init + + Err _ -> + model |> UR.init + SearchStateChanged state -> { model | searchState = state } |> UR.init + -- TODO: Retrieve recent searches only when state changed to show the dropdown + |> UR.addCmd (Ports.askForRecentSearches ()) CompletedLoadTranslation lang (Ok transl) -> case model.profile of @@ -903,8 +923,25 @@ update msg model = EnteredSearch s -> UR.init { model | searchText = s } - SubmitedSearch -> - UR.init model + SubmittedSearch searchState -> + case searchState of + Active query -> + let + newRecentSearches = + (query :: model.recentSearches) + |> List.take 3 + + encoded = + Encode.encode 0 (Encode.list Encode.string newRecentSearches) + in + { model | recentSearches = newRecentSearches } + |> UR.init + |> UR.addCmd (searchFor query) + |> UR.addCmd (Ports.storeRecentSearches encoded) + + _ -> + model + |> UR.init ShowNotificationModal b -> UR.init @@ -1118,6 +1155,9 @@ msgToString msg = Ignored -> [ "Ignored" ] + GotRecentSearches _ -> + [ "GotRecentSearches" ] + SearchStateChanged _ -> [ "SearchStateChanged" ] @@ -1142,8 +1182,8 @@ msgToString msg = EnteredSearch _ -> [ "EnteredSearch" ] - SubmitedSearch -> - [ "SubmitedSearch" ] + SubmittedSearch _ -> + [ "SubmittedSearch" ] ShowNotificationModal _ -> [ "ShowNotificationModal" ] diff --git a/src/index.js b/src/index.js index 2f46b90f9..8912a906f 100755 --- a/src/index.js +++ b/src/index.js @@ -65,6 +65,7 @@ const LANGUAGE_KEY = 'bespiral.language' const AUTH_PREF_KEY = 'bespiral.auth.pref' const PUSH_PREF = 'bespiral.push.pref' const SELECTED_COMMUNITY_KEY = 'bespiral.selected_community' +const RECENT_SEARCHES = 'bespiral.recent_search' const env = process.env.NODE_ENV || 'development' const config = configuration[env] @@ -183,6 +184,18 @@ function storeLanguage (lang) { window.localStorage.setItem(LANGUAGE_KEY, lang) } +// STORE RECENT SEARCHES +app.ports.storeRecentSearches.subscribe(query => + window.localStorage.setItem(RECENT_SEARCHES, query) +) + +// RETRIEVE RECENT SEARCHES +// Elm pings JS with `askForRecentSearches` command when search field is focused, +// JS sends back recent searches to Elm via `gotRecentSearches` subscription. +app.ports.askForRecentSearches.subscribe(() => { + app.ports.gotRecentSearches.send(window.localStorage.getItem(RECENT_SEARCHES) || '[]') +}) + // STORE AUTH PREFERENCE function storeAuthPreference (auth) { From 60ac7e9a8935fd696e2db3a3b331aaffc6fe83b8 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Sat, 23 Jan 2021 17:05:01 +0300 Subject: [PATCH 03/91] Update GraphQl types --- src/elm/Cambiatus/Object.elm | 4 ++ src/elm/Cambiatus/Object/SearchResult.elm | 59 +++++++++++++++++++++++ src/elm/Cambiatus/Query.elm | 12 +++++ 3 files changed, 75 insertions(+) create mode 100644 src/elm/Cambiatus/Object/SearchResult.elm diff --git a/src/elm/Cambiatus/Object.elm b/src/elm/Cambiatus/Object.elm index 3313e99a4..827d5c2e5 100644 --- a/src/elm/Cambiatus/Object.elm +++ b/src/elm/Cambiatus/Object.elm @@ -93,6 +93,10 @@ type PushSubscription = PushSubscription +type SearchResult + = SearchResult + + type SignUpResponse = SignUpResponse diff --git a/src/elm/Cambiatus/Object/SearchResult.elm b/src/elm/Cambiatus/Object/SearchResult.elm new file mode 100644 index 000000000..360a70100 --- /dev/null +++ b/src/elm/Cambiatus/Object/SearchResult.elm @@ -0,0 +1,59 @@ +-- Do not manually edit this file, it was auto-generated by dillonkearns/elm-graphql +-- https://github.com/dillonkearns/elm-graphql + + +module Cambiatus.Object.SearchResult exposing (..) + +import Cambiatus.InputObject +import Cambiatus.Interface +import Cambiatus.Object +import Cambiatus.Scalar +import Cambiatus.ScalarCodecs +import Cambiatus.Union +import Graphql.Internal.Builder.Argument as Argument exposing (Argument) +import Graphql.Internal.Builder.Object as Object +import Graphql.Internal.Encode as Encode exposing (Value) +import Graphql.Operation exposing (RootMutation, RootQuery, RootSubscription) +import Graphql.OptionalArgument exposing (OptionalArgument(..)) +import Graphql.SelectionSet exposing (SelectionSet) +import Json.Decode as Decode + + +type alias ActionsOptionalArguments = + { query : OptionalArgument String } + + +actions : + (ActionsOptionalArguments -> ActionsOptionalArguments) + -> SelectionSet decodesTo Cambiatus.Object.Action + -> SelectionSet (List decodesTo) Cambiatus.Object.SearchResult +actions fillInOptionals object_ = + let + filledInOptionals = + fillInOptionals { query = Absent } + + optionalArgs = + [ Argument.optional "query" filledInOptionals.query Encode.string ] + |> List.filterMap identity + in + Object.selectionForCompositeField "actions" optionalArgs object_ (identity >> Decode.list) + + +type alias ProductsOptionalArguments = + { query : OptionalArgument String } + + +products : + (ProductsOptionalArguments -> ProductsOptionalArguments) + -> SelectionSet decodesTo Cambiatus.Object.Product + -> SelectionSet (List decodesTo) Cambiatus.Object.SearchResult +products fillInOptionals object_ = + let + filledInOptionals = + fillInOptionals { query = Absent } + + optionalArgs = + [ Argument.optional "query" filledInOptionals.query Encode.string ] + |> List.filterMap identity + in + Object.selectionForCompositeField "products" optionalArgs object_ (identity >> Decode.list) diff --git a/src/elm/Cambiatus/Query.elm b/src/elm/Cambiatus/Query.elm index b08fd7643..7c5a5b073 100644 --- a/src/elm/Cambiatus/Query.elm +++ b/src/elm/Cambiatus/Query.elm @@ -221,6 +221,18 @@ profile requiredArgs object_ = Object.selectionForCompositeField "profile" [ Argument.required "account" requiredArgs.account Encode.string ] object_ (identity >> Decode.nullable) +type alias SearchRequiredArguments = + { communityId : String } + + +search : + SearchRequiredArguments + -> SelectionSet decodesTo Cambiatus.Object.SearchResult + -> SelectionSet decodesTo RootQuery +search requiredArgs object_ = + Object.selectionForCompositeField "search" [ Argument.required "communityId" requiredArgs.communityId Encode.string ] object_ identity + + type alias TransferRequiredArguments = { input : Cambiatus.InputObject.TransferInput } From de375f8dabfb5d3aacab61dd4cce229ee37b48ee Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Sat, 23 Jan 2021 17:05:24 +0300 Subject: [PATCH 04/91] Send query with GraphQl --- src/elm/Session/LoggedIn.elm | 81 ++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index c8fcf8f31..96f644802 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -34,7 +34,11 @@ import Avatar import Browser.Dom as Dom import Browser.Events import Cambiatus.Object +import Cambiatus.Object.Action +import Cambiatus.Object.Product +import Cambiatus.Object.SearchResult import Cambiatus.Object.UnreadNotifications +import Cambiatus.Query import Cambiatus.Subscription as Subscription import Community import Eos exposing (Symbol) @@ -43,7 +47,8 @@ import Flags exposing (Flags) import Graphql.Document import Graphql.Http import Graphql.Operation exposing (RootSubscription) -import Graphql.SelectionSet exposing (SelectionSet) +import Graphql.OptionalArgument exposing (OptionalArgument(..)) +import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) import Html exposing (Html, a, button, div, footer, img, input, li, nav, p, span, text, ul) import Html.Attributes exposing (class, classList, placeholder, src, style, type_) import Html.Events exposing (onBlur, onClick, onFocus, onInput, onMouseEnter, onSubmit) @@ -782,13 +787,53 @@ type Msg | SelectCommunity Symbol (Cmd Msg) | HideFeedbackLocal | SearchStateChanged SearchState + | CompletedLoadSearchResults (Result (Graphql.Http.Error SearchResult) SearchResult) | GotRecentSearches String -searchFor : String -> Cmd msg -searchFor query = - -- TODO: send GraphQl query - Cmd.none +type alias SearchProduct = + { title : String + } + + +type alias SearchAction = + { description : String + } + + +type alias SearchResult = + { products : List SearchProduct + , actions : List SearchAction + } + + +searchFor : Symbol -> Shared -> String -> Cmd Msg +searchFor selectedCommunity shared queryString = + let + req = + { communityId = Eos.symbolToString selectedCommunity } + in + Api.Graphql.query + shared + (Cambiatus.Query.search req (searchResultSelectionSet queryString)) + CompletedLoadSearchResults + + +searchResultSelectionSet : String -> SelectionSet SearchResult Cambiatus.Object.SearchResult +searchResultSelectionSet queryString = + SelectionSet.succeed SearchResult + |> with (Cambiatus.Object.SearchResult.products (\_ -> { query = Present queryString }) productsSelectionSet) + |> with (Cambiatus.Object.SearchResult.actions (\_ -> { query = Present queryString }) actionsSelectionSet) + + +productsSelectionSet : SelectionSet SearchProduct Cambiatus.Object.Product +productsSelectionSet = + SelectionSet.map SearchProduct Cambiatus.Object.Product.title + + +actionsSelectionSet : SelectionSet SearchAction Cambiatus.Object.Action +actionsSelectionSet = + SelectionSet.map SearchAction Cambiatus.Object.Action.description update : Msg -> Model -> UpdateResult @@ -818,6 +863,13 @@ update msg model = Ignored -> UR.init model + CompletedLoadSearchResults res -> + let + _ = + Debug.log "CompletedLoadSearchResults res" res + in + model |> UR.init + GotRecentSearches queries -> case Decode.decodeString (list string) queries of Ok queryList -> @@ -927,17 +979,25 @@ update msg model = case searchState of Active query -> let + newRecentSearches : List String newRecentSearches = (query :: model.recentSearches) |> List.take 3 - encoded = - Encode.encode 0 (Encode.list Encode.string newRecentSearches) + storeRecentSearches : Cmd msg + storeRecentSearches = + newRecentSearches + |> Encode.list Encode.string + |> Encode.encode 0 + |> Ports.storeRecentSearches + + selectedCommunity = + model.selectedCommunity in { model | recentSearches = newRecentSearches } |> UR.init - |> UR.addCmd (searchFor query) - |> UR.addCmd (Ports.storeRecentSearches encoded) + |> UR.addCmd storeRecentSearches + |> UR.addCmd (searchFor selectedCommunity shared query) _ -> model @@ -1155,6 +1215,9 @@ msgToString msg = Ignored -> [ "Ignored" ] + CompletedLoadSearchResults _ -> + [ "CompletedLoadSearchResults" ] + GotRecentSearches _ -> [ "GotRecentSearches" ] From d76154608f54dcebc4b248c54a363ff85fa7d554 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Sat, 23 Jan 2021 17:07:37 +0300 Subject: [PATCH 05/91] Remove Debug log --- src/elm/Session/LoggedIn.elm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 96f644802..b1adadcb1 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -864,10 +864,6 @@ update msg model = UR.init model CompletedLoadSearchResults res -> - let - _ = - Debug.log "CompletedLoadSearchResults res" res - in model |> UR.init GotRecentSearches queries -> From 0f3d99599fda96948d58396ef6ce7d32f3d63d25 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 25 Jan 2021 10:25:05 +0300 Subject: [PATCH 06/91] Move search to separate module --- src/elm/Ports.elm | 4 +- src/elm/Search.elm | 241 +++++++++++++++++++++++++++++++++++ src/elm/Session/LoggedIn.elm | 203 +++-------------------------- src/index.js | 4 +- 4 files changed, 263 insertions(+), 189 deletions(-) create mode 100644 src/elm/Search.elm diff --git a/src/elm/Ports.elm b/src/elm/Ports.elm index ff7080b36..f3679ae80 100755 --- a/src/elm/Ports.elm +++ b/src/elm/Ports.elm @@ -1,7 +1,7 @@ port module Ports exposing ( JavascriptOut , JavascriptOutModel - , askForRecentSearches + , getRecentSearches , gotRecentSearches , javascriptInPort , javascriptOut @@ -71,7 +71,7 @@ port storeRecentSearches : String -> Cmd msg {-| Ping JS to send back the recent searches from the `localStorage`. -} -port askForRecentSearches : () -> Cmd msg +port getRecentSearches : () -> Cmd msg diff --git a/src/elm/Search.elm b/src/elm/Search.elm new file mode 100644 index 000000000..4a97440bd --- /dev/null +++ b/src/elm/Search.elm @@ -0,0 +1,241 @@ +module Search exposing (Model, Msg, init, subscriptions, update, view) + +import Api.Graphql +import Cambiatus.Object +import Cambiatus.Object.Action +import Cambiatus.Object.Product +import Cambiatus.Object.SearchResult +import Cambiatus.Query +import Eos exposing (Symbol) +import Graphql.Http +import Graphql.OptionalArgument exposing (OptionalArgument(..)) +import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) +import Html exposing (Html, div, input, li, span, text, ul) +import Html.Attributes exposing (class, placeholder, type_, value) +import Html.Events exposing (onBlur, onFocus, onInput, onSubmit) +import Icons +import Json.Decode as Decode exposing (Value, list, string) +import Json.Encode as Encode +import List.Extra as List +import Ports +import Session.Shared exposing (Shared) + + + +-- MODEL + + +type alias Model = + { searchState : SearchState + , recentSearches : List String + , searchText : String + , selectedCommunity : Symbol + } + + +init : Symbol -> Model +init selectedCommunity = + { searchState = Inactive + , recentSearches = [] + , searchText = "" + , selectedCommunity = selectedCommunity + } + + + +-- TYPES + + +type SearchState + = Inactive + | Active String + | ResultsShowed + + + +-- GRAPHQL + + +sendSearchQuery : Symbol -> Shared -> String -> Cmd Msg +sendSearchQuery selectedCommunity shared queryString = + let + req = + { communityId = Eos.symbolToString selectedCommunity } + in + Api.Graphql.query + shared + (Cambiatus.Query.search req (searchResultSelectionSet queryString)) + SearchResultsLoaded + + +type alias SearchResult = + { products : List SearchProduct + , actions : List SearchAction + } + + +type alias SearchProduct = + { title : String + } + + +type alias SearchAction = + { description : String + } + + +searchResultSelectionSet : String -> SelectionSet SearchResult Cambiatus.Object.SearchResult +searchResultSelectionSet queryString = + SelectionSet.succeed SearchResult + |> with (Cambiatus.Object.SearchResult.products (\_ -> { query = Present queryString }) productsSelectionSet) + |> with (Cambiatus.Object.SearchResult.actions (\_ -> { query = Present queryString }) actionsSelectionSet) + + +productsSelectionSet : SelectionSet SearchProduct Cambiatus.Object.Product +productsSelectionSet = + SelectionSet.map SearchProduct Cambiatus.Object.Product.title + + +actionsSelectionSet : SelectionSet SearchAction Cambiatus.Object.Action +actionsSelectionSet = + SelectionSet.map SearchAction Cambiatus.Object.Action.description + + + +-- UPDATE + + +type Msg + = StateChanged SearchState + | GotRecentSearches String + | SearchResultsLoaded (Result (Graphql.Http.Error SearchResult) SearchResult) + | QuerySubmitted SearchState + + +update : Shared -> Model -> Msg -> ( Model, Cmd Msg ) +update shared model msg = + case msg of + SearchResultsLoaded res -> + let + _ = + Debug.log "CompletedLoadSearchResults" res + in + ( model, Cmd.none ) + + GotRecentSearches queries -> + case Decode.decodeString (list string) queries of + Ok queryList -> + ( { model | recentSearches = queryList }, Cmd.none ) + + Err _ -> + ( model, Cmd.none ) + + StateChanged state -> + let + searchText = + case state of + Active q -> + q + + _ -> + model.searchText + in + ( { model + | searchState = state + , searchText = searchText + } + , Cmd.none + ) + + QuerySubmitted searchState -> + case searchState of + Active query -> + let + newRecentSearches : List String + newRecentSearches = + (query :: model.recentSearches) + |> List.unique + |> List.take 3 + + storeRecentSearches : Cmd msg + storeRecentSearches = + newRecentSearches + |> Encode.list Encode.string + |> Encode.encode 0 + |> Ports.storeRecentSearches + + selectedCommunity = + model.selectedCommunity + in + ( { model | recentSearches = newRecentSearches } + , Cmd.batch [ storeRecentSearches, sendSearchQuery selectedCommunity shared query ] + ) + + _ -> + ( model, Cmd.none ) + + + +-- VIEW + + +view : Model -> Html Msg +view model = + let + iconColor = + case model.searchState of + Inactive -> + "fill-gray" + + _ -> + "fill-indigo" + + viewRecentSearches = + case model.searchState of + Active _ -> + let + viewQuery q = + div [ class "leading-10" ] + [ Icons.clock "fill-gray inline-block align-middle mr-3" + , span [ class "inline align-middle" ] [ text q ] + ] + in + div [ class "fixed bg-white w-full left-0 px-2 py-4 border-2" ] + [ span [ class "font-bold" ] [ text "Recently searched" ] + , ul [] + [ li [] + (List.map viewQuery model.recentSearches) + ] + ] + + _ -> + text "" + in + div [ class "w-full" ] + [ Html.form + [ class "w-full relative block mt-2" + , onSubmit (QuerySubmitted model.searchState) + ] + [ input + [ type_ "search" + , class "w-full form-input rounded-full bg-gray-100 pl-10 m-0 block" + , placeholder "Find friends and communities" + , value model.searchText + , onFocus (StateChanged (Active model.searchText)) + , onBlur (StateChanged Inactive) + , onInput (\q -> StateChanged (Active q)) + ] + [] + , Icons.search <| "absolute top-0 left-0 mt-2 ml-2" ++ " " ++ iconColor + ] + , viewRecentSearches + ] + + + +-- SUBSCRIPTION + + +subscriptions : Sub Msg +subscriptions = + Ports.gotRecentSearches GotRecentSearches diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index b1adadcb1..479222aeb 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -21,7 +21,6 @@ module Session.LoggedIn exposing , msgToString , profile , readAllNotifications - , searchFor , subscriptions , update , view @@ -34,11 +33,7 @@ import Avatar import Browser.Dom as Dom import Browser.Events import Cambiatus.Object -import Cambiatus.Object.Action -import Cambiatus.Object.Product -import Cambiatus.Object.SearchResult import Cambiatus.Object.UnreadNotifications -import Cambiatus.Query import Cambiatus.Subscription as Subscription import Community import Eos exposing (Symbol) @@ -47,21 +42,21 @@ import Flags exposing (Flags) import Graphql.Document import Graphql.Http import Graphql.Operation exposing (RootSubscription) -import Graphql.OptionalArgument exposing (OptionalArgument(..)) -import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) +import Graphql.SelectionSet exposing (SelectionSet) import Html exposing (Html, a, button, div, footer, img, input, li, nav, p, span, text, ul) import Html.Attributes exposing (class, classList, placeholder, src, style, type_) import Html.Events exposing (onBlur, onClick, onFocus, onInput, onMouseEnter, onSubmit) import Http import I18Next exposing (Delims(..), Translations, t) import Icons -import Json.Decode as Decode exposing (Value, list, string) +import Json.Decode as Decode exposing (Value) import Json.Encode as Encode exposing (Value) import List.Extra as List import Notification exposing (Notification) import Ports import Profile exposing (Model) import Route exposing (Route) +import Search import Session.Shared as Shared exposing (Shared) import Shop import Task @@ -84,6 +79,7 @@ init shared accountName flags = , Cmd.batch [ Api.Graphql.query shared (Profile.query accountName) CompletedLoadProfile , Api.Graphql.query shared (Community.settingsQuery flags.selectedCommunity) CompletedLoadSettings + , Ports.getRecentSearches () ] ) @@ -124,7 +120,7 @@ subscriptions model = Sub.batch [ Sub.map GotAuthMsg (Auth.subscriptions model.auth) , Sub.map KeyDown (Browser.Events.onKeyDown (Decode.field "key" Decode.string)) - , Ports.gotRecentSearches GotRecentSearches + , Sub.map SearchMsg Search.subscriptions ] @@ -139,7 +135,6 @@ type alias Model = , selectedCommunity : Symbol , showUserNav : Bool , showLanguageItems : Bool - , searchText : String , showNotificationModal : Bool , showMainNav : Bool , notification : Notification.Model @@ -151,8 +146,7 @@ type alias Model = , hasShop : FeatureStatus , hasObjectives : FeatureStatus , hasKyc : FeatureStatus - , searchState : SearchState - , recentSearches : List String + , searchModel : Search.Model } @@ -169,7 +163,6 @@ initModel shared authModel accountName selectedCommunity = , selectedCommunity = selectedCommunity , showUserNav = False , showLanguageItems = False - , searchText = "" , showNotificationModal = False , showMainNav = False , notification = Notification.init @@ -181,8 +174,7 @@ initModel shared authModel accountName selectedCommunity = , hasShop = FeatureLoading , hasObjectives = FeatureLoading , hasKyc = FeatureLoading - , searchState = Inactive - , recentSearches = [] + , searchModel = Search.init selectedCommunity } @@ -399,63 +391,6 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = ] -type SearchState - = Inactive - | Active String - | ResultsShowed - - -viewSearch model = - let - iconColor = - case model.searchState of - Inactive -> - "fill-gray" - - _ -> - "fill-indigo" - - viewRecentSearches = - case model.searchState of - Active _ -> - let - viewQuery q = - div [ class "leading-10" ] - [ Icons.clock "fill-gray inline-block align-middle mr-3" - , span [ class "inline align-middle" ] [ text q ] - ] - in - div [ class "fixed bg-white w-full left-0 px-2 py-4 border-2" ] - [ span [ class "font-bold" ] [ text "Recently searched" ] - , ul [] - [ li [] - (List.map viewQuery model.recentSearches) - ] - ] - - _ -> - text "" - in - div [ class "w-full" ] - [ Html.form - [ class "w-full relative block mt-2" - , onSubmit (SubmittedSearch model.searchState) - ] - [ input - [ type_ "search" - , class "w-full form-input rounded-full bg-gray-100 pl-10 m-0 block" - , placeholder "Find friends and communities" - , onFocus (SearchStateChanged (Active "")) - , onBlur (SearchStateChanged Inactive) - , onInput (\query -> SearchStateChanged (Active query)) - ] - [] - , Icons.search <| "absolute top-0 left-0 mt-2 ml-2" ++ " " ++ iconColor - ] - , viewRecentSearches - ] - - viewHeader : Model -> Profile.Model -> Html Msg viewHeader ({ shared } as model) profile_ = let @@ -562,7 +497,8 @@ viewHeader ({ shared } as model) profile_ = ] ] ] - , viewSearch model + , Search.view model.searchModel + |> Html.map SearchMsg ] @@ -771,8 +707,6 @@ type Msg | CompletedLoadSettings (Result (Graphql.Http.Error (Maybe Community.Settings)) (Maybe Community.Settings)) | ClickedTryAgainProfile Eos.Name | ClickedLogout - | EnteredSearch String - | SubmittedSearch SearchState | ShowNotificationModal Bool | ShowUserNav Bool | ShowMainNav Bool @@ -786,54 +720,7 @@ type Msg | CloseCommunitySelector | SelectCommunity Symbol (Cmd Msg) | HideFeedbackLocal - | SearchStateChanged SearchState - | CompletedLoadSearchResults (Result (Graphql.Http.Error SearchResult) SearchResult) - | GotRecentSearches String - - -type alias SearchProduct = - { title : String - } - - -type alias SearchAction = - { description : String - } - - -type alias SearchResult = - { products : List SearchProduct - , actions : List SearchAction - } - - -searchFor : Symbol -> Shared -> String -> Cmd Msg -searchFor selectedCommunity shared queryString = - let - req = - { communityId = Eos.symbolToString selectedCommunity } - in - Api.Graphql.query - shared - (Cambiatus.Query.search req (searchResultSelectionSet queryString)) - CompletedLoadSearchResults - - -searchResultSelectionSet : String -> SelectionSet SearchResult Cambiatus.Object.SearchResult -searchResultSelectionSet queryString = - SelectionSet.succeed SearchResult - |> with (Cambiatus.Object.SearchResult.products (\_ -> { query = Present queryString }) productsSelectionSet) - |> with (Cambiatus.Object.SearchResult.actions (\_ -> { query = Present queryString }) actionsSelectionSet) - - -productsSelectionSet : SelectionSet SearchProduct Cambiatus.Object.Product -productsSelectionSet = - SelectionSet.map SearchProduct Cambiatus.Object.Product.title - - -actionsSelectionSet : SelectionSet SearchAction Cambiatus.Object.Action -actionsSelectionSet = - SelectionSet.map SearchAction Cambiatus.Object.Action.description + | SearchMsg Search.Msg update : Msg -> Model -> UpdateResult @@ -863,23 +750,14 @@ update msg model = Ignored -> UR.init model - CompletedLoadSearchResults res -> - model |> UR.init - - GotRecentSearches queries -> - case Decode.decodeString (list string) queries of - Ok queryList -> - { model | recentSearches = queryList } - |> UR.init - - Err _ -> - model |> UR.init - - SearchStateChanged state -> - { model | searchState = state } + SearchMsg searchMsg -> + let + ( searchModel, searchCmd ) = + Search.update shared model.searchModel searchMsg + in + { model | searchModel = searchModel } |> UR.init - -- TODO: Retrieve recent searches only when state changed to show the dropdown - |> UR.addCmd (Ports.askForRecentSearches ()) + |> UR.addCmd (Cmd.map SearchMsg searchCmd) CompletedLoadTranslation lang (Ok transl) -> case model.profile of @@ -968,37 +846,6 @@ update msg model = ] } - EnteredSearch s -> - UR.init { model | searchText = s } - - SubmittedSearch searchState -> - case searchState of - Active query -> - let - newRecentSearches : List String - newRecentSearches = - (query :: model.recentSearches) - |> List.take 3 - - storeRecentSearches : Cmd msg - storeRecentSearches = - newRecentSearches - |> Encode.list Encode.string - |> Encode.encode 0 - |> Ports.storeRecentSearches - - selectedCommunity = - model.selectedCommunity - in - { model | recentSearches = newRecentSearches } - |> UR.init - |> UR.addCmd storeRecentSearches - |> UR.addCmd (searchFor selectedCommunity shared query) - - _ -> - model - |> UR.init - ShowNotificationModal b -> UR.init { closeAllModals @@ -1211,14 +1058,8 @@ msgToString msg = Ignored -> [ "Ignored" ] - CompletedLoadSearchResults _ -> - [ "CompletedLoadSearchResults" ] - - GotRecentSearches _ -> - [ "GotRecentSearches" ] - - SearchStateChanged _ -> - [ "SearchStateChanged" ] + SearchMsg _ -> + [ "SearchMsg" ] CompletedLoadTranslation _ r -> [ "CompletedLoadTranslation", UR.resultToString r ] @@ -1238,12 +1079,6 @@ msgToString msg = ClickedLogout -> [ "ClickedLogout" ] - EnteredSearch _ -> - [ "EnteredSearch" ] - - SubmittedSearch _ -> - [ "SubmittedSearch" ] - ShowNotificationModal _ -> [ "ShowNotificationModal" ] diff --git a/src/index.js b/src/index.js index 8912a906f..cfb3c863c 100755 --- a/src/index.js +++ b/src/index.js @@ -190,9 +190,7 @@ app.ports.storeRecentSearches.subscribe(query => ) // RETRIEVE RECENT SEARCHES -// Elm pings JS with `askForRecentSearches` command when search field is focused, -// JS sends back recent searches to Elm via `gotRecentSearches` subscription. -app.ports.askForRecentSearches.subscribe(() => { +app.ports.getRecentSearches.subscribe(() => { app.ports.gotRecentSearches.send(window.localStorage.getItem(RECENT_SEARCHES) || '[]') }) From c435d8a45ea7d62c7b3cba12564627a18014d2cc Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 25 Jan 2021 10:38:37 +0300 Subject: [PATCH 07/91] Send recent search item on click --- src/elm/Search.elm | 106 +++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index 4a97440bd..c7080efab 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -12,7 +12,7 @@ import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) import Html exposing (Html, div, input, li, span, text, ul) import Html.Attributes exposing (class, placeholder, type_, value) -import Html.Events exposing (onBlur, onFocus, onInput, onSubmit) +import Html.Events exposing (onBlur, onClick, onFocus, onInput, onSubmit) import Icons import Json.Decode as Decode exposing (Value, list, string) import Json.Encode as Encode @@ -108,13 +108,17 @@ actionsSelectionSet = type Msg = StateChanged SearchState | GotRecentSearches String + | RecentSearchClicked String | SearchResultsLoaded (Result (Graphql.Http.Error SearchResult) SearchResult) - | QuerySubmitted SearchState + | QuerySubmitted update : Shared -> Model -> Msg -> ( Model, Cmd Msg ) update shared model msg = case msg of + RecentSearchClicked q -> + update shared { model | searchText = q } QuerySubmitted + SearchResultsLoaded res -> let _ = @@ -147,32 +151,30 @@ update shared model msg = , Cmd.none ) - QuerySubmitted searchState -> - case searchState of - Active query -> - let - newRecentSearches : List String - newRecentSearches = - (query :: model.recentSearches) - |> List.unique - |> List.take 3 - - storeRecentSearches : Cmd msg - storeRecentSearches = - newRecentSearches - |> Encode.list Encode.string - |> Encode.encode 0 - |> Ports.storeRecentSearches - - selectedCommunity = - model.selectedCommunity - in - ( { model | recentSearches = newRecentSearches } - , Cmd.batch [ storeRecentSearches, sendSearchQuery selectedCommunity shared query ] - ) - - _ -> - ( model, Cmd.none ) + QuerySubmitted -> + let + newRecentSearches : List String + newRecentSearches = + (model.searchText :: model.recentSearches) + |> List.unique + |> List.take 3 + + storeRecentSearches : Cmd msg + storeRecentSearches = + newRecentSearches + |> Encode.list Encode.string + |> Encode.encode 0 + |> Ports.storeRecentSearches + + selectedCommunity = + model.selectedCommunity + in + ( { model | recentSearches = newRecentSearches } + , Cmd.batch + [ storeRecentSearches + , sendSearchQuery selectedCommunity shared model.searchText + ] + ) @@ -189,49 +191,49 @@ view model = _ -> "fill-indigo" - - viewRecentSearches = - case model.searchState of - Active _ -> - let - viewQuery q = - div [ class "leading-10" ] - [ Icons.clock "fill-gray inline-block align-middle mr-3" - , span [ class "inline align-middle" ] [ text q ] - ] - in - div [ class "fixed bg-white w-full left-0 px-2 py-4 border-2" ] - [ span [ class "font-bold" ] [ text "Recently searched" ] - , ul [] - [ li [] - (List.map viewQuery model.recentSearches) - ] - ] - - _ -> - text "" in div [ class "w-full" ] [ Html.form [ class "w-full relative block mt-2" - , onSubmit (QuerySubmitted model.searchState) + , onSubmit QuerySubmitted ] [ input [ type_ "search" , class "w-full form-input rounded-full bg-gray-100 pl-10 m-0 block" , placeholder "Find friends and communities" , value model.searchText - , onFocus (StateChanged (Active model.searchText)) + , onFocus (StateChanged <| Active model.searchText) , onBlur (StateChanged Inactive) , onInput (\q -> StateChanged (Active q)) ] [] , Icons.search <| "absolute top-0 left-0 mt-2 ml-2" ++ " " ++ iconColor ] - , viewRecentSearches + , viewRecentSearches model ] +viewRecentSearches : Model -> Html Msg +viewRecentSearches model = + let + viewQuery q = + li [ class "leading-10", onClick (RecentSearchClicked q) ] + [ Icons.clock "fill-gray inline-block align-middle mr-3" + , span [ class "inline align-middle" ] [ text q ] + ] + in + case model.searchState of + Active _ -> + div [ class "fixed bg-white w-full left-0 px-2 py-4 border-2" ] + [ span [ class "font-bold" ] [ text "Recently searched" ] + , ul [] + (List.map viewQuery model.recentSearches) + ] + + _ -> + text "" + + -- SUBSCRIPTION From 31d38ab46c01a6d992026eee960f740d11ff4369 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 25 Jan 2021 12:15:22 +0300 Subject: [PATCH 08/91] Show recent queries instead page body if search is activated --- src/elm/Search.elm | 107 ++++++++++++++++++++------------ src/elm/Session/LoggedIn.elm | 117 +++++++++++++++++++++-------------- 2 files changed, 137 insertions(+), 87 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index c7080efab..93c194c90 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -1,4 +1,4 @@ -module Search exposing (Model, Msg, init, subscriptions, update, view) +module Search exposing (Model, Msg, init, isActive, subscriptions, update, viewForm, viewRecentQueries) import Api.Graphql import Cambiatus.Object @@ -26,18 +26,18 @@ import Session.Shared exposing (Shared) type alias Model = - { searchState : SearchState - , recentSearches : List String - , searchText : String + { state : State + , recentQueries : List String + , queryText : String , selectedCommunity : Symbol } init : Symbol -> Model init selectedCommunity = - { searchState = Inactive - , recentSearches = [] - , searchText = "" + { state = Inactive + , recentQueries = [] + , queryText = "" , selectedCommunity = selectedCommunity } @@ -46,7 +46,7 @@ init selectedCommunity = -- TYPES -type SearchState +type State = Inactive | Active String | ResultsShowed @@ -106,7 +106,7 @@ actionsSelectionSet = type Msg - = StateChanged SearchState + = StateChanged State | GotRecentSearches String | RecentSearchClicked String | SearchResultsLoaded (Result (Graphql.Http.Error SearchResult) SearchResult) @@ -117,7 +117,11 @@ update : Shared -> Model -> Msg -> ( Model, Cmd Msg ) update shared model msg = case msg of RecentSearchClicked q -> - update shared { model | searchText = q } QuerySubmitted + let + _ = + Debug.log "q" q + in + update shared { model | queryText = q } QuerySubmitted SearchResultsLoaded res -> let @@ -129,7 +133,7 @@ update shared model msg = GotRecentSearches queries -> case Decode.decodeString (list string) queries of Ok queryList -> - ( { model | recentSearches = queryList }, Cmd.none ) + ( { model | recentQueries = queryList }, Cmd.none ) Err _ -> ( model, Cmd.none ) @@ -142,11 +146,11 @@ update shared model msg = q _ -> - model.searchText + model.queryText in ( { model - | searchState = state - , searchText = searchText + | state = state + , queryText = searchText } , Cmd.none ) @@ -155,7 +159,7 @@ update shared model msg = let newRecentSearches : List String newRecentSearches = - (model.searchText :: model.recentSearches) + (model.queryText :: model.recentQueries) |> List.unique |> List.take 3 @@ -169,10 +173,10 @@ update shared model msg = selectedCommunity = model.selectedCommunity in - ( { model | recentSearches = newRecentSearches } + ( { model | recentQueries = newRecentSearches } , Cmd.batch [ storeRecentSearches - , sendSearchQuery selectedCommunity shared model.searchText + , sendSearchQuery selectedCommunity shared model.queryText ] ) @@ -181,59 +185,82 @@ update shared model msg = -- VIEW -view : Model -> Html Msg -view model = +viewForm : Model -> Html Msg +viewForm model = let iconColor = - case model.searchState of + case model.state of Inactive -> "fill-gray" _ -> "fill-indigo" in - div [ class "w-full" ] + div [ class "w-full px-4" ] [ Html.form - [ class "w-full relative block mt-2" + [ class "w-full mt-2 flex" , onSubmit QuerySubmitted ] - [ input - [ type_ "search" - , class "w-full form-input rounded-full bg-gray-100 pl-10 m-0 block" - , placeholder "Find friends and communities" - , value model.searchText - , onFocus (StateChanged <| Active model.searchText) - , onBlur (StateChanged Inactive) - , onInput (\q -> StateChanged (Active q)) + [ div [ class "relative w-full" ] + [ input + [ type_ "search" + , class "w-full form-input rounded-full bg-gray-100 pl-10 m-0 block" + , placeholder "Find friends and communities" + , value model.queryText + , onFocus (StateChanged <| Active model.queryText) + , onInput (\q -> StateChanged (Active q)) + ] + [] + , Icons.search <| "absolute top-0 left-0 mt-2 ml-2" ++ " " ++ iconColor ] - [] - , Icons.search <| "absolute top-0 left-0 mt-2 ml-2" ++ " " ++ iconColor + , case model.state of + Active _ -> + span + [ class "text-orange-300 leading-10 inline-block ml-3" + , onClick (StateChanged Inactive) + ] + [ text "cancel" ] + + _ -> + text "" ] - , viewRecentSearches model ] -viewRecentSearches : Model -> Html Msg -viewRecentSearches model = +viewRecentQueries : Model -> Html Msg +viewRecentQueries model = let viewQuery q = - li [ class "leading-10", onClick (RecentSearchClicked q) ] + li + [ class "leading-10 hover:text-orange-500 cursor-pointer" + , onClick (RecentSearchClicked q) + ] [ Icons.clock "fill-gray inline-block align-middle mr-3" , span [ class "inline align-middle" ] [ text q ] ] in - case model.searchState of + case model.state of Active _ -> - div [ class "fixed bg-white w-full left-0 px-2 py-4 border-2" ] + div [ class "w-full left-0 p-4" ] [ span [ class "font-bold" ] [ text "Recently searched" ] - , ul [] - (List.map viewQuery model.recentSearches) + , ul [ class "text-gray-900" ] + (List.map viewQuery model.recentQueries) ] _ -> text "" +isActive : Model -> Bool +isActive model = + case model.state of + Active _ -> + True + + _ -> + False + + -- SUBSCRIPTION diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 479222aeb..14db002ed 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -301,7 +301,53 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = let { t } = shared.translators + in + div + [ class "min-h-screen flex flex-col" ] + ([ div [ class "bg-white" ] + [ div [ class "container mx-auto" ] + [ viewHeader model profile_ |> Html.map thisMsg + , -- Search form is separated from search results because it needs to + -- be between community selector and user dropdown on Desktops. + Search.viewForm model.searchModel + |> Html.map (SearchMsg >> thisMsg) + , if Search.isActive model.searchModel then + text "" + else + viewMainMenu page model |> Html.map thisMsg + ] + ] + ] + ++ (if Search.isActive model.searchModel then + [ div [ class "bg-white flex-grow" ] + [ Search.viewRecentQueries model.searchModel + |> Html.map (SearchMsg >> thisMsg) + ] + ] + + else + viewPageBody t model profile_ page content thisMsg + ) + ++ [ viewFooter shared + , Modal.initWith + { closeMsg = ClosedAuthModal + , isVisible = model.showAuthModal + } + |> Modal.withBody + (Auth.view True shared model.auth + |> List.map (Html.map GotAuthMsg) + ) + |> Modal.toHtml + |> Html.map thisMsg + , communitySelectorModal model + |> Html.map thisMsg + ] + ) + + +viewPageBody t model profile_ page content thisMsg = + let hasUserKycFilled = case profile_.kyc of Just _ -> @@ -342,53 +388,32 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = [] ] in - div - [ class "min-h-screen flex flex-col" ] - [ div [ class "bg-white" ] - [ div [ class "container mx-auto" ] - [ viewHeader model profile_ |> Html.map thisMsg - , viewMainMenu page model |> Html.map thisMsg - ] - ] - , case model.feedback of - Show status message -> - viewFeedback status message |> Html.map thisMsg + [ case model.feedback of + Show status message -> + viewFeedback status message |> Html.map thisMsg + + Hidden -> + text "" + , div [ class "flex-grow" ] + [ case model.hasKyc of + FeatureLoading -> + div [ class "full-spinner-container h-full" ] + [ div [ class "spinner spinner--delay mt-8" ] [] ] + + FeatureLoaded isKycEnabled -> + let + isContentAllowed = + List.member page availableWithoutKyc + || not isKycEnabled + || (isKycEnabled && hasUserKycFilled) + in + if isContentAllowed then + content - Hidden -> - text "" - , div [ class "flex-grow" ] - [ case model.hasKyc of - FeatureLoading -> - div [ class "full-spinner-container h-full" ] - [ div [ class "spinner spinner--delay mt-8" ] [] ] - - FeatureLoaded isKycEnabled -> - let - isContentAllowed = - List.member page availableWithoutKyc - || not isKycEnabled - || (isKycEnabled && hasUserKycFilled) - in - if isContentAllowed then - content - - else - viewKycRestriction - ] - , viewFooter shared - , Modal.initWith - { closeMsg = ClosedAuthModal - , isVisible = model.showAuthModal - } - |> Modal.withBody - (Auth.view True shared model.auth - |> List.map (Html.map GotAuthMsg) - ) - |> Modal.toHtml - |> Html.map thisMsg - , communitySelectorModal model - |> Html.map thisMsg + else + viewKycRestriction ] + ] viewHeader : Model -> Profile.Model -> Html Msg @@ -497,8 +522,6 @@ viewHeader ({ shared } as model) profile_ = ] ] ] - , Search.view model.searchModel - |> Html.map SearchMsg ] From 914c61654081926cd42253bc02ab8467b01e763f Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 25 Jan 2021 13:01:29 +0300 Subject: [PATCH 09/91] Adjust flag icon --- src/elm/Icons.elm | 13 +++++++++++-- src/elm/Page/Community/Objectives.elm | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/elm/Icons.elm b/src/elm/Icons.elm index 8793272dc..7d5c189d2 100644 --- a/src/elm/Icons.elm +++ b/src/elm/Icons.elm @@ -174,8 +174,17 @@ languages class_ = flag : String -> Svg msg flag class_ = svg [ width "40", height "46", viewBox "0 0 40 46", class class_ ] - [ Svg.path [ d "M0.439542 11.1667L19.5604 0.110077C19.829 -0.0366924 20.1709 -0.0366924 20.4395 0.110077L39.5604 11.1667C39.829 11.3135 40 11.607 40 11.925V34.0628C40 34.3808 39.829 34.6743 39.5604 34.8211L20.4395 45.8777C20.293 45.9511 20.1465 46 20 46C19.8535 46 19.7069 45.9511 19.5604 45.8777L0.439542 34.8211C0.170922 34.6743 0 34.3808 0 34.0628V11.925C0 11.607 0.170922 11.3135 0.439542 11.1667Z", fill "#8ACC9E" ] [] - , Svg.path [ fillRule "evenodd", clipRule "evenodd", d "M27.0759 19.7659L29.3273 16.2036C29.6024 15.7688 29.599 15.2358 29.3182 14.8039C29.0375 14.372 28.5217 14.1064 27.9639 14.1065H13.2858C13.2858 13.7187 12.935 13.4043 12.5023 13.4043C12.0695 13.4043 11.7188 13.7187 11.7188 14.1065V31.8947C11.7188 32.2825 12.0695 32.5969 12.5023 32.5969C12.935 32.5969 13.2858 32.2825 13.2858 31.8947V25.8092H27.9117C28.4543 25.8086 28.9578 25.5565 29.2429 25.1428C29.528 24.7291 29.554 24.2127 29.3116 23.7776L27.0759 19.7659ZM13.2858 24.4049V15.5108H27.9117L25.2634 19.7238L27.8751 24.4049H13.2858Z", fill "white" ] [] + [ Svg.path + [ d "M0.439542 11.1667L19.5604 0.110077C19.829 -0.0366924 20.1709 -0.0366924 20.4395 0.110077L39.5604 11.1667C39.829 11.3135 40 11.607 40 11.925V34.0628C40 34.3808 39.829 34.6743 39.5604 34.8211L20.4395 45.8777C20.293 45.9511 20.1465 46 20 46C19.8535 46 19.7069 45.9511 19.5604 45.8777L0.439542 34.8211C0.170922 34.6743 0 34.3808 0 34.0628V11.925C0 11.607 0.170922 11.3135 0.439542 11.1667Z" + ] + [] + , Svg.path + [ fillRule "evenodd" + , clipRule "evenodd" + , d "M27.0759 19.7659L29.3273 16.2036C29.6024 15.7688 29.599 15.2358 29.3182 14.8039C29.0375 14.372 28.5217 14.1064 27.9639 14.1065H13.2858C13.2858 13.7187 12.935 13.4043 12.5023 13.4043C12.0695 13.4043 11.7188 13.7187 11.7188 14.1065V31.8947C11.7188 32.2825 12.0695 32.5969 12.5023 32.5969C12.935 32.5969 13.2858 32.2825 13.2858 31.8947V25.8092H27.9117C28.4543 25.8086 28.9578 25.5565 29.2429 25.1428C29.528 24.7291 29.554 24.2127 29.3116 23.7776L27.0759 19.7659ZM13.2858 24.4049V15.5108H27.9117L25.2634 19.7238L27.8751 24.4049H13.2858Z" + , fill "white" + ] + [] ] diff --git a/src/elm/Page/Community/Objectives.elm b/src/elm/Page/Community/Objectives.elm index 2b8e33f0a..517223460 100644 --- a/src/elm/Page/Community/Objectives.elm +++ b/src/elm/Page/Community/Objectives.elm @@ -236,7 +236,7 @@ viewAction ({ shared } as loggedIn) model objectiveId action = loggedIn.shared.translators.tr r_id replaces in div [ class "flex flex-wrap sm:flex-no-wrap mt-8 mb-4 relative bg-purple-500 rounded-lg px-4 py-5" ] - [ div [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full" ] + [ div [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full fill-green" ] , div [ class "w-full" ] [ p [ class "text-white" ] [ text action.description ] , div [ class "flex flex-wrap my-6 -mx-2 items-center" ] From 7b78bccb01e2f31e40b6546574b9e7a79e80e5e4 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 25 Jan 2021 13:01:40 +0300 Subject: [PATCH 10/91] Adjust svg fill colors --- tailwind.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tailwind.config.js b/tailwind.config.js index a005a29f7..d114c99eb 100755 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -28,7 +28,9 @@ module.exports = { }), fill: { gray: '#DADADA', - indigo: '#45469B' + indigo: '#45469B', + black: '#000', + green: '#8ACC9E' }, // Colors used on the 'Design System' colors: { From a8fe191b24cbe03619a310ff4b620f9076fd64d3 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 25 Jan 2021 15:18:38 +0300 Subject: [PATCH 11/91] Add search results with tabs --- src/elm/Search.elm | 185 ++++++++++++++++++++++++++++++----- src/elm/Session/LoggedIn.elm | 8 +- 2 files changed, 164 insertions(+), 29 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index 93c194c90..a312c715c 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -1,4 +1,4 @@ -module Search exposing (Model, Msg, init, isActive, subscriptions, update, viewForm, viewRecentQueries) +module Search exposing (Model, Msg, init, isActive, subscriptions, update, viewForm, viewRecentQueries, viewSearchBody) import Api.Graphql import Cambiatus.Object @@ -10,11 +10,11 @@ import Eos exposing (Symbol) import Graphql.Http import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, div, input, li, span, text, ul) -import Html.Attributes exposing (class, placeholder, type_, value) -import Html.Events exposing (onBlur, onClick, onFocus, onInput, onSubmit) +import Html exposing (Html, button, div, h3, img, input, li, p, span, strong, text, ul) +import Html.Attributes exposing (class, placeholder, src, type_, value) +import Html.Events exposing (onClick, onFocus, onInput, onSubmit) import Icons -import Json.Decode as Decode exposing (Value, list, string) +import Json.Decode as Decode exposing (list, string) import Json.Encode as Encode import List.Extra as List import Ports @@ -30,6 +30,7 @@ type alias Model = , recentQueries : List String , queryText : String , selectedCommunity : Symbol + , found : Maybe SearchResult } @@ -39,6 +40,7 @@ init selectedCommunity = , recentQueries = [] , queryText = "" , selectedCommunity = selectedCommunity + , found = Nothing } @@ -50,6 +52,8 @@ type State = Inactive | Active String | ResultsShowed + | OffersShowed + | ActionsShowed @@ -69,13 +73,15 @@ sendSearchQuery selectedCommunity shared queryString = type alias SearchResult = - { products : List SearchProduct + { offers : List SearchOffer , actions : List SearchAction } -type alias SearchProduct = +type alias SearchOffer = { title : String + , price : Float + , image : Maybe String } @@ -91,9 +97,12 @@ searchResultSelectionSet queryString = |> with (Cambiatus.Object.SearchResult.actions (\_ -> { query = Present queryString }) actionsSelectionSet) -productsSelectionSet : SelectionSet SearchProduct Cambiatus.Object.Product +productsSelectionSet : SelectionSet SearchOffer Cambiatus.Object.Product productsSelectionSet = - SelectionSet.map SearchProduct Cambiatus.Object.Product.title + SelectionSet.map3 SearchOffer + Cambiatus.Object.Product.title + Cambiatus.Object.Product.price + Cambiatus.Object.Product.image actionsSelectionSet : SelectionSet SearchAction Cambiatus.Object.Action @@ -111,24 +120,31 @@ type Msg | RecentSearchClicked String | SearchResultsLoaded (Result (Graphql.Http.Error SearchResult) SearchResult) | QuerySubmitted + | ShowOffersClicked + | ShowActionsClicked update : Shared -> Model -> Msg -> ( Model, Cmd Msg ) update shared model msg = case msg of RecentSearchClicked q -> - let - _ = - Debug.log "q" q - in update shared { model | queryText = q } QuerySubmitted + ShowOffersClicked -> + ( { model | state = OffersShowed }, Cmd.none ) + + ShowActionsClicked -> + ( { model | state = ActionsShowed }, Cmd.none ) + SearchResultsLoaded res -> - let - _ = - Debug.log "CompletedLoadSearchResults" res - in - ( model, Cmd.none ) + case res of + Ok searchResult -> + ( { model | found = Just searchResult } + , Cmd.none + ) + + Err _ -> + ( model, Cmd.none ) GotRecentSearches queries -> case Decode.decodeString (list string) queries of @@ -198,13 +214,13 @@ viewForm model = in div [ class "w-full px-4" ] [ Html.form - [ class "w-full mt-2 flex" + [ class "w-full mt-2 flex items-center" , onSubmit QuerySubmitted ] [ div [ class "relative w-full" ] [ input [ type_ "search" - , class "w-full form-input rounded-full bg-gray-100 pl-10 m-0 block" + , class "w-full form-input rounded-full bg-gray-100 pl-10 m-0" , placeholder "Find friends and communities" , value model.queryText , onFocus (StateChanged <| Active model.queryText) @@ -216,7 +232,7 @@ viewForm model = , case model.state of Active _ -> span - [ class "text-orange-300 leading-10 inline-block ml-3" + [ class "text-orange-300 ml-3" , onClick (StateChanged Inactive) ] [ text "cancel" ] @@ -227,6 +243,18 @@ viewForm model = ] +viewSearchBody : Model -> Html Msg +viewSearchBody model = + div [ class "container mx-auto p-4" ] + [ case model.found of + Just results -> + viewResults model.state results + + Nothing -> + viewRecentQueries model + ] + + viewRecentQueries : Model -> Html Msg viewRecentQueries model = let @@ -242,7 +270,7 @@ viewRecentQueries model = case model.state of Active _ -> div [ class "w-full left-0 p-4" ] - [ span [ class "font-bold" ] [ text "Recently searched" ] + [ strong [] [ text "Recently searched" ] , ul [ class "text-gray-900" ] (List.map viewQuery model.recentQueries) ] @@ -251,14 +279,121 @@ viewRecentQueries model = text "" +type ActiveTab + = OffersTab + | ActionsTab + + +viewTabs results activeTab = + let + viewTab tab label clickMsg = + li + [ onClick clickMsg + , if activeTab == tab then + class "bg-orange-300 text-white" + + else + class "bg-gray-100" + , class "rounded-sm flex-1 text-center" + ] + [ text label ] + in + ul [ class "space-x-2 flex items-stretch leading-10" ] + [ viewTab OffersTab ("Offers " ++ String.fromInt (List.length results.offers)) ShowOffersClicked + , viewTab ActionsTab ("Actions " ++ String.fromInt (List.length results.actions)) ShowActionsClicked + ] + + +viewOffers ({ offers, actions } as results) = + let + viewOffer : SearchOffer -> Html msg + viewOffer offer = + div [ class "border-2 rounded-lg overflow-hidden bg-white" ] + [ case offer.image of + Nothing -> + text "" + + Just url -> + img [ src url ] [] + , h3 [ class "px-2" ] [ text offer.title ] + , p [ class "px-2" ] [ text <| String.fromFloat offer.price ] + ] + in + div [] + [ viewTabs results OffersTab + , div [ class "flex" ] + (List.map viewOffer offers) + ] + + +viewActions : SearchResult -> Html Msg +viewActions ({ actions, offers } as results) = + let + viewAction action = + div [ class "border-2 rounded-lg overflow-hidden bg-white" ] + [ div [] [ text action.description ] + ] + in + div [] + [ viewTabs results ActionsTab + , div [ class "flex" ] + (List.map viewAction actions) + ] + + +viewResults : State -> SearchResult -> Html Msg +viewResults state ({ actions, offers } as results) = + let + viewItem icon count singular plural showMsg = + li [ class "py-4 flex items-center" ] + [ div [ class "flex-grow flex items-center" ] + [ icon "w-8 h-8 fill-black mr-3" + , span [] + [ text "We found " + , strong [] + [ text (String.fromInt count) + , text " " + , text <| + if count == 1 then + singular + + else + plural + ] + ] + ] + , button + [ onClick showMsg + , class "button button-primary w-auto button-sm px-6" + ] + [ text "Show" ] + ] + in + case state of + OffersShowed -> + viewOffers results + + ActionsShowed -> + viewActions results + + _ -> + div [] + [ strong [ class "block py-4" ] [ text "Here is what we found" ] + , ul [] + [ viewItem Icons.shop (List.length offers) "offer" "offers" ShowOffersClicked + , viewItem Icons.flag (List.length actions) "action" "actions" ShowActionsClicked + ] + ] + + isActive : Model -> Bool isActive model = case model.state of - Active _ -> - True + Inactive -> + False _ -> - False + True diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 14db002ed..d8251c481 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -43,9 +43,9 @@ import Graphql.Document import Graphql.Http import Graphql.Operation exposing (RootSubscription) import Graphql.SelectionSet exposing (SelectionSet) -import Html exposing (Html, a, button, div, footer, img, input, li, nav, p, span, text, ul) -import Html.Attributes exposing (class, classList, placeholder, src, style, type_) -import Html.Events exposing (onBlur, onClick, onFocus, onInput, onMouseEnter, onSubmit) +import Html exposing (Html, a, button, div, footer, img, nav, p, span, text) +import Html.Attributes exposing (class, classList, src, style, type_) +import Html.Events exposing (onClick, onMouseEnter) import Http import I18Next exposing (Delims(..), Translations, t) import Icons @@ -321,7 +321,7 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = ] ++ (if Search.isActive model.searchModel then [ div [ class "bg-white flex-grow" ] - [ Search.viewRecentQueries model.searchModel + [ Search.viewSearchBody model.searchModel |> Html.map (SearchMsg >> thisMsg) ] ] From 877d227603ebeba5eec04c573687eee900252538 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 27 Jan 2021 07:38:57 +0300 Subject: [PATCH 12/91] Improve types --- src/elm/Search.elm | 72 +++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index a312c715c..d2d73d148 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -10,14 +10,15 @@ import Eos exposing (Symbol) import Graphql.Http import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, button, div, h3, img, input, li, p, span, strong, text, ul) -import Html.Attributes exposing (class, placeholder, src, type_, value) +import Html exposing (Html, a, button, div, h3, img, input, li, p, span, strong, text, ul) +import Html.Attributes exposing (class, href, placeholder, src, type_, value) import Html.Events exposing (onClick, onFocus, onInput, onSubmit) import Icons import Json.Decode as Decode exposing (list, string) import Json.Encode as Encode import List.Extra as List import Ports +import Route import Session.Shared exposing (Shared) @@ -51,9 +52,12 @@ init selectedCommunity = type State = Inactive | Active String - | ResultsShowed - | OffersShowed - | ActionsShowed + | ResultsShowed ActiveTab + + +type ActiveTab + = Offers + | Actions @@ -79,14 +83,16 @@ type alias SearchResult = type alias SearchOffer = - { title : String + { id : Int + , title : String , price : Float , image : Maybe String } type alias SearchAction = - { description : String + { id : Int + , description : String } @@ -99,7 +105,8 @@ searchResultSelectionSet queryString = productsSelectionSet : SelectionSet SearchOffer Cambiatus.Object.Product productsSelectionSet = - SelectionSet.map3 SearchOffer + SelectionSet.map4 SearchOffer + Cambiatus.Object.Product.id Cambiatus.Object.Product.title Cambiatus.Object.Product.price Cambiatus.Object.Product.image @@ -107,7 +114,9 @@ productsSelectionSet = actionsSelectionSet : SelectionSet SearchAction Cambiatus.Object.Action actionsSelectionSet = - SelectionSet.map SearchAction Cambiatus.Object.Action.description + SelectionSet.map2 SearchAction + Cambiatus.Object.Action.id + Cambiatus.Object.Action.description @@ -131,10 +140,10 @@ update shared model msg = update shared { model | queryText = q } QuerySubmitted ShowOffersClicked -> - ( { model | state = OffersShowed }, Cmd.none ) + ( { model | state = ResultsShowed Offers }, Cmd.none ) ShowActionsClicked -> - ( { model | state = ActionsShowed }, Cmd.none ) + ( { model | state = ResultsShowed Actions }, Cmd.none ) SearchResultsLoaded res -> case res of @@ -156,16 +165,20 @@ update shared model msg = StateChanged state -> let - searchText = + ( searchText, found ) = case state of Active q -> - q + ( q, model.found ) + + Inactive -> + ( "", Nothing ) _ -> - model.queryText + ( model.queryText, model.found ) in ( { model | state = state + , found = found , queryText = searchText } , Cmd.none @@ -230,15 +243,15 @@ viewForm model = , Icons.search <| "absolute top-0 left-0 mt-2 ml-2" ++ " " ++ iconColor ] , case model.state of - Active _ -> + Inactive -> + text "" + + _ -> span [ class "text-orange-300 ml-3" , onClick (StateChanged Inactive) ] [ text "cancel" ] - - _ -> - text "" ] ] @@ -279,11 +292,6 @@ viewRecentQueries model = text "" -type ActiveTab - = OffersTab - | ActionsTab - - viewTabs results activeTab = let viewTab tab label clickMsg = @@ -299,8 +307,8 @@ viewTabs results activeTab = [ text label ] in ul [ class "space-x-2 flex items-stretch leading-10" ] - [ viewTab OffersTab ("Offers " ++ String.fromInt (List.length results.offers)) ShowOffersClicked - , viewTab ActionsTab ("Actions " ++ String.fromInt (List.length results.actions)) ShowActionsClicked + [ viewTab Offers ("Offers " ++ String.fromInt (List.length results.offers)) ShowOffersClicked + , viewTab Actions ("Actions " ++ String.fromInt (List.length results.actions)) ShowActionsClicked ] @@ -308,7 +316,11 @@ viewOffers ({ offers, actions } as results) = let viewOffer : SearchOffer -> Html msg viewOffer offer = - div [ class "border-2 rounded-lg overflow-hidden bg-white" ] + a + -- TODO: Hide search bar after clicking the link! + [ Route.href (Route.ViewSale (String.fromInt offer.id)) + , class "border-2 rounded-lg overflow-hidden bg-white" + ] [ case offer.image of Nothing -> text "" @@ -320,7 +332,7 @@ viewOffers ({ offers, actions } as results) = ] in div [] - [ viewTabs results OffersTab + [ viewTabs results Offers , div [ class "flex" ] (List.map viewOffer offers) ] @@ -335,7 +347,7 @@ viewActions ({ actions, offers } as results) = ] in div [] - [ viewTabs results ActionsTab + [ viewTabs results Actions , div [ class "flex" ] (List.map viewAction actions) ] @@ -370,10 +382,10 @@ viewResults state ({ actions, offers } as results) = ] in case state of - OffersShowed -> + ResultsShowed Offers -> viewOffers results - ActionsShowed -> + ResultsShowed Actions -> viewActions results _ -> From 9ccf50047bd03df2ce950ecb1d6c14ccdd9092c6 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 27 Jan 2021 08:25:16 +0300 Subject: [PATCH 13/91] Improve types --- src/elm/Search.elm | 108 ++++++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index d2d73d148..b272edfda 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -18,7 +18,7 @@ import Json.Decode as Decode exposing (list, string) import Json.Encode as Encode import List.Extra as List import Ports -import Route +import Route exposing (Route) import Session.Shared exposing (Shared) @@ -52,37 +52,21 @@ init selectedCommunity = type State = Inactive | Active String - | ResultsShowed ActiveTab + | ResultsShowed FoundItems -type ActiveTab +type FoundItems = Offers | Actions - --- GRAPHQL - - -sendSearchQuery : Symbol -> Shared -> String -> Cmd Msg -sendSearchQuery selectedCommunity shared queryString = - let - req = - { communityId = Eos.symbolToString selectedCommunity } - in - Api.Graphql.query - shared - (Cambiatus.Query.search req (searchResultSelectionSet queryString)) - SearchResultsLoaded - - type alias SearchResult = - { offers : List SearchOffer - , actions : List SearchAction + { offers : List FoundOffer + , actions : List FoundAction } -type alias SearchOffer = +type alias FoundOffer = { id : Int , title : String , price : Float @@ -90,31 +74,47 @@ type alias SearchOffer = } -type alias SearchAction = +type alias FoundAction = { id : Int , description : String } + +-- GRAPHQL + + +sendSearchQuery : Symbol -> Shared -> String -> Cmd Msg +sendSearchQuery selectedCommunity shared queryString = + let + req = + { communityId = Eos.symbolToString selectedCommunity } + in + Api.Graphql.query + shared + (Cambiatus.Query.search req (searchResultSelectionSet queryString)) + SearchResultsLoaded + + searchResultSelectionSet : String -> SelectionSet SearchResult Cambiatus.Object.SearchResult searchResultSelectionSet queryString = SelectionSet.succeed SearchResult - |> with (Cambiatus.Object.SearchResult.products (\_ -> { query = Present queryString }) productsSelectionSet) + |> with (Cambiatus.Object.SearchResult.products (\_ -> { query = Present queryString }) offersSelectionSet) |> with (Cambiatus.Object.SearchResult.actions (\_ -> { query = Present queryString }) actionsSelectionSet) -productsSelectionSet : SelectionSet SearchOffer Cambiatus.Object.Product -productsSelectionSet = - SelectionSet.map4 SearchOffer +offersSelectionSet : SelectionSet FoundOffer Cambiatus.Object.Product +offersSelectionSet = + SelectionSet.map4 FoundOffer Cambiatus.Object.Product.id Cambiatus.Object.Product.title Cambiatus.Object.Product.price Cambiatus.Object.Product.image -actionsSelectionSet : SelectionSet SearchAction Cambiatus.Object.Action +actionsSelectionSet : SelectionSet FoundAction Cambiatus.Object.Action actionsSelectionSet = - SelectionSet.map2 SearchAction + SelectionSet.map2 FoundAction Cambiatus.Object.Action.id Cambiatus.Object.Action.description @@ -126,24 +126,33 @@ actionsSelectionSet = type Msg = StateChanged State | GotRecentSearches String - | RecentSearchClicked String + | RecentQueryClicked String | SearchResultsLoaded (Result (Graphql.Http.Error SearchResult) SearchResult) | QuerySubmitted - | ShowOffersClicked - | ShowActionsClicked + | TabActivated FoundItems + | FoundItemClicked Route update : Shared -> Model -> Msg -> ( Model, Cmd Msg ) update shared model msg = case msg of - RecentSearchClicked q -> - update shared { model | queryText = q } QuerySubmitted + FoundItemClicked route -> + let + -- Make the search dropdown inactive before opening the found item's URL. + ( inactiveModel, _ ) = + update shared model (StateChanged Inactive) + in + ( inactiveModel + , Route.replaceUrl shared.navKey route + ) - ShowOffersClicked -> - ( { model | state = ResultsShowed Offers }, Cmd.none ) + RecentQueryClicked q -> + update shared { model | queryText = q } QuerySubmitted - ShowActionsClicked -> - ( { model | state = ResultsShowed Actions }, Cmd.none ) + TabActivated activeTab -> + ( { model | state = ResultsShowed activeTab } + , Cmd.none + ) SearchResultsLoaded res -> case res of @@ -271,10 +280,10 @@ viewSearchBody model = viewRecentQueries : Model -> Html Msg viewRecentQueries model = let - viewQuery q = + viewItem q = li [ class "leading-10 hover:text-orange-500 cursor-pointer" - , onClick (RecentSearchClicked q) + , onClick (RecentQueryClicked q) ] [ Icons.clock "fill-gray inline-block align-middle mr-3" , span [ class "inline align-middle" ] [ text q ] @@ -285,7 +294,7 @@ viewRecentQueries model = div [ class "w-full left-0 p-4" ] [ strong [] [ text "Recently searched" ] , ul [ class "text-gray-900" ] - (List.map viewQuery model.recentQueries) + (List.map viewItem model.recentQueries) ] _ -> @@ -307,19 +316,18 @@ viewTabs results activeTab = [ text label ] in ul [ class "space-x-2 flex items-stretch leading-10" ] - [ viewTab Offers ("Offers " ++ String.fromInt (List.length results.offers)) ShowOffersClicked - , viewTab Actions ("Actions " ++ String.fromInt (List.length results.actions)) ShowActionsClicked + [ viewTab Offers ("Offers " ++ String.fromInt (List.length results.offers)) (TabActivated Offers) + , viewTab Actions ("Actions " ++ String.fromInt (List.length results.actions)) (TabActivated Actions) ] viewOffers ({ offers, actions } as results) = let - viewOffer : SearchOffer -> Html msg + viewOffer : FoundOffer -> Html Msg viewOffer offer = - a - -- TODO: Hide search bar after clicking the link! - [ Route.href (Route.ViewSale (String.fromInt offer.id)) - , class "border-2 rounded-lg overflow-hidden bg-white" + span + [ class "border-2 rounded-lg overflow-hidden bg-white" + , onClick (FoundItemClicked (Route.ViewSale (String.fromInt offer.id))) ] [ case offer.image of Nothing -> @@ -392,8 +400,8 @@ viewResults state ({ actions, offers } as results) = div [] [ strong [ class "block py-4" ] [ text "Here is what we found" ] , ul [] - [ viewItem Icons.shop (List.length offers) "offer" "offers" ShowOffersClicked - , viewItem Icons.flag (List.length actions) "action" "actions" ShowActionsClicked + [ viewItem Icons.shop (List.length offers) "offer" "offers" (TabActivated Offers) + , viewItem Icons.flag (List.length actions) "action" "actions" (TabActivated Actions) ] ] From 955ea641bb249e5bc1726d0bfe5c8b1dd773a411 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 27 Jan 2021 09:30:55 +0300 Subject: [PATCH 14/91] Hanlde click for 0-found results --- src/elm/Search.elm | 54 ++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index b272edfda..0fd82d732 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -10,8 +10,8 @@ import Eos exposing (Symbol) import Graphql.Http import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, a, button, div, h3, img, input, li, p, span, strong, text, ul) -import Html.Attributes exposing (class, href, placeholder, src, type_, value) +import Html exposing (Html, button, div, h3, img, input, li, p, span, strong, text, ul) +import Html.Attributes exposing (class, placeholder, src, type_, value) import Html.Events exposing (onClick, onFocus, onInput, onSubmit) import Icons import Json.Decode as Decode exposing (list, string) @@ -52,10 +52,10 @@ init selectedCommunity = type State = Inactive | Active String - | ResultsShowed FoundItems + | ResultsShowed FoundItemsKind -type FoundItems +type FoundItemsKind = Offers | Actions @@ -129,7 +129,7 @@ type Msg | RecentQueryClicked String | SearchResultsLoaded (Result (Graphql.Http.Error SearchResult) SearchResult) | QuerySubmitted - | TabActivated FoundItems + | TabActivated FoundItemsKind | FoundItemClicked Route @@ -257,7 +257,7 @@ viewForm model = _ -> span - [ class "text-orange-300 ml-3" + [ class "text-orange-300 pl-3 leading-10 cursor-pointer" , onClick (StateChanged Inactive) ] [ text "cancel" ] @@ -301,26 +301,37 @@ viewRecentQueries model = text "" +viewTabs : SearchResult -> FoundItemsKind -> Html Msg viewTabs results activeTab = let - viewTab tab label clickMsg = + viewTab : FoundItemsKind -> String -> List a -> Msg -> Html Msg + viewTab tabKind label foundItems clickMsg = + let + count = + List.length foundItems + in li - [ onClick clickMsg - , if activeTab == tab then + [ if activeTab == tabKind then class "bg-orange-300 text-white" else class "bg-gray-100" - , class "rounded-sm flex-1 text-center" + , class "rounded-sm flex-1 text-center cursor-pointer" + , if count > 0 then + onClick clickMsg + + else + class "cursor-not-allowed text-gray-300" ] - [ text label ] + [ text <| label ++ String.fromInt count ] in ul [ class "space-x-2 flex items-stretch leading-10" ] - [ viewTab Offers ("Offers " ++ String.fromInt (List.length results.offers)) (TabActivated Offers) - , viewTab Actions ("Actions " ++ String.fromInt (List.length results.actions)) (TabActivated Actions) + [ viewTab Offers "Offers " results.offers (TabActivated Offers) + , viewTab Actions "Actions " results.actions (TabActivated Actions) ] +viewOffers : SearchResult -> Html Msg viewOffers ({ offers, actions } as results) = let viewOffer : FoundOffer -> Html Msg @@ -350,13 +361,13 @@ viewActions : SearchResult -> Html Msg viewActions ({ actions, offers } as results) = let viewAction action = - div [ class "border-2 rounded-lg overflow-hidden bg-white" ] + div [ class "border-2 w-full rounded-lg overflow-hidden bg-white" ] [ div [] [ text action.description ] ] in div [] [ viewTabs results Actions - , div [ class "flex" ] + , div [ class "flex flex-col" ] (List.map viewAction actions) ] @@ -383,9 +394,16 @@ viewResults state ({ actions, offers } as results) = ] ] , button - [ onClick showMsg - , class "button button-primary w-auto button-sm px-6" - ] + (class "button w-auto button-sm px-6" + :: (if count == 0 then + [ class "button-disabled" ] + + else + [ class "button-primary" + , onClick showMsg + ] + ) + ) [ text "Show" ] ] in From 7c010b3368a561eb87b9d16277c0472e01da3aaa Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 27 Jan 2021 09:48:16 +0300 Subject: [PATCH 15/91] Add html/css for actions --- src/elm/Search.elm | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index 0fd82d732..f639413fe 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -10,7 +10,7 @@ import Eos exposing (Symbol) import Graphql.Http import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, button, div, h3, img, input, li, p, span, strong, text, ul) +import Html exposing (Html, br, button, div, h3, img, input, li, p, span, strong, text, ul) import Html.Attributes exposing (class, placeholder, src, type_, value) import Html.Events exposing (onClick, onFocus, onInput, onSubmit) import Icons @@ -361,8 +361,21 @@ viewActions : SearchResult -> Html Msg viewActions ({ actions, offers } as results) = let viewAction action = - div [ class "border-2 w-full rounded-lg overflow-hidden bg-white" ] - [ div [] [ text action.description ] + div [ class "relative w-full mt-8 mb-4 bg-purple-500 rounded-lg text-white" ] + [ div [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full fill-green" ] + , div [ class "px-4 pt-8 pb-6" ] + [ p [ class "mb-6" ] [ text action.description ] + , div [ class "flex justify-between" ] + [ p [] + [ text "Você ganha" + , br [] [] + , span [ class "text-green" ] [ text "300" ] + , text " " + , text "MUDAS" + ] + , button [ class "self-end button button-primary" ] [ text "Claim" ] + ] + ] ] in div [] From 4fe9e828e869c23823313864dc61bc610c905bd4 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 27 Jan 2021 18:45:28 +0300 Subject: [PATCH 16/91] Adjust search layout and background --- src/elm/Search.elm | 77 ++++++++++++++++++++---------------- src/elm/Session/LoggedIn.elm | 6 +-- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index f639413fe..4a8681e56 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -77,6 +77,7 @@ type alias FoundOffer = type alias FoundAction = { id : Int , description : String + , reward : Float } @@ -114,9 +115,10 @@ offersSelectionSet = actionsSelectionSet : SelectionSet FoundAction Cambiatus.Object.Action actionsSelectionSet = - SelectionSet.map2 FoundAction + SelectionSet.map3 FoundAction Cambiatus.Object.Action.id Cambiatus.Object.Action.description + Cambiatus.Object.Action.reward @@ -267,10 +269,10 @@ viewForm model = viewSearchBody : Model -> Html Msg viewSearchBody model = - div [ class "container mx-auto p-4" ] + div [ class "container mx-auto flex flex-grow" ] [ case model.found of Just results -> - viewResults model.state results + viewResults model.selectedCommunity model.state results Nothing -> viewRecentQueries model @@ -291,7 +293,7 @@ viewRecentQueries model = in case model.state of Active _ -> - div [ class "w-full left-0 p-4" ] + div [ class "w-full p-4 bg-white" ] [ strong [] [ text "Recently searched" ] , ul [ class "text-gray-900" ] (List.map viewItem model.recentQueries) @@ -325,19 +327,19 @@ viewTabs results activeTab = ] [ text <| label ++ String.fromInt count ] in - ul [ class "space-x-2 flex items-stretch leading-10" ] + ul [ class "space-x-2 flex items-stretch leading-10 p-4 pb-2 bg-white" ] [ viewTab Offers "Offers " results.offers (TabActivated Offers) , viewTab Actions "Actions " results.actions (TabActivated Actions) ] -viewOffers : SearchResult -> Html Msg +viewOffers : SearchResult -> List (Html Msg) viewOffers ({ offers, actions } as results) = let viewOffer : FoundOffer -> Html Msg viewOffer offer = - span - [ class "border-2 rounded-lg overflow-hidden bg-white" + li + [ class "border-2 rounded-lg overflow-hidden bg-white w-1/2" , onClick (FoundItemClicked (Route.ViewSale (String.fromInt offer.id))) ] [ case offer.image of @@ -350,15 +352,14 @@ viewOffers ({ offers, actions } as results) = , p [ class "px-2" ] [ text <| String.fromFloat offer.price ] ] in - div [] - [ viewTabs results Offers - , div [ class "flex" ] - (List.map viewOffer offers) - ] + [ viewTabs results Offers + , ul [ class "flex flex-wrap m-4" ] + (List.map viewOffer offers) + ] -viewActions : SearchResult -> Html Msg -viewActions ({ actions, offers } as results) = +viewActions : Symbol -> SearchResult -> List (Html Msg) +viewActions symbol ({ actions, offers } as results) = let viewAction action = div [ class "relative w-full mt-8 mb-4 bg-purple-500 rounded-lg text-white" ] @@ -367,26 +368,25 @@ viewActions ({ actions, offers } as results) = [ p [ class "mb-6" ] [ text action.description ] , div [ class "flex justify-between" ] [ p [] - [ text "Você ganha" + [ text "You gain" , br [] [] - , span [ class "text-green" ] [ text "300" ] + , span [ class "text-green" ] [ text <| String.fromFloat action.reward ] , text " " - , text "MUDAS" + , text <| Eos.symbolToSymbolCodeString symbol ] , button [ class "self-end button button-primary" ] [ text "Claim" ] ] ] ] in - div [] - [ viewTabs results Actions - , div [ class "flex flex-col" ] - (List.map viewAction actions) - ] + [ viewTabs results Actions + , div [ class "flex flex-col px-4" ] + (List.map viewAction actions) + ] -viewResults : State -> SearchResult -> Html Msg -viewResults state ({ actions, offers } as results) = +viewResultsOverview : SearchResult -> List (Html Msg) +viewResultsOverview { offers, actions } = let viewItem icon count singular plural showMsg = li [ class "py-4 flex items-center" ] @@ -420,21 +420,32 @@ viewResults state ({ actions, offers } as results) = [ text "Show" ] ] in + [ strong [ class "block py-4" ] [ text "Here is what we found" ] + , ul [] + [ viewItem Icons.shop (List.length offers) "offer" "offers" (TabActivated Offers) + , viewItem Icons.flag (List.length actions) "action" "actions" (TabActivated Actions) + ] + ] + + +viewResults : Symbol -> State -> SearchResult -> Html Msg +viewResults symbol state ({ actions, offers } as results) = + let + wrapWithClass c = + div [ class ("flex-grow " ++ c) ] + in case state of ResultsShowed Offers -> viewOffers results + |> wrapWithClass "bg-gray-100" ResultsShowed Actions -> - viewActions results + viewActions symbol results + |> wrapWithClass "bg-gray-100" _ -> - div [] - [ strong [ class "block py-4" ] [ text "Here is what we found" ] - , ul [] - [ viewItem Icons.shop (List.length offers) "offer" "offers" (TabActivated Offers) - , viewItem Icons.flag (List.length actions) "action" "actions" (TabActivated Actions) - ] - ] + viewResultsOverview results + |> wrapWithClass "bg-white p-4" isActive : Model -> Bool diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index d8251c481..c02dbab5e 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -320,10 +320,8 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = ] ] ++ (if Search.isActive model.searchModel then - [ div [ class "bg-white flex-grow" ] - [ Search.viewSearchBody model.searchModel - |> Html.map (SearchMsg >> thisMsg) - ] + [ Search.viewSearchBody model.searchModel + |> Html.map (SearchMsg >> thisMsg) ] else From ccd7e108be5fbe5dc4662a62b3779164d551c3a3 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 28 Jan 2021 15:34:29 +0300 Subject: [PATCH 17/91] Adjust styles for offers list --- src/elm/Search.elm | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index 4a8681e56..bed6a514e 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -11,7 +11,7 @@ import Graphql.Http import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) import Html exposing (Html, br, button, div, h3, img, input, li, p, span, strong, text, ul) -import Html.Attributes exposing (class, placeholder, src, type_, value) +import Html.Attributes exposing (class, minlength, placeholder, required, src, style, type_, value) import Html.Events exposing (onClick, onFocus, onInput, onSubmit) import Icons import Json.Decode as Decode exposing (list, string) @@ -244,6 +244,9 @@ viewForm model = [ div [ class "relative w-full" ] [ input [ type_ "search" + + --, minlength 3 + --, required True , class "w-full form-input rounded-full bg-gray-100 pl-10 m-0" , placeholder "Find friends and communities" , value model.queryText @@ -333,27 +336,36 @@ viewTabs results activeTab = ] -viewOffers : SearchResult -> List (Html Msg) -viewOffers ({ offers, actions } as results) = +viewOffers : Symbol -> SearchResult -> List (Html Msg) +viewOffers symbol ({ offers, actions } as results) = let viewOffer : FoundOffer -> Html Msg viewOffer offer = li - [ class "border-2 rounded-lg overflow-hidden bg-white w-1/2" - , onClick (FoundItemClicked (Route.ViewSale (String.fromInt offer.id))) - ] - [ case offer.image of - Nothing -> - text "" - - Just url -> - img [ src url ] [] - , h3 [ class "px-2" ] [ text offer.title ] - , p [ class "px-2" ] [ text <| String.fromFloat offer.price ] + [ class "flex px-2 w-1/2 sm:w-1/3 md:w-1/4" ] + [ div + [ class "rounded-md overflow-hidden bg-white flex-grow mb-4 pb-4 cursor-pointer hover:shadow" + , onClick (FoundItemClicked (Route.ViewSale (String.fromInt offer.id))) + ] + [ case offer.image of + Nothing -> + text "" + + Just url -> + img [ src url ] [] + , h3 [ class "p-3" ] [ text offer.title ] + , p [ class "px-3 leading-none" ] + [ span [ class "text-xl text-green font-medium" ] [ text <| String.fromFloat offer.price ] + , br [] [] + , span [ class "text-gray-300 text-xs" ] + [ text <| Eos.symbolToSymbolCodeString symbol + ] + ] + ] ] in [ viewTabs results Offers - , ul [ class "flex flex-wrap m-4" ] + , ul [ class "offers-list flex flex-wrap mt-6 mb-8 mx-2 justify-between" ] (List.map viewOffer offers) ] @@ -436,7 +448,7 @@ viewResults symbol state ({ actions, offers } as results) = in case state of ResultsShowed Offers -> - viewOffers results + viewOffers symbol results |> wrapWithClass "bg-gray-100" ResultsShowed Actions -> From 3bafc5b3a7a50b81e179d404b9bbc6a3f746d4a7 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 28 Jan 2021 16:14:05 +0300 Subject: [PATCH 18/91] Add actions list styles --- src/elm/Search.elm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index bed6a514e..dca5b7423 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -10,7 +10,7 @@ import Eos exposing (Symbol) import Graphql.Http import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, br, button, div, h3, img, input, li, p, span, strong, text, ul) +import Html exposing (Html, br, button, div, h3, i, img, input, li, p, span, strong, text, ul) import Html.Attributes exposing (class, minlength, placeholder, required, src, style, type_, value) import Html.Events exposing (onClick, onFocus, onInput, onSubmit) import Icons @@ -365,7 +365,7 @@ viewOffers symbol ({ offers, actions } as results) = ] in [ viewTabs results Offers - , ul [ class "offers-list flex flex-wrap mt-6 mb-8 mx-2 justify-between" ] + , ul [ class "offers-list flex flex-wrap mt-6 mb-8 mx-2 justify-left" ] (List.map viewOffer offers) ] @@ -374,15 +374,15 @@ viewActions : Symbol -> SearchResult -> List (Html Msg) viewActions symbol ({ actions, offers } as results) = let viewAction action = - div [ class "relative w-full mt-8 mb-4 bg-purple-500 rounded-lg text-white" ] - [ div [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full fill-green" ] - , div [ class "px-4 pt-8 pb-6" ] - [ p [ class "mb-6" ] [ text action.description ] + li [ class "relative mb-10 w-full sm:px-2 sm:w-1/2 lg:w-1/3" ] + [ i [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full fill-green" ] + , div [ class "px-4 pt-8 pb-4 text-sm font-light bg-purple-500 rounded-lg text-white" ] + [ p [ class "mb-8" ] [ text action.description ] , div [ class "flex justify-between" ] [ p [] [ text "You gain" , br [] [] - , span [ class "text-green" ] [ text <| String.fromFloat action.reward ] + , span [ class "text-green font-medium" ] [ text <| String.fromFloat action.reward ] , text " " , text <| Eos.symbolToSymbolCodeString symbol ] @@ -392,7 +392,7 @@ viewActions symbol ({ actions, offers } as results) = ] in [ viewTabs results Actions - , div [ class "flex flex-col px-4" ] + , ul [ class "flex px-4 sm:px-2 pt-12 flex-wrap justify-left" ] (List.map viewAction actions) ] From 7f64012fec2d482e9b52e1d0e775157422b0dd82 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 28 Jan 2021 17:28:35 +0300 Subject: [PATCH 19/91] Close search After changing community and clicking Profile link. --- src/elm/Search.elm | 7 ++++++- src/elm/Session/LoggedIn.elm | 33 +++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index dca5b7423..2977949f1 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -1,4 +1,4 @@ -module Search exposing (Model, Msg, init, isActive, subscriptions, update, viewForm, viewRecentQueries, viewSearchBody) +module Search exposing (Model, Msg, closeSearch, init, isActive, subscriptions, update, viewForm, viewRecentQueries, viewSearchBody) import Api.Graphql import Cambiatus.Object @@ -135,6 +135,11 @@ type Msg | FoundItemClicked Route +closeSearch : Shared -> Model -> ( Model, Cmd Msg ) +closeSearch shared model = + update shared model (StateChanged Inactive) + + update : Shared -> Model -> Msg -> ( Model, Cmd Msg ) update shared model msg = case msg of diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index c02dbab5e..38ff5780e 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -120,7 +120,7 @@ subscriptions model = Sub.batch [ Sub.map GotAuthMsg (Auth.subscriptions model.auth) , Sub.map KeyDown (Browser.Events.onKeyDown (Decode.field "key" Decode.string)) - , Sub.map SearchMsg Search.subscriptions + , Sub.map GotSearchMsg Search.subscriptions ] @@ -310,7 +310,7 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = , -- Search form is separated from search results because it needs to -- be between community selector and user dropdown on Desktops. Search.viewForm model.searchModel - |> Html.map (SearchMsg >> thisMsg) + |> Html.map (GotSearchMsg >> thisMsg) , if Search.isActive model.searchModel then text "" @@ -321,7 +321,7 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = ] ++ (if Search.isActive model.searchModel then [ Search.viewSearchBody model.searchModel - |> Html.map (SearchMsg >> thisMsg) + |> Html.map (GotSearchMsg >> thisMsg) ] else @@ -488,6 +488,7 @@ viewHeader ({ shared } as model) profile_ = [ class "flex block w-full px-4 py-4 justify-start items-center text-sm" , Route.href Route.Profile , onClick (ShowUserNav False) + , onClick SearchClosed ] [ Icons.profile "mr-4" , text_ "menu.profile" @@ -741,7 +742,8 @@ type Msg | CloseCommunitySelector | SelectCommunity Symbol (Cmd Msg) | HideFeedbackLocal - | SearchMsg Search.Msg + | GotSearchMsg Search.Msg + | SearchClosed update : Msg -> Model -> UpdateResult @@ -771,14 +773,22 @@ update msg model = Ignored -> UR.init model - SearchMsg searchMsg -> + SearchClosed -> + { model + | searchModel = + Search.closeSearch shared model.searchModel + |> Tuple.first + } + |> UR.init + + GotSearchMsg searchMsg -> let ( searchModel, searchCmd ) = Search.update shared model.searchModel searchMsg in { model | searchModel = searchModel } |> UR.init - |> UR.addCmd (Cmd.map SearchMsg searchCmd) + |> UR.addCmd (Cmd.map GotSearchMsg searchCmd) CompletedLoadTranslation lang (Ok transl) -> case model.profile of @@ -958,6 +968,10 @@ update msg model = { model | selectedCommunity = communityId , showCommunitySelector = False + , searchModel = + Search.closeSearch shared model.searchModel + |> Tuple.first + |> (\searchModel -> { searchModel | selectedCommunity = communityId }) } |> UR.init |> UR.addCmd (Api.Graphql.query shared (Community.settingsQuery communityId) CompletedLoadSettings) @@ -1079,8 +1093,11 @@ msgToString msg = Ignored -> [ "Ignored" ] - SearchMsg _ -> - [ "SearchMsg" ] + SearchClosed -> + [ "SearchClosed" ] + + GotSearchMsg _ -> + [ "GotSearchMsg" ] CompletedLoadTranslation _ r -> [ "CompletedLoadTranslation", UR.resultToString r ] From 4e60528b6192dd3dbece64933d78edb555ba1220 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 28 Jan 2021 17:52:46 +0300 Subject: [PATCH 20/91] Add empty results state --- src/elm/Search.elm | 36 ++++++++++++++++++++++++++++-------- src/elm/Session/LoggedIn.elm | 2 +- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index 2977949f1..9ea986b6d 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -1,4 +1,4 @@ -module Search exposing (Model, Msg, closeSearch, init, isActive, subscriptions, update, viewForm, viewRecentQueries, viewSearchBody) +module Search exposing (Model, Msg, closeSearch, init, isActive, subscriptions, update, viewBody, viewForm, viewRecentQueries) import Api.Graphql import Cambiatus.Object @@ -11,7 +11,7 @@ import Graphql.Http import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) import Html exposing (Html, br, button, div, h3, i, img, input, li, p, span, strong, text, ul) -import Html.Attributes exposing (class, minlength, placeholder, required, src, style, type_, value) +import Html.Attributes exposing (class, placeholder, src, type_, value) import Html.Events exposing (onClick, onFocus, onInput, onSubmit) import Icons import Json.Decode as Decode exposing (list, string) @@ -184,7 +184,7 @@ update shared model msg = ( searchText, found ) = case state of Active q -> - ( q, model.found ) + ( q, Nothing ) Inactive -> ( "", Nothing ) @@ -275,12 +275,32 @@ viewForm model = ] -viewSearchBody : Model -> Html Msg -viewSearchBody model = +viewEmptyResults queryText = + div [ class "flex-grow bg-white text-center" ] + [ h3 [ class "mt-20 text-xl font-bold" ] + [ text <| "You searched for \"" ++ queryText ++ "\"" ] + , div [] + [ img + [ class "w-2/3 mx-auto md:w-64 mt-6 mb-8" + , src "/images/not_found.svg" + ] + [] + , text "No results were found" + ] + ] + + +viewBody : Model -> Html Msg +viewBody model = div [ class "container mx-auto flex flex-grow" ] [ case model.found of - Just results -> - viewResults model.selectedCommunity model.state results + Just ({ actions, offers } as results) -> + case ( List.length actions, List.length offers ) of + ( 0, 0 ) -> + viewEmptyResults model.queryText + + _ -> + viewResults model.selectedCommunity model.state results Nothing -> viewRecentQueries model @@ -446,7 +466,7 @@ viewResultsOverview { offers, actions } = viewResults : Symbol -> State -> SearchResult -> Html Msg -viewResults symbol state ({ actions, offers } as results) = +viewResults symbol state results = let wrapWithClass c = div [ class ("flex-grow " ++ c) ] diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 38ff5780e..9e2301fcc 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -320,7 +320,7 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = ] ] ++ (if Search.isActive model.searchModel then - [ Search.viewSearchBody model.searchModel + [ Search.viewBody model.searchModel |> Html.map (GotSearchMsg >> thisMsg) ] From 6ee8a6d24f8e4842bc63ab283952666bc5a98287 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 1 Feb 2021 13:02:21 +0300 Subject: [PATCH 21/91] Simplify Aciton functions --- src/elm/Page/Community.elm | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index 22754a272..a68c16360 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -353,7 +353,12 @@ viewObjective loggedIn model metadata index objective = actsNButton = objective.actions |> List.sortBy (\a -> a.position |> Maybe.withDefault 0) - |> List.map (viewAction loggedIn metadata model.date) + |> List.map + (viewAction loggedIn.shared.translators + (LoggedIn.isAccount metadata.creator loggedIn) + metadata.symbol + model.date + ) in if objective.isCompleted then text "" @@ -405,18 +410,15 @@ viewObjective loggedIn model metadata index objective = -- VIEW ACTION -viewAction : LoggedIn.Model -> Community.Model -> Maybe Posix -> Community.Action -> Html Msg -viewAction loggedIn metadata maybeDate action = +viewAction : Translators -> Bool -> Symbol -> Maybe Posix -> Action -> Html Msg +viewAction translators canEdit symbol maybeDate action = let - t = - loggedIn.shared.translators.t + { t, tr } = + translators text_ s = text (t s) - canEdit = - LoggedIn.isAccount metadata.creator loggedIn - posixDeadline : Posix posixDeadline = action.deadline @@ -513,14 +515,11 @@ viewAction loggedIn metadata maybeDate action = ) rewardStr = - String.fromFloat action.reward ++ " " ++ Eos.symbolToSymbolCodeString metadata.symbol + String.fromFloat action.reward ++ " " ++ Eos.symbolToSymbolCodeString symbol ( usages, usagesLeft ) = ( String.fromInt action.usages, String.fromInt action.usagesLeft ) - tr = - loggedIn.shared.translators.tr - validationType : String validationType = action.verificationType From ab33b58687078c59eb1781a3c065329bee05afe7 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 1 Feb 2021 13:08:10 +0300 Subject: [PATCH 22/91] Simplify view proof function --- src/elm/Page/Community.elm | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index a68c16360..271bb5757 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -29,7 +29,7 @@ import Json.Encode as Encode exposing (Value) import Page import Route import Session.LoggedIn as LoggedIn exposing (External(..), FeedbackStatus(..)) -import Session.Shared exposing (Translators) +import Session.Shared exposing (Shared, Translators) import Strftime import Task import Time exposing (Posix, posixToMillis) @@ -198,7 +198,7 @@ view loggedIn model = ] ClaimWithProofs action -> - viewClaimWithProofs model loggedIn action + viewClaimWithProofs model.proofs loggedIn.shared.translators action in { title = title , content = @@ -209,14 +209,14 @@ view loggedIn model = } -viewClaimWithProofs : Model -> LoggedIn.Model -> Action -> Html Msg -viewClaimWithProofs model { shared } action = +viewClaimWithProofs : Maybe Proof -> Translators -> Action -> Html Msg +viewClaimWithProofs proofs translators action = let { t } = - shared.translators + translators isUploadingInProgress = - case model.proofs of + case proofs of Just (Proof Uploading _) -> True @@ -230,12 +230,12 @@ viewClaimWithProofs model { shared } action = [ text <| Maybe.withDefault "" action.photoProofInstructions ] - , case model.proofs of + , case proofs of Just (Proof _ (Just { code, secondsAfterClaim, availabilityPeriod })) -> case code of Just c -> viewProofCode - shared.translators + translators c secondsAfterClaim availabilityPeriod @@ -248,9 +248,9 @@ viewClaimWithProofs model { shared } action = , div [ class "mb-4" ] [ span [ class "input-label block mb-2" ] [ text (t "community.actions.proof.photo") ] - , case model.proofs of + , case proofs of Just (Proof photoStatus _) -> - viewPhotoUploader shared.translators photoStatus + viewPhotoUploader translators photoStatus _ -> text "" From 53b09aa486d1774d0b26ad942ff375c04db724bc Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 1 Feb 2021 13:11:20 +0300 Subject: [PATCH 23/91] Simplify claim confirmaiton --- src/elm/Page/Community.elm | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index 271bb5757..35feb2b0b 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -183,7 +183,7 @@ view loggedIn model = , div [ class "container mx-auto" ] [ if community.hasObjectives then div [ class "px-4 pb-4" ] - [ viewClaimConfirmation loggedIn model + [ viewClaimConfirmation loggedIn.shared.translators model , div [ class "container bg-white py-6 sm:py-8 px-3 sm:px-6 rounded-lg mt-4" ] (Page.viewTitle (t "community.objectives.title_plural") :: List.indexedMap (viewObjective loggedIn model community) @@ -613,12 +613,9 @@ viewAction translators canEdit symbol maybeDate action = ] -viewClaimConfirmation : LoggedIn.Model -> Model -> Html Msg -viewClaimConfirmation loggedIn model = +viewClaimConfirmation : Translators -> ClaimConfirmationModalStatus -> Html Msg +viewClaimConfirmation { t } claimConfirmationModalStatus = let - t = - loggedIn.shared.translators.t - text_ s = text (t s) @@ -662,7 +659,7 @@ viewClaimConfirmation loggedIn model = |> Modal.toHtml ] in - case model.claimConfirmationModalStatus of + case claimConfirmationModalStatus of Open action -> let acceptMsg = From 6c93277339f7347197c74d34f15c6cb3a806739b Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 1 Feb 2021 13:12:08 +0300 Subject: [PATCH 24/91] Simplify view community status --- src/elm/Page/Community.elm | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index 35feb2b0b..d7f94279c 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -183,7 +183,7 @@ view loggedIn model = , div [ class "container mx-auto" ] [ if community.hasObjectives then div [ class "px-4 pb-4" ] - [ viewClaimConfirmation loggedIn.shared.translators model + [ viewClaimConfirmation loggedIn.shared.translators model.claimConfirmationModalStatus , div [ class "container bg-white py-6 sm:py-8 px-3 sm:px-6 rounded-lg mt-4" ] (Page.viewTitle (t "community.objectives.title_plural") :: List.indexedMap (viewObjective loggedIn model community) @@ -193,7 +193,7 @@ view loggedIn model = else text "" - , viewCommunityStats loggedIn community + , viewCommunityStats loggedIn.shared.translators community ] ] @@ -721,12 +721,8 @@ viewPhotoUploader { t } proofPhotoStatus = ] -viewCommunityStats : LoggedIn.Model -> Community.Model -> Html msg -viewCommunityStats loggedIn community = - let - t = - loggedIn.shared.translators.t - in +viewCommunityStats : Translators -> Community.Model -> Html msg +viewCommunityStats { t } community = div [ class "flex flex-wrap px-4 container mb-6" ] [ div [ class "flex w-full lg:w-1/2 h-48 mb-4" ] [ div [ class "flex-grow" ] From 466f091cbd6f7e510eb91883d9240ff3d7650c7e Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 1 Feb 2021 13:21:37 +0300 Subject: [PATCH 25/91] Rename proofs to proof --- src/elm/Page/Community.elm | 42 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index d7f94279c..71629bbe6 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -59,7 +59,7 @@ initModel _ _ = , actionId = Nothing , openObjective = Nothing , claimConfirmationModalStatus = Closed - , proofs = Nothing + , proof = Nothing } @@ -69,7 +69,7 @@ initModel _ _ = subscriptions : Model -> Sub Msg subscriptions model = - case model.proofs of + case model.proof of Just (Proof _ (Just _)) -> Time.every 1000 Tick @@ -87,7 +87,7 @@ type alias Model = , actionId : Maybe Int , openObjective : Maybe Int , claimConfirmationModalStatus : ClaimConfirmationModalStatus - , proofs : Maybe Proof + , proof : Maybe Proof } @@ -198,7 +198,7 @@ view loggedIn model = ] ClaimWithProofs action -> - viewClaimWithProofs model.proofs loggedIn.shared.translators action + viewClaimWithProofs model.proof loggedIn.shared.translators action in { title = title , content = @@ -824,7 +824,7 @@ update msg model ({ shared } as loggedIn) = model |> UR.init GotUint64Name (Ok uint64name) -> - case ( model.proofs, model.actionId ) of + case ( model.proof, model.actionId ) of ( Just (Proof proofPhoto (Just proofCode)), Just actionId ) -> let verificationCode = @@ -836,7 +836,7 @@ update msg model ({ shared } as loggedIn) = | code = Just verificationCode } in - { model | proofs = Just (Proof proofPhoto newProofCode) } + { model | proof = Just (Proof proofPhoto newProofCode) } |> UR.init _ -> @@ -847,7 +847,7 @@ update msg model ({ shared } as loggedIn) = UR.init model Tick timer -> - case model.proofs of + case model.proof of Just (Proof proofPhoto (Just proofCode)) -> let secondsAfterClaim = @@ -864,7 +864,7 @@ update msg model ({ shared } as loggedIn) = | secondsAfterClaim = secondsAfterClaim } in - { model | proofs = Just (Proof proofPhoto newProofCode) } + { model | proof = Just (Proof proofPhoto newProofCode) } |> UR.init else @@ -885,7 +885,7 @@ update msg model ({ shared } as loggedIn) = in { model | actionId = Just actionId - , proofs = Just (Proof NoPhotoAdded initProofCodeParts) + , proof = Just (Proof NoPhotoAdded initProofCodeParts) } |> UR.init |> UR.addPort @@ -942,7 +942,7 @@ update msg model ({ shared } as loggedIn) = _ -> model.pageStatus , claimConfirmationModalStatus = Closed - , proofs = Just (Proof NoPhotoAdded Nothing) + , proof = Just (Proof NoPhotoAdded Nothing) } |> UR.init |> UR.addCmd @@ -972,7 +972,7 @@ update msg model ({ shared } as loggedIn) = Api.uploadImage loggedIn.shared file CompletedPhotoUpload newProofs = - case model.proofs of + case model.proof of Just (Proof _ proofCode) -> Just (Proof Uploading proofCode) @@ -980,7 +980,7 @@ update msg model ({ shared } as loggedIn) = Nothing in { model - | proofs = newProofs + | proof = newProofs } |> UR.init |> UR.addCmd uploadImage @@ -992,27 +992,27 @@ update msg model ({ shared } as loggedIn) = CompletedPhotoUpload (Ok url) -> let newProofs = - case model.proofs of + case model.proof of Just (Proof _ proofCode) -> Just (Proof (Uploaded url) proofCode) _ -> Nothing in - { model | proofs = newProofs } + { model | proof = newProofs } |> UR.init CompletedPhotoUpload (Err error) -> let newProofs = - case model.proofs of + case model.proof of Just (Proof _ proofCode) -> Just (Proof (UploadFailed error) proofCode) _ -> Nothing in - { model | proofs = newProofs } + { model | proof = newProofs } |> UR.init |> UR.logHttpError msg error @@ -1030,7 +1030,7 @@ update msg model ({ shared } as loggedIn) = _ -> model.pageStatus , claimConfirmationModalStatus = Closed - , proofs = Nothing + , proof = Nothing } |> UR.init |> UR.addExt @@ -1045,7 +1045,7 @@ update msg model ({ shared } as loggedIn) = ClaimAction action -> let hasPhotoError = - case model.proofs of + case model.proof of Just (Proof (Uploaded _) _) -> False @@ -1057,7 +1057,7 @@ update msg model ({ shared } as loggedIn) = False newModel = - case model.proofs of + case model.proof of Just (Proof _ _) -> -- Claim with proof has no confirmation model @@ -1066,7 +1066,7 @@ update msg model ({ shared } as loggedIn) = { model | claimConfirmationModalStatus = InProgress } ( proofPhotoUrl, proofCode_, proofTime ) = - case model.proofs of + case model.proof of Just (Proof (Uploaded url) (Just { code, claimTimestamp })) -> ( url, Maybe.withDefault "" code, claimTimestamp ) @@ -1127,7 +1127,7 @@ update msg model ({ shared } as loggedIn) = _ -> model.pageStatus - , proofs = Nothing + , proof = Nothing } |> UR.init |> UR.addExt (ShowFeedback LoggedIn.Success message) From 6ef20a4366fb7e0e3a901a89291cea89fcd1ea17 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 1 Feb 2021 17:47:45 +0300 Subject: [PATCH 26/91] WIP: Claim works on community page --- src/elm/Page/Community.elm | 1035 ++++++------------------------------ 1 file changed, 161 insertions(+), 874 deletions(-) diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index 71629bbe6..1101aa5f8 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -9,6 +9,7 @@ module Page.Community exposing , view ) +import Action import Api import Api.Graphql import Avatar @@ -58,8 +59,10 @@ initModel _ _ = , pageStatus = Loading , actionId = Nothing , openObjective = Nothing - , claimConfirmationModalStatus = Closed - , proof = Nothing + , claimingAction = Nothing + + --, claimConfirmationModalStatus = Closed + --, proof = Nothing } @@ -69,11 +72,11 @@ initModel _ _ = subscriptions : Model -> Sub Msg subscriptions model = - case model.proof of - Just (Proof _ (Just _)) -> - Time.every 1000 Tick + case model.claimingAction of + Just ca -> + Sub.map GotActionMsg (Action.subscriptions ca) - _ -> + Nothing -> Sub.none @@ -83,23 +86,13 @@ subscriptions model = type alias Model = { date : Maybe Posix + , claimingAction : Maybe Action.Model , pageStatus : PageStatus , actionId : Maybe Int , openObjective : Maybe Int - , claimConfirmationModalStatus : ClaimConfirmationModalStatus - , proof : Maybe Proof - } - - -type Proof - = Proof ProofPhotoStatus (Maybe ProofCode) - -type alias ProofCode = - { code : Maybe String - , claimTimestamp : Int - , secondsAfterClaim : Int - , availabilityPeriod : Int + --, claimConfirmationModalStatus : ClaimConfirmationModalStatus + --, proof : Maybe Proof } @@ -115,19 +108,6 @@ type ActiveSection | ClaimWithProofs Community.Action -type ClaimConfirmationModalStatus - = Open Community.Action - | InProgress - | Closed - - -type ProofPhotoStatus - = NoPhotoAdded - | Uploading - | UploadFailed Http.Error - | Uploaded String - - -- VIEW @@ -183,7 +163,16 @@ view loggedIn model = , div [ class "container mx-auto" ] [ if community.hasObjectives then div [ class "px-4 pb-4" ] - [ viewClaimConfirmation loggedIn.shared.translators model.claimConfirmationModalStatus + [ Html.map GotActionMsg + (case model.claimingAction of + Just ca -> + Action.viewClaimConfirmation + loggedIn.shared.translators + ca.claimConfirmationModalStatus + + Nothing -> + text "" + ) , div [ class "container bg-white py-6 sm:py-8 px-3 sm:px-6 rounded-lg mt-4" ] (Page.viewTitle (t "community.objectives.title_plural") :: List.indexedMap (viewObjective loggedIn model community) @@ -198,7 +187,13 @@ view loggedIn model = ] ClaimWithProofs action -> - viewClaimWithProofs model.proof loggedIn.shared.translators action + case model.claimingAction of + Just ca -> + Html.map GotActionMsg + (Action.viewClaimWithProofs ca.proof loggedIn.shared.translators action) + + Nothing -> + text "" in { title = title , content = @@ -209,115 +204,52 @@ view loggedIn model = } -viewClaimWithProofs : Maybe Proof -> Translators -> Action -> Html Msg -viewClaimWithProofs proofs translators action = - let - { t } = - translators - - isUploadingInProgress = - case proofs of - Just (Proof Uploading _) -> - True - - _ -> - False - in - div [ class "bg-white border-t border-gray-300" ] - [ div [ class "container p-4 mx-auto" ] - [ div [ class "heading-bold leading-7 font-bold" ] [ text <| t "community.actions.proof.title" ] - , p [ class "mb-4" ] - [ text <| - Maybe.withDefault "" action.photoProofInstructions - ] - , case proofs of - Just (Proof _ (Just { code, secondsAfterClaim, availabilityPeriod })) -> - case code of - Just c -> - viewProofCode - translators - c - secondsAfterClaim - availabilityPeriod - - _ -> - text "" - - _ -> - text "" - , div [ class "mb-4" ] - [ span [ class "input-label block mb-2" ] - [ text (t "community.actions.proof.photo") ] - , case proofs of - Just (Proof photoStatus _) -> - viewPhotoUploader translators photoStatus - - _ -> - text "" - ] - , div [ class "md:flex" ] - [ button - [ class "modal-cancel" - , onClick - (if isUploadingInProgress then - NoOp - - else - CloseProofSection CancelClicked - ) - , classList [ ( "button-disabled", isUploadingInProgress ) ] - , disabled isUploadingInProgress +viewCommunityStats : Translators -> Community.Model -> Html msg +viewCommunityStats { t } community = + div [ class "flex flex-wrap px-4 container mb-6" ] + [ div [ class "flex w-full lg:w-1/2 h-48 mb-4" ] + [ div [ class "flex-grow" ] + [ div + [ class " min-w-40 h-48 relative bg-white rounded-lg p-4 overflow-hidden" ] + [ p [ class "w-full font-bold text-green text-3xl" ] + [ text <| String.fromInt community.memberCount ] + , p [ class " text-gray-700 text-sm" ] + [ text <| t "community.index.members" ] + , img [ class "absolute bottom-0 right-0", src "/images/girl-playing-guitar.svg" ] [] ] - [ text (t "menu.cancel") ] - , button - [ class "modal-accept" - , classList [ ( "button-disabled", isUploadingInProgress ) ] - , onClick - (if isUploadingInProgress then - NoOp - - else - ClaimAction action - ) - , disabled isUploadingInProgress + ] + , div [ class "px-2 mb-6" ] + [ div [ class "flex flex-col w-40" ] + [ div [ class "w-40 h-24 bg-white rounded-lg px-4 py-2 mb-4" ] + [ p [ class "w-full font-bold text-green text-3xl" ] + [ text <| String.fromInt community.claimCount ] + , p [ class " text-gray-700 text-sm" ] + [ text <| t "community.index.claims" ] + ] + , div [ class "w-40 h-20 bg-white rounded-lg px-4 py-2" ] + [ p [ class "w-full font-bold text-green text-3xl" ] + [ text <| String.fromInt community.transferCount ] + , p [ class " text-gray-700 text-sm" ] + [ text <| t "community.index.transfers" ] + ] ] - [ text (t "menu.send") ] ] ] - ] - - -viewProofCode : Translators -> String -> Int -> Int -> Html msg -viewProofCode { t } proofCode secondsAfterClaim proofCodeValiditySeconds = - let - remainingSeconds = - proofCodeValiditySeconds - secondsAfterClaim - - timerMinutes = - remainingSeconds // 60 - - timerSeconds = - remainingSeconds - (timerMinutes * 60) - - toString timeVal = - if timeVal < 10 then - "0" ++ String.fromInt timeVal - - else - String.fromInt timeVal - - timer = - toString timerMinutes ++ ":" ++ toString timerSeconds - in - div [ class "mb-4" ] - [ span [ class "input-label block mb-1" ] - [ text (t "community.actions.form.verification_code") ] - , div [ class "text-2xl text-black font-bold inline-block align-middle mr-2" ] - [ text proofCode ] - , span [ class "whitespace-no-wrap text-body rounded-full bg-lightred px-3 py-1 text-white" ] - [ text (t "community.actions.proof.code_period_label") - , text " " - , text timer + , div [ class "w-full lg:w-1/2 h-48" ] + [ div [ class "" ] + [ div [ class "w-full relative bg-white rounded-lg p-4 h-48 overflow-hidden" ] + [ p [ class "w-full font-bold text-green text-3xl" ] + [ text <| String.fromInt community.productCount ] + , p [ class " text-gray-700 text-sm" ] + [ text <| t "community.index.products" ] + , p + [ class "w-full font-bold text-green text-3xl mt-4" ] + [ text <| String.fromInt community.orderCount ] + , p [ class " text-gray-700 text-sm" ] + [ text <| t "community.index.orders" ] + , img [ class "absolute right-0 bottom-0", src "/images/booth.svg" ] [] + ] + ] ] ] @@ -354,10 +286,14 @@ viewObjective loggedIn model metadata index objective = objective.actions |> List.sortBy (\a -> a.position |> Maybe.withDefault 0) |> List.map - (viewAction loggedIn.shared.translators - (LoggedIn.isAccount metadata.creator loggedIn) - metadata.symbol - model.date + (\action -> + Html.map GotActionMsg + (Action.viewAction loggedIn.shared.translators + (LoggedIn.isAccount metadata.creator loggedIn) + metadata.symbol + model.date + action + ) ) in if objective.isCompleted then @@ -407,371 +343,6 @@ viewObjective loggedIn model metadata index objective = --- VIEW ACTION - - -viewAction : Translators -> Bool -> Symbol -> Maybe Posix -> Action -> Html Msg -viewAction translators canEdit symbol maybeDate action = - let - { t, tr } = - translators - - text_ s = - text (t s) - - posixDeadline : Posix - posixDeadline = - action.deadline - |> Utils.posixDateTime - - deadlineStr : String - deadlineStr = - posixDeadline - |> Strftime.format "%d %B %Y" Time.utc - - pastDeadline : Bool - pastDeadline = - case action.deadline of - Just _ -> - case maybeDate of - Just today -> - posixToMillis today > posixToMillis posixDeadline - - Nothing -> - False - - Nothing -> - False - - rewardStrike : String - rewardStrike = - if pastDeadline || (action.usagesLeft < 1 && action.usages > 0) then - " line-through" - - else - "" - - dateColor : String - dateColor = - if pastDeadline then - " text-red" - - else - " text-indigo-500" - - usagesColor : String - usagesColor = - if action.usagesLeft >= 1 || action.usages == 0 then - " text-indigo-500" - - else - " text-red" - - ( claimColors, claimText ) = - if pastDeadline || (action.usagesLeft < 1 && action.usages > 0) then - ( " button-disabled", "dashboard.closed" ) - - else - ( " button button-primary", "dashboard.claim" ) - - claimSize = - if canEdit then - " w-4/5" - - else - " w-1/2" - - validatorAvatars = - List.take 3 action.validators - |> List.indexedMap - (\vIndex v -> - let - margin = - if vIndex /= 0 then - " -ml-5" - - else - "" - in - ("h-10 w-10 border-white border-4 rounded-full bg-white" ++ margin) - |> Avatar.view v.avatar - ) - |> (\vals -> - let - numValidators = - List.length action.validators - in - if numValidators > 3 then - vals - ++ [ div - [ class "h-10 w-10 flex flex-col border-white border-4 bg-grey rounded-full -ml-5" ] - [ p [ class "text-date-purple m-auto text-xs font-black leading-none tracking-wide" ] - [ text ("+" ++ String.fromInt (numValidators - 3)) ] - ] - ] - - else - vals - ) - - rewardStr = - String.fromFloat action.reward ++ " " ++ Eos.symbolToSymbolCodeString symbol - - ( usages, usagesLeft ) = - ( String.fromInt action.usages, String.fromInt action.usagesLeft ) - - validationType : String - validationType = - action.verificationType - |> VerificationType.toString - - isClosed = - pastDeadline || (action.usages > 0 && action.usagesLeft == 0) - - viewClaimButton = - button - [ class ("h-10 uppercase rounded-lg ml-1" ++ claimColors ++ claimSize) - , onClick - (if isClosed then - NoOp - - else - OpenClaimConfirmation action - ) - ] - [ if action.hasProofPhoto then - span [ class "inline-block w-4 align-middle mr-2" ] [ Icons.camera "" ] - - else - text "" - , span [ class "inline-block align-middle" ] [ text_ claimText ] - ] - in - if action.isCompleted then - text "" - - else - div [ class "py-6 px-2" ] - [ div [ class "flex flex-col border-l-8 border-light-grey rounded-l-sm pl-2 sm:pl-6" ] - [ span [ class "text-text-grey text-sm sm:text-base" ] - [ text action.description ] - , div [ class "flex flex-col sm:flex-row sm:items-center sm:justify-between" ] - [ div [ class "text-xs mt-5 sm:w-1/3" ] - [ case action.deadline of - Just _ -> - div [] - [ span [ class "capitalize text-text-grey" ] [ text_ "community.actions.available_until" ] - , span [ class dateColor ] [ text deadlineStr ] - , span [] [ text_ "community.actions.or" ] - ] - - Nothing -> - text "" - , if action.usages > 0 then - p [ class usagesColor ] - [ text (tr "community.actions.usages" [ ( "usages", usages ), ( "usagesLeft", usagesLeft ) ]) ] - - else - text "" - ] - , div [ class "sm:self-end" ] - [ div [ class "mt-3 flex flex-row items-center" ] - (if validationType == "CLAIMABLE" then - validatorAvatars - - else - [ span [ class "text-date-purple uppercase text-sm mr-1" ] - [ text_ "community.actions.automatic_analyzers" ] - , img [ src "/icons/tooltip.svg" ] [] - ] - ) - , div [ class "capitalize text-text-grey text-sm sm:text-right" ] - [ text_ "community.actions.verifiers" ] - ] - ] - , div [ class "mt-5 flex flex-row items-baseline" ] - [ div [ class ("text-green text-base mt-5 flex-grow-1" ++ rewardStrike) ] - [ span [] [ text (t "community.actions.reward" ++ ": ") ] - , span [ class "font-medium" ] [ text rewardStr ] - ] - , div [ class "hidden sm:flex sm:visible flex-row justify-end flex-grow-1" ] - [ if validationType == "CLAIMABLE" then - viewClaimButton - - else - text "" - ] - ] - ] - , div [ class "flex flex-row mt-8 justify-between sm:hidden" ] - [ if validationType == "CLAIMABLE" then - viewClaimButton - - else - text "" - ] - ] - - -viewClaimConfirmation : Translators -> ClaimConfirmationModalStatus -> Html Msg -viewClaimConfirmation { t } claimConfirmationModalStatus = - let - text_ s = - text (t s) - - modalContent acceptMsg isInProgress = - div [] - [ Modal.initWith - { closeMsg = CloseClaimConfirmation - , isVisible = True - } - |> Modal.withHeader (t "claim.modal.title") - |> Modal.withBody [ text_ "dashboard.check_claim.body" ] - |> Modal.withFooter - [ button - [ class "modal-cancel" - , classList [ ( "button-disabled", isInProgress ) ] - , onClick - (if isInProgress then - NoOp - - else - CloseClaimConfirmation - ) - , disabled isInProgress - ] - [ text_ "dashboard.check_claim.no" ] - , button - [ class "modal-accept" - , classList [ ( "button-disabled", isInProgress ) ] - , onClick - (if isInProgress then - NoOp - - else - acceptMsg - ) - , disabled isInProgress - ] - [ text (t "dashboard.check_claim.yes") - ] - ] - |> Modal.toHtml - ] - in - case claimConfirmationModalStatus of - Open action -> - let - acceptMsg = - if action.hasProofPhoto then - OpenProofSection action - - else - ClaimAction action - in - modalContent acceptMsg False - - InProgress -> - modalContent NoOp True - - Closed -> - text "" - - -viewPhotoUploader : Translators -> ProofPhotoStatus -> Html Msg -viewPhotoUploader { t } proofPhotoStatus = - let - uploadedAttrs = - case proofPhotoStatus of - Uploaded url -> - [ class " bg-no-repeat bg-center bg-cover" - , style "background-image" ("url(" ++ url ++ ")") - ] - - _ -> - [] - in - label - (class "relative bg-purple-500 w-full md:w-2/3 h-56 rounded-sm flex justify-center items-center cursor-pointer" - :: uploadedAttrs - ) - [ input - [ class "hidden-img-input" - , type_ "file" - , accept "image/*" - , Page.onFileChange EnteredPhoto - , multiple False - ] - [] - , div [] - [ case proofPhotoStatus of - Uploading -> - div [ class "spinner spinner-light" ] [] - - Uploaded _ -> - span [ class "absolute bottom-0 right-0 mr-4 mb-4 bg-orange-300 w-8 h-8 p-2 rounded-full" ] - [ Icons.camera "" ] - - _ -> - div [ class "text-white text-body font-bold text-center" ] - [ div [ class "w-10 mx-auto mb-2" ] [ Icons.camera "" ] - , div [] [ text (t "community.actions.proof.upload_photo_hint") ] - ] - ] - ] - - -viewCommunityStats : Translators -> Community.Model -> Html msg -viewCommunityStats { t } community = - div [ class "flex flex-wrap px-4 container mb-6" ] - [ div [ class "flex w-full lg:w-1/2 h-48 mb-4" ] - [ div [ class "flex-grow" ] - [ div - [ class " min-w-40 h-48 relative bg-white rounded-lg p-4 overflow-hidden" ] - [ p [ class "w-full font-bold text-green text-3xl" ] - [ text <| String.fromInt community.memberCount ] - , p [ class " text-gray-700 text-sm" ] - [ text <| t "community.index.members" ] - , img [ class "absolute bottom-0 right-0", src "/images/girl-playing-guitar.svg" ] [] - ] - ] - , div [ class "px-2 mb-6" ] - [ div [ class "flex flex-col w-40" ] - [ div [ class "w-40 h-24 bg-white rounded-lg px-4 py-2 mb-4" ] - [ p [ class "w-full font-bold text-green text-3xl" ] - [ text <| String.fromInt community.claimCount ] - , p [ class " text-gray-700 text-sm" ] - [ text <| t "community.index.claims" ] - ] - , div [ class "w-40 h-20 bg-white rounded-lg px-4 py-2" ] - [ p [ class "w-full font-bold text-green text-3xl" ] - [ text <| String.fromInt community.transferCount ] - , p [ class " text-gray-700 text-sm" ] - [ text <| t "community.index.transfers" ] - ] - ] - ] - ] - , div [ class "w-full lg:w-1/2 h-48" ] - [ div [ class "" ] - [ div [ class "w-full relative bg-white rounded-lg p-4 h-48 overflow-hidden" ] - [ p [ class "w-full font-bold text-green text-3xl" ] - [ text <| String.fromInt community.productCount ] - , p [ class " text-gray-700 text-sm" ] - [ text <| t "community.index.products" ] - , p - [ class "w-full font-bold text-green text-3xl mt-4" ] - [ text <| String.fromInt community.orderCount ] - , p [ class " text-gray-700 text-sm" ] - [ text <| t "community.index.orders" ] - , img [ class "absolute right-0 bottom-0", src "/images/booth.svg" ] [] - ] - ] - ] - ] - - - -- UPDATE @@ -786,25 +357,7 @@ type Msg -- Objective | ClickedOpenObjective Int | ClickedCloseObjective - -- Action - | OpenClaimConfirmation Community.Action - | CloseClaimConfirmation - | ClaimAction Community.Action - | GotClaimActionResponse (Result Value String) - -- Proofs - | OpenProofSection Community.Action - | CloseProofSection ReasonToCloseProofSection - | GotProofTime Int Posix - | GetUint64Name String - | GotUint64Name (Result Value String) - | Tick Time.Posix - | EnteredPhoto (List File) - | CompletedPhotoUpload (Result Http.Error String) - - -type ReasonToCloseProofSection - = CancelClicked - | TimerExpired + | GotActionMsg Action.Msg update : Msg -> Model -> LoggedIn.Model -> UpdateResult @@ -820,83 +373,28 @@ update msg model ({ shared } as loggedIn) = GotTime date -> UR.init { model | date = Just date } - GetUint64Name _ -> - model |> UR.init - - GotUint64Name (Ok uint64name) -> - case ( model.proof, model.actionId ) of - ( Just (Proof proofPhoto (Just proofCode)), Just actionId ) -> - let - verificationCode = - Claim.generateVerificationCode actionId uint64name proofCode.claimTimestamp - - newProofCode = - Just - { proofCode - | code = Just verificationCode - } - in - { model | proof = Just (Proof proofPhoto newProofCode) } - |> UR.init - - _ -> - model - |> UR.init - - GotUint64Name (Err _) -> - UR.init model - - Tick timer -> - case model.proof of - Just (Proof proofPhoto (Just proofCode)) -> - let - secondsAfterClaim = - (Time.posixToMillis timer // 1000) - proofCode.claimTimestamp - - isProofCodeActive = - (proofCode.availabilityPeriod - secondsAfterClaim) > 0 - in - if isProofCodeActive then - let - newProofCode = - Just - { proofCode - | secondsAfterClaim = secondsAfterClaim - } - in - { model | proof = Just (Proof proofPhoto newProofCode) } - |> UR.init - - else - update (CloseProofSection TimerExpired) model loggedIn + GotActionMsg actionMsg -> + let + updateClaimingAction : Action.Model -> UR.UpdateResult Model Msg extMsg + updateClaimingAction actionModel = + Action.update loggedIn actionMsg actionModel + |> UR.map + (\a -> { model | claimingAction = Just a }) + GotActionMsg + (\extMsg uResult -> uResult) + in + case actionMsg of + Action.OpenClaimConfirmation action -> + updateClaimingAction (Action.init action) _ -> - model |> UR.init + case model.claimingAction of + Just ca -> + updateClaimingAction ca - GotProofTime actionId posix -> - let - initProofCodeParts = - Just - { code = Nothing - , claimTimestamp = Time.posixToMillis posix // 1000 - , secondsAfterClaim = 0 - , availabilityPeriod = 30 * 60 - } - in - { model - | actionId = Just actionId - , proof = Just (Proof NoPhotoAdded initProofCodeParts) - } - |> UR.init - |> UR.addPort - { responseAddress = GetUint64Name (Eos.nameToString loggedIn.accountName) - , responseData = Encode.null - , data = - Encode.object - [ ( "name", Encode.string "accountNameToUint64" ) - , ( "accountName", Encode.string (Eos.nameToString loggedIn.accountName) ) - ] - } + Nothing -> + model + |> UR.init CompletedLoadCommunity (Ok community) -> case community of @@ -923,250 +421,35 @@ update msg model ({ shared } as loggedIn) = { model | openObjective = Nothing } |> UR.init - OpenClaimConfirmation action -> - { model | claimConfirmationModalStatus = Open action } - |> UR.init - - OpenProofSection action -> - let - runProofCodeTimer = - Task.perform (GotProofTime action.id) Time.now - in - if action.hasProofPhoto then - { model - | pageStatus = - case model.pageStatus of - Loaded community _ -> - Loaded community (ClaimWithProofs action) - - _ -> - model.pageStatus - , claimConfirmationModalStatus = Closed - , proof = Just (Proof NoPhotoAdded Nothing) - } - |> UR.init - |> UR.addCmd - (if action.hasProofCode then - runProofCodeTimer - - else - Cmd.none - ) - |> UR.addPort - { responseAddress = NoOp - , responseData = Encode.null - , data = - Encode.object - [ ( "id", Encode.string "communityPage" ) - , ( "name", Encode.string "scrollIntoView" ) - ] - } - - else - model - |> UR.init - - EnteredPhoto (file :: _) -> - let - uploadImage = - Api.uploadImage loggedIn.shared file CompletedPhotoUpload - - newProofs = - case model.proof of - Just (Proof _ proofCode) -> - Just (Proof Uploading proofCode) - - _ -> - Nothing - in - { model - | proof = newProofs - } - |> UR.init - |> UR.addCmd uploadImage - |> UR.addExt HideFeedback - - EnteredPhoto [] -> - UR.init model - - CompletedPhotoUpload (Ok url) -> - let - newProofs = - case model.proof of - Just (Proof _ proofCode) -> - Just (Proof (Uploaded url) proofCode) - - _ -> - Nothing - in - { model | proof = newProofs } - |> UR.init - - CompletedPhotoUpload (Err error) -> - let - newProofs = - case model.proof of - Just (Proof _ proofCode) -> - Just (Proof (UploadFailed error) proofCode) - - _ -> - Nothing - in - { model | proof = newProofs } - |> UR.init - |> UR.logHttpError msg error - - CloseClaimConfirmation -> - { model | claimConfirmationModalStatus = Closed } - |> UR.init - - CloseProofSection reason -> - { model - | pageStatus = - case model.pageStatus of - Loaded community (ClaimWithProofs _) -> - Loaded community ObjectivesAndActions - - _ -> - model.pageStatus - , claimConfirmationModalStatus = Closed - , proof = Nothing - } - |> UR.init - |> UR.addExt - (case reason of - TimerExpired -> - ShowFeedback LoggedIn.Failure (t "community.actions.proof.time_expired") - - CancelClicked -> - HideFeedback - ) - - ClaimAction action -> - let - hasPhotoError = - case model.proof of - Just (Proof (Uploaded _) _) -> - False - - Just (Proof _ _) -> - -- Error: photo wasn't uploaded while claiming with proof - True - - Nothing -> - False - - newModel = - case model.proof of - Just (Proof _ _) -> - -- Claim with proof has no confirmation - model - - Nothing -> - { model | claimConfirmationModalStatus = InProgress } - - ( proofPhotoUrl, proofCode_, proofTime ) = - case model.proof of - Just (Proof (Uploaded url) (Just { code, claimTimestamp })) -> - ( url, Maybe.withDefault "" code, claimTimestamp ) - - Just (Proof (Uploaded url) Nothing) -> - ( url, "", 0 ) - - _ -> - ( "", "", 0 ) - in - if hasPhotoError then - model - |> UR.init - |> UR.addExt (ShowFeedback LoggedIn.Failure (t "community.actions.proof.no_photo_error")) - - else if LoggedIn.isAuth loggedIn then - newModel - |> UR.init - |> UR.addPort - { responseAddress = ClaimAction action - , responseData = Encode.null - , data = - Eos.encodeTransaction - [ { accountName = shared.contracts.community - , name = "claimaction" - , authorization = - { actor = loggedIn.accountName - , permissionName = Eos.samplePermission - } - , data = - { actionId = action.id - , maker = loggedIn.accountName - , proofPhoto = proofPhotoUrl - , proofCode = proofCode_ - , proofTime = proofTime - } - |> Claim.encodeClaimAction - } - ] - } - - else - newModel - |> UR.init - |> UR.addExt (Just (ClaimAction action) |> RequiredAuthentication) - - GotClaimActionResponse (Ok _) -> - let - message = - shared.translators.tr "dashboard.check_claim.success" - [ ( "symbolCode", Eos.symbolToSymbolCodeString loggedIn.selectedCommunity ) ] - in - { model - | claimConfirmationModalStatus = Closed - , pageStatus = - case model.pageStatus of - Loaded community (ClaimWithProofs _) -> - Loaded community ObjectivesAndActions - - _ -> - model.pageStatus - , proof = Nothing - } - |> UR.init - |> UR.addExt (ShowFeedback LoggedIn.Success message) - - GotClaimActionResponse (Err _) -> - { model - | claimConfirmationModalStatus = Closed - } - |> UR.init - |> UR.addExt (ShowFeedback LoggedIn.Failure (t "dashboard.check_claim.failure")) - jsAddressToMsg : List String -> Value -> Maybe Msg jsAddressToMsg addr val = - case addr of - "ClaimAction" :: [] -> - Decode.decodeValue - (Decode.oneOf - [ Decode.field "transactionId" Decode.string |> Decode.map Ok - , Decode.succeed (Err val) - ] - ) - val - |> Result.map (Just << GotClaimActionResponse) - |> Result.withDefault Nothing - - "GetUint64Name" :: [] -> - Decode.decodeValue - (Decode.oneOf - [ Decode.field "uint64name" Decode.string |> Decode.map Ok - , Decode.succeed (Err val) - ] - ) - val - |> Result.map (Just << GotUint64Name) - |> Result.withDefault Nothing - - _ -> - Nothing + --case addr of + -- "ClaimAction" :: [] -> + -- Decode.decodeValue + -- (Decode.oneOf + -- [ Decode.field "transactionId" Decode.string |> Decode.map Ok + -- , Decode.succeed (Err val) + -- ] + -- ) + -- val + -- |> Result.map (Just << GotClaimActionResponse) + -- |> Result.withDefault Nothing + -- + -- "GetUint64Name" :: [] -> + -- Decode.decodeValue + -- (Decode.oneOf + -- [ Decode.field "uint64name" Decode.string |> Decode.map Ok + -- , Decode.succeed (Err val) + -- ] + -- ) + -- val + -- |> Result.map (Just << GotUint64Name) + -- |> Result.withDefault Nothing + -- + -- _ -> + -- Nothing + Nothing msgToString : Msg -> List String @@ -1178,12 +461,14 @@ msgToString msg = GotTime _ -> [ "GotTime" ] - Tick _ -> - [ "Tick" ] - - GotProofTime _ _ -> - [ "GotProofTime" ] + GotActionMsg _ -> + [ "GotActionMsg" ] + --Tick _ -> + -- [ "Tick" ] + -- + --GotProofTime _ _ -> + -- [ "GotProofTime" ] CompletedLoadCommunity r -> [ "CompletedLoadCommunity", UR.resultToString r ] @@ -1193,32 +478,34 @@ msgToString msg = ClickedCloseObjective -> [ "ClickedCloseObjective" ] - OpenProofSection _ -> - [ "OpenAddPhotoProof" ] - - CloseProofSection _ -> - [ "CloseAddPhotoProof" ] - - EnteredPhoto _ -> - [ "EnteredPhoto" ] - - CompletedPhotoUpload r -> - [ "CompletedPhotoUpload", UR.resultToString r ] - - OpenClaimConfirmation _ -> - [ "OpenClaimConfirmation" ] - - CloseClaimConfirmation -> - [ "CloseClaimConfirmation" ] - - ClaimAction _ -> - [ "ClaimAction" ] - - GetUint64Name _ -> - [ "GetUint64Name" ] - GotUint64Name n -> - [ "GotClaimActionResponse", UR.resultToString n ] - GotClaimActionResponse r -> - [ "GotClaimActionResponse", UR.resultToString r ] +--OpenProofSection _ -> +-- [ "OpenAddPhotoProof" ] +-- +--CloseProofSection _ -> +-- [ "CloseAddPhotoProof" ] +-- +--EnteredPhoto _ -> +-- [ "EnteredPhoto" ] +-- +--CompletedPhotoUpload r -> +-- [ "CompletedPhotoUpload", UR.resultToString r ] +-- +--OpenClaimConfirmation _ -> +-- [ "OpenClaimConfirmation" ] +-- +--CloseClaimConfirmation -> +-- [ "CloseClaimConfirmation" ] +-- +--ClaimAction _ -> +-- [ "ClaimAction" ] +-- +--GetUint64Name _ -> +-- [ "GetUint64Name" ] +-- +--GotUint64Name n -> +-- [ "GotClaimActionResponse", UR.resultToString n ] +-- +--GotClaimActionResponse r -> +-- [ "GotClaimActionResponse", UR.resultToString r ] From b6d1f3a0594ec72a87bb63a8f49c9b7efb3aa3fa Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 1 Feb 2021 21:04:00 +0300 Subject: [PATCH 27/91] Separate Action module from Community --- src/elm/Action.elm | 912 +++++++++++++++++++++++++++++++++++++ src/elm/Page/Community.elm | 192 ++++---- 2 files changed, 1014 insertions(+), 90 deletions(-) create mode 100644 src/elm/Action.elm diff --git a/src/elm/Action.elm b/src/elm/Action.elm new file mode 100644 index 000000000..be2c6307f --- /dev/null +++ b/src/elm/Action.elm @@ -0,0 +1,912 @@ +module Action exposing (Action, Model, Msg(..), UpdateResultAction, init, jsAddressToMsg, msgToString, subscriptions, update, viewAction, viewClaimConfirmation, viewClaimWithProofs) + +import Api +import Avatar +import Cambiatus.Enum.VerificationType as VerificationType exposing (VerificationType) +import Cambiatus.Scalar exposing (DateTime) +import Claim +import Eos exposing (Symbol) +import Eos.Account as Eos +import File exposing (File) +import Html exposing (Html, button, div, img, input, label, p, span, text) +import Html.Attributes exposing (accept, class, classList, disabled, multiple, src, style, type_) +import Html.Events exposing (onClick) +import Http +import Icons +import Json.Decode as Decode +import Json.Encode as Encode exposing (Value) +import Page +import Profile +import Session.LoggedIn as LoggedIn exposing (External(..)) +import Session.Shared exposing (Translators) +import Strftime +import Task +import Time exposing (Posix, posixToMillis) +import UpdateResult as UR +import Utils +import View.Modal as Modal + + +type ClaimConfirmationModalStatus + = Open Action + | InProgress + | Closed + + +type Proof + = Proof ProofPhotoStatus (Maybe ProofCode) + + +type alias ProofCode = + { code : Maybe String + , claimTimestamp : Int + , secondsAfterClaim : Int + , availabilityPeriod : Int + } + + +type ProofPhotoStatus + = NoPhotoAdded + | Uploading + | UploadFailed Http.Error + | Uploaded String + + +type alias Action = + { id : Int + , description : String + , reward : Float + , verificationReward : Float + , creator : Eos.Name + , validators : List Profile.Minimal + , usages : Int + , usagesLeft : Int + , deadline : Maybe DateTime + , verificationType : VerificationType + , verifications : Int + , isCompleted : Bool + , hasProofPhoto : Bool + , hasProofCode : Bool + , photoProofInstructions : Maybe String + , position : Maybe Int + } + + +type alias Model = + { claimConfirmationModalStatus : ClaimConfirmationModalStatus + , action : Action + , proof : Maybe Proof + } + + +init : Action -> Model +init action = + { claimConfirmationModalStatus = Closed + , action = action + , proof = Nothing -- TODO: Should it be Nothing? + } + + +type Msg + = NoOp + -- Action + | OpenClaimConfirmation Action + | CloseClaimConfirmation + | ClaimAction Action + | GotClaimActionResponse (Result Value String) + -- Proofs + | OpenProofSection Action + | CloseProofSection ReasonToCloseProofSection + | GotProofTime Int Posix + | GetUint64Name String + | GotUint64Name (Result Value String) + | Tick Time.Posix + | EnteredPhoto (List File) + | CompletedPhotoUpload (Result Http.Error String) + + +type ReasonToCloseProofSection + = CancelClicked + | TimerExpired + + + +-- VIEW ACTION + + +viewAction : Translators -> Bool -> Symbol -> Maybe Posix -> Action -> Html Msg +viewAction translators canEdit symbol maybeDate action = + let + { t, tr } = + translators + + text_ s = + text (t s) + + posixDeadline : Posix + posixDeadline = + action.deadline + |> Utils.posixDateTime + + deadlineStr : String + deadlineStr = + posixDeadline + |> Strftime.format "%d %B %Y" Time.utc + + pastDeadline : Bool + pastDeadline = + case action.deadline of + Just _ -> + case maybeDate of + Just today -> + posixToMillis today > posixToMillis posixDeadline + + Nothing -> + False + + Nothing -> + False + + rewardStrike : String + rewardStrike = + if pastDeadline || (action.usagesLeft < 1 && action.usages > 0) then + " line-through" + + else + "" + + dateColor : String + dateColor = + if pastDeadline then + " text-red" + + else + " text-indigo-500" + + usagesColor : String + usagesColor = + if action.usagesLeft >= 1 || action.usages == 0 then + " text-indigo-500" + + else + " text-red" + + ( claimColors, claimText ) = + if pastDeadline || (action.usagesLeft < 1 && action.usages > 0) then + ( " button-disabled", "dashboard.closed" ) + + else + ( " button button-primary", "dashboard.claim" ) + + claimSize = + if canEdit then + " w-4/5" + + else + " w-1/2" + + validatorAvatars = + List.take 3 action.validators + |> List.indexedMap + (\vIndex v -> + let + margin = + if vIndex /= 0 then + " -ml-5" + + else + "" + in + ("h-10 w-10 border-white border-4 rounded-full bg-white" ++ margin) + |> Avatar.view v.avatar + ) + |> (\vals -> + let + numValidators = + List.length action.validators + in + if numValidators > 3 then + vals + ++ [ div + [ class "h-10 w-10 flex flex-col border-white border-4 bg-grey rounded-full -ml-5" ] + [ p [ class "text-date-purple m-auto text-xs font-black leading-none tracking-wide" ] + [ text ("+" ++ String.fromInt (numValidators - 3)) ] + ] + ] + + else + vals + ) + + rewardStr = + String.fromFloat action.reward ++ " " ++ Eos.symbolToSymbolCodeString symbol + + ( usages, usagesLeft ) = + ( String.fromInt action.usages, String.fromInt action.usagesLeft ) + + validationType : String + validationType = + action.verificationType + |> VerificationType.toString + + isClosed = + pastDeadline + || (action.usages > 0 && action.usagesLeft == 0) + + viewClaimButton = + button + [ class ("h-10 uppercase rounded-lg ml-1" ++ claimColors ++ claimSize) + , onClick + (if isClosed then + NoOp + + else + OpenClaimConfirmation action + ) + ] + [ if action.hasProofPhoto then + span [ class "inline-block w-4 align-middle mr-2" ] [ Icons.camera "" ] + + else + text "" + , span [ class "inline-block align-middle" ] [ text_ claimText ] + ] + in + if action.isCompleted then + text "" + + else + div [ class "py-6 px-2" ] + [ div [ class "flex flex-col border-l-8 border-light-grey rounded-l-sm pl-2 sm:pl-6" ] + [ span [ class "text-text-grey text-sm sm:text-base" ] + [ text action.description ] + , div [ class "flex flex-col sm:flex-row sm:items-center sm:justify-between" ] + [ div [ class "text-xs mt-5 sm:w-1/3" ] + [ case action.deadline of + Just _ -> + div [] + [ span [ class "capitalize text-text-grey" ] [ text_ "community.actions.available_until" ] + , span [ class dateColor ] [ text deadlineStr ] + , span [] [ text_ "community.actions.or" ] + ] + + Nothing -> + text "" + , if action.usages > 0 then + p [ class usagesColor ] + [ text (tr "community.actions.usages" [ ( "usages", usages ), ( "usagesLeft", usagesLeft ) ]) ] + + else + text "" + ] + , div [ class "sm:self-end" ] + [ div [ class "mt-3 flex flex-row items-center" ] + (if validationType == "CLAIMABLE" then + validatorAvatars + + else + [ span [ class "text-date-purple uppercase text-sm mr-1" ] + [ text_ "community.actions.automatic_analyzers" ] + , img [ src "/icons/tooltip.svg" ] [] + ] + ) + , div [ class "capitalize text-text-grey text-sm sm:text-right" ] + [ text_ "community.actions.verifiers" ] + ] + ] + , div [ class "mt-5 flex flex-row items-baseline" ] + [ div [ class ("text-green text-base mt-5 flex-grow-1" ++ rewardStrike) ] + [ span [] [ text (t "community.actions.reward" ++ ": ") ] + , span [ class "font-medium" ] [ text rewardStr ] + ] + , div [ class "hidden sm:flex sm:visible flex-row justify-end flex-grow-1" ] + [ if validationType == "CLAIMABLE" then + viewClaimButton + + else + text "" + ] + ] + ] + , div [ class "flex flex-row mt-8 justify-between sm:hidden" ] + [ if validationType == "CLAIMABLE" then + viewClaimButton + + else + text "" + ] + ] + + +viewClaimConfirmation : Translators -> ClaimConfirmationModalStatus -> Html Msg +viewClaimConfirmation { t } claimConfirmationModalStatus = + let + text_ s = + text (t s) + + modalContent acceptMsg isInProgress = + div [] + [ Modal.initWith + { closeMsg = CloseClaimConfirmation + , isVisible = True + } + |> Modal.withHeader (t "claim.modal.title") + |> Modal.withBody [ text_ "dashboard.check_claim.body" ] + |> Modal.withFooter + [ button + [ class "modal-cancel" + , classList [ ( "button-disabled", isInProgress ) ] + , onClick + (if isInProgress then + NoOp + + else + CloseClaimConfirmation + ) + , disabled isInProgress + ] + [ text_ "dashboard.check_claim.no" ] + , button + [ class "modal-accept" + , classList [ ( "button-disabled", isInProgress ) ] + , onClick + (if isInProgress then + NoOp + + else + acceptMsg + ) + , disabled isInProgress + ] + [ text (t "dashboard.check_claim.yes") + ] + ] + |> Modal.toHtml + ] + in + case claimConfirmationModalStatus of + Open action -> + let + acceptMsg = + if action.hasProofPhoto then + OpenProofSection action + + else + ClaimAction action + in + modalContent acceptMsg False + + InProgress -> + modalContent NoOp True + + Closed -> + text "" + + +viewPhotoUploader : Translators -> ProofPhotoStatus -> Html Msg +viewPhotoUploader { t } proofPhotoStatus = + let + uploadedAttrs = + case proofPhotoStatus of + Uploaded url -> + [ class " bg-no-repeat bg-center bg-cover" + , style "background-image" ("url(" ++ url ++ ")") + ] + + _ -> + [] + in + label + (class "relative bg-purple-500 w-full md:w-2/3 h-56 rounded-sm flex justify-center items-center cursor-pointer" + :: uploadedAttrs + ) + [ input + [ class "hidden-img-input" + , type_ "file" + , accept "image/*" + , Page.onFileChange EnteredPhoto + , multiple False + ] + [] + , div [] + [ case proofPhotoStatus of + Uploading -> + div [ class "spinner spinner-light" ] [] + + Uploaded _ -> + span [ class "absolute bottom-0 right-0 mr-4 mb-4 bg-orange-300 w-8 h-8 p-2 rounded-full" ] + [ Icons.camera "" ] + + _ -> + div [ class "text-white text-body font-bold text-center" ] + [ div [ class "w-10 mx-auto mb-2" ] [ Icons.camera "" ] + , div [] [ text (t "community.actions.proof.upload_photo_hint") ] + ] + ] + ] + + +viewClaimWithProofs : Maybe Proof -> Translators -> Action -> Html Msg +viewClaimWithProofs proofs translators action = + let + { t } = + translators + + isUploadingInProgress = + case proofs of + Just (Proof Uploading _) -> + True + + _ -> + False + in + div [ class "bg-white border-t border-gray-300" ] + [ div [ class "container p-4 mx-auto" ] + [ div [ class "heading-bold leading-7 font-bold" ] [ text <| t "community.actions.proof.title" ] + , p [ class "mb-4" ] + [ text <| + Maybe.withDefault "" action.photoProofInstructions + ] + , case proofs of + Just (Proof _ (Just { code, secondsAfterClaim, availabilityPeriod })) -> + case code of + Just c -> + viewProofCode + translators + c + secondsAfterClaim + availabilityPeriod + + _ -> + text "" + + _ -> + text "" + , div [ class "mb-4" ] + [ span [ class "input-label block mb-2" ] + [ text (t "community.actions.proof.photo") ] + , case proofs of + Just (Proof photoStatus _) -> + viewPhotoUploader translators photoStatus + + _ -> + text "" + ] + , div [ class "md:flex" ] + [ button + [ class "modal-cancel" + , onClick + (if isUploadingInProgress then + NoOp + + else + CloseProofSection CancelClicked + ) + , classList [ ( "button-disabled", isUploadingInProgress ) ] + , disabled isUploadingInProgress + ] + [ text (t "menu.cancel") ] + , button + [ class "modal-accept" + , classList [ ( "button-disabled", isUploadingInProgress ) ] + , onClick + (if isUploadingInProgress then + NoOp + + else + ClaimAction action + ) + , disabled isUploadingInProgress + ] + [ text (t "menu.send") ] + ] + ] + ] + + +viewProofCode : Translators -> String -> Int -> Int -> Html msg +viewProofCode { t } proofCode secondsAfterClaim proofCodeValiditySeconds = + let + remainingSeconds = + proofCodeValiditySeconds - secondsAfterClaim + + timerMinutes = + remainingSeconds // 60 + + timerSeconds = + remainingSeconds - (timerMinutes * 60) + + toString timeVal = + if timeVal < 10 then + "0" ++ String.fromInt timeVal + + else + String.fromInt timeVal + + timer = + toString timerMinutes ++ ":" ++ toString timerSeconds + in + div [ class "mb-4" ] + [ span [ class "input-label block mb-1" ] + [ text (t "community.actions.form.verification_code") ] + , div [ class "text-2xl text-black font-bold inline-block align-middle mr-2" ] + [ text proofCode ] + , span [ class "whitespace-no-wrap text-body rounded-full bg-lightred px-3 py-1 text-white" ] + [ text (t "community.actions.proof.code_period_label") + , text " " + , text timer + ] + ] + + +subscriptions : Model -> Sub Msg +subscriptions model = + case model.proof of + Just (Proof _ (Just _)) -> + Time.every 1000 Tick + + _ -> + Sub.none + + + +-- UPDATE + + +type alias UpdateResultAction = + UR.UpdateResult Model Msg (External Msg) + + +update : LoggedIn.Model -> Msg -> Model -> UpdateResultAction +update ({ shared } as loggedIn) msg model = + let + { t } = + shared.translators + in + case msg of + OpenClaimConfirmation action -> + { model | claimConfirmationModalStatus = Open action } + |> UR.init + + OpenProofSection action -> + let + runProofCodeTimer = + Task.perform (GotProofTime action.id) Time.now + in + if action.hasProofPhoto then + { model + | claimConfirmationModalStatus = Closed + , proof = Just (Proof NoPhotoAdded Nothing) + } + |> UR.init + |> UR.addCmd + (if action.hasProofCode then + runProofCodeTimer + + else + Cmd.none + ) + |> UR.addPort + { responseAddress = NoOp + , responseData = Encode.null + , data = + Encode.object + [ ( "id", Encode.string "communityPage" ) + , ( "name", Encode.string "scrollIntoView" ) + ] + } + + else + model |> UR.init + + EnteredPhoto (file :: _) -> + let + uploadImage = + Api.uploadImage shared file CompletedPhotoUpload + + newProof = + case model.proof of + Just (Proof _ proofCode) -> + Just (Proof Uploading proofCode) + + _ -> + Nothing + in + { model + | proof = newProof + } + |> UR.init + |> UR.addCmd uploadImage + |> UR.addExt HideFeedback + + EnteredPhoto [] -> + model + |> UR.init + + CompletedPhotoUpload (Ok url) -> + let + newProofs = + case model.proof of + Just (Proof _ proofCode) -> + Just (Proof (Uploaded url) proofCode) + + _ -> + Nothing + in + { model | proof = newProofs } + |> UR.init + + CompletedPhotoUpload (Err error) -> + let + newProofs = + case model.proof of + Just (Proof _ proofCode) -> + Just (Proof (UploadFailed error) proofCode) + + _ -> + Nothing + in + { model | proof = newProofs } + |> UR.init + |> UR.logHttpError msg error + + CloseClaimConfirmation -> + { model | claimConfirmationModalStatus = Closed } + |> UR.init + + CloseProofSection reason -> + { model + | claimConfirmationModalStatus = Closed + , proof = Nothing + } + |> UR.init + |> UR.addExt + (case reason of + TimerExpired -> + ShowFeedback LoggedIn.Failure (t "community.actions.proof.time_expired") + + CancelClicked -> + HideFeedback + ) + + ClaimAction action -> + let + hasPhotoError = + case model.proof of + Just (Proof (Uploaded _) _) -> + False + + Just (Proof _ _) -> + -- Error: photo wasn't uploaded while claiming with proof + True + + Nothing -> + False + + newModel = + case model.proof of + Just (Proof _ _) -> + -- Claim with proof has no confirmation + model + + Nothing -> + { model | claimConfirmationModalStatus = InProgress } + + ( proofPhotoUrl, proofCode_, proofTime ) = + case model.proof of + Just (Proof (Uploaded url) (Just { code, claimTimestamp })) -> + ( url, Maybe.withDefault "" code, claimTimestamp ) + + Just (Proof (Uploaded url) Nothing) -> + ( url, "", 0 ) + + _ -> + ( "", "", 0 ) + in + if hasPhotoError then + model + |> UR.init + |> UR.addExt (ShowFeedback LoggedIn.Failure (t "community.actions.proof.no_photo_error")) + + else if LoggedIn.isAuth loggedIn then + newModel + |> UR.init + |> UR.addPort + { responseAddress = ClaimAction action + , responseData = Encode.null + , data = + Eos.encodeTransaction + [ { accountName = shared.contracts.community + , name = "claimaction" + , authorization = + { actor = loggedIn.accountName + , permissionName = Eos.samplePermission + } + , data = + { actionId = action.id + , maker = loggedIn.accountName + , proofPhoto = proofPhotoUrl + , proofCode = proofCode_ + , proofTime = proofTime + } + |> Claim.encodeClaimAction + } + ] + } + + else + newModel + |> UR.init + |> UR.addExt (Just (ClaimAction action) |> RequiredAuthentication) + + GotClaimActionResponse (Ok _) -> + let + message = + shared.translators.tr "dashboard.check_claim.success" + [ ( "symbolCode", Eos.symbolToSymbolCodeString loggedIn.selectedCommunity ) ] + in + { model + | claimConfirmationModalStatus = Closed + , proof = Nothing + } + |> UR.init + |> UR.addExt (ShowFeedback LoggedIn.Success message) + + GotClaimActionResponse (Err _) -> + { model + | claimConfirmationModalStatus = Closed + } + |> UR.init + |> UR.addExt (ShowFeedback LoggedIn.Failure (t "dashboard.check_claim.failure")) + + GetUint64Name _ -> + model |> UR.init + + GotUint64Name (Ok uint64name) -> + case ( model.proof, model.action.id ) of + ( Just (Proof proofPhoto (Just proofCode)), actionId ) -> + let + verificationCode = + Claim.generateVerificationCode actionId uint64name proofCode.claimTimestamp + + newProofCode = + Just + { proofCode + | code = Just verificationCode + } + in + { model | proof = Just (Proof proofPhoto newProofCode) } + |> UR.init + + _ -> + model + |> UR.init + + GotUint64Name (Err _) -> + model |> UR.init + + Tick timer -> + case model.proof of + Just (Proof proofPhoto (Just proofCode)) -> + let + secondsAfterClaim = + (Time.posixToMillis timer // 1000) - proofCode.claimTimestamp + + isProofCodeActive = + (proofCode.availabilityPeriod - secondsAfterClaim) > 0 + in + if isProofCodeActive then + let + newProofCode = + Just + { proofCode + | secondsAfterClaim = secondsAfterClaim + } + in + { model | proof = Just (Proof proofPhoto newProofCode) } |> UR.init + + else + update loggedIn (CloseProofSection TimerExpired) model + + _ -> + model |> UR.init + + GotProofTime actionId posix -> + let + initProofCodeParts = + Just + { code = Nothing + , claimTimestamp = Time.posixToMillis posix // 1000 + , secondsAfterClaim = 0 + , availabilityPeriod = 30 * 60 + } + in + { model + | --actionId = Just actionId + proof = Just (Proof NoPhotoAdded initProofCodeParts) + } + |> UR.init + |> UR.addPort + { responseAddress = GetUint64Name (Eos.nameToString loggedIn.accountName) + , responseData = Encode.null + , data = + Encode.object + [ ( "name", Encode.string "accountNameToUint64" ) + , ( "accountName", Encode.string (Eos.nameToString loggedIn.accountName) ) + ] + } + + NoOp -> + model |> UR.init + + +jsAddressToMsg : List String -> Value -> Maybe Msg +jsAddressToMsg addr val = + case addr of + "ClaimAction" :: [] -> + Decode.decodeValue + (Decode.oneOf + [ Decode.field "transactionId" Decode.string |> Decode.map Ok + , Decode.succeed (Err val) + ] + ) + val + |> Result.map (Just << GotClaimActionResponse) + |> Result.withDefault Nothing + + "GetUint64Name" :: [] -> + Decode.decodeValue + (Decode.oneOf + [ Decode.field "uint64name" Decode.string |> Decode.map Ok + , Decode.succeed (Err val) + ] + ) + val + |> Result.map (Just << GotUint64Name) + |> Result.withDefault Nothing + + _ -> + Nothing + + +msgToString : Msg -> List String +msgToString msg = + case msg of + NoOp -> + [ "NoOp" ] + + Tick _ -> + [ "Tick" ] + + GotProofTime _ _ -> + [ "GotProofTime" ] + + OpenProofSection _ -> + [ "OpenProofSection" ] + + CloseProofSection _ -> + [ "CloseProofSection" ] + + EnteredPhoto _ -> + [ "EnteredPhoto" ] + + CompletedPhotoUpload r -> + [ "CompletedPhotoUpload", UR.resultToString r ] + + OpenClaimConfirmation _ -> + [ "OpenClaimConfirmation" ] + + CloseClaimConfirmation -> + [ "CloseClaimConfirmation" ] + + ClaimAction _ -> + [ "ClaimAction" ] + + GetUint64Name _ -> + [ "GetUint64Name" ] + + GotUint64Name n -> + [ "GotUint64Name", UR.resultToString n ] + + GotClaimActionResponse r -> + [ "GotClaimActionResponse", UR.resultToString r ] diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index 1101aa5f8..fa6ade74f 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -10,33 +10,22 @@ module Page.Community exposing ) import Action -import Api import Api.Graphql -import Avatar -import Cambiatus.Enum.VerificationType as VerificationType -import Claim -import Community exposing (Action, Model) +import Browser exposing (UrlRequest(..)) +import Community exposing (Model) import Eos exposing (Symbol) -import Eos.Account as Eos -import File exposing (File) import Graphql.Http -import Html exposing (Html, button, div, img, input, label, p, span, text) -import Html.Attributes exposing (accept, class, classList, disabled, id, multiple, src, style, type_) +import Html exposing (Html, button, div, img, p, text) +import Html.Attributes exposing (class, classList, id, src) import Html.Events exposing (onClick) -import Http -import Icons -import Json.Decode as Decode -import Json.Encode as Encode exposing (Value) +import Json.Encode exposing (Value) import Page import Route -import Session.LoggedIn as LoggedIn exposing (External(..), FeedbackStatus(..)) -import Session.Shared exposing (Shared, Translators) -import Strftime +import Session.LoggedIn as LoggedIn exposing (External(..), FeedbackStatus(..), mapExternal) +import Session.Shared exposing (Translators) import Task -import Time exposing (Posix, posixToMillis) +import Time exposing (Posix) import UpdateResult as UR -import Utils -import View.Modal as Modal @@ -362,10 +351,6 @@ type Msg update : Msg -> Model -> LoggedIn.Model -> UpdateResult update msg model ({ shared } as loggedIn) = - let - t = - shared.translators.t - in case msg of NoOp -> UR.init model @@ -375,18 +360,101 @@ update msg model ({ shared } as loggedIn) = GotActionMsg actionMsg -> let - updateClaimingAction : Action.Model -> UR.UpdateResult Model Msg extMsg + updateClaimingAction : Action.Model -> UpdateResult updateClaimingAction actionModel = + let + cb : External Action.Msg -> UpdateResult -> UpdateResult + cb = + \extMsgAction uResult -> + uResult + |> UR.addExt (mapExternal GotActionMsg extMsgAction) + in Action.update loggedIn actionMsg actionModel |> UR.map (\a -> { model | claimingAction = Just a }) GotActionMsg - (\extMsg uResult -> uResult) + cb in case actionMsg of Action.OpenClaimConfirmation action -> updateClaimingAction (Action.init action) + Action.GotClaimActionResponse (Ok _) -> + case model.claimingAction of + Just ca -> + let + updatePageStatus m = + { m + | pageStatus = + case model.pageStatus of + Loaded community (ClaimWithProofs _) -> + Loaded community ObjectivesAndActions + + _ -> + m.pageStatus + } + in + updateClaimingAction ca + |> UR.mapModel updatePageStatus + + Nothing -> + model + |> UR.init + + Action.OpenProofSection action -> + case model.claimingAction of + Just ca -> + if ca.action.hasProofPhoto then + let + updatePageStatus m = + { m + | pageStatus = + case model.pageStatus of + Loaded community _ -> + Loaded community (ClaimWithProofs action) + + _ -> + model.pageStatus + } + in + updateClaimingAction ca + |> UR.mapModel updatePageStatus + + else + model + |> UR.init + + Nothing -> + model + |> UR.init + + Action.CloseProofSection _ -> + case model.claimingAction of + Just ca -> + if ca.action.hasProofPhoto then + let + updatePageStatus m = + { m + | pageStatus = + case model.pageStatus of + Loaded community (ClaimWithProofs _) -> + Loaded community ObjectivesAndActions + + _ -> + model.pageStatus + } + in + updateClaimingAction ca + |> UR.mapModel updatePageStatus + + else + model + |> UR.init + + Nothing -> + model + |> UR.init + _ -> case model.claimingAction of Just ca -> @@ -424,32 +492,13 @@ update msg model ({ shared } as loggedIn) = jsAddressToMsg : List String -> Value -> Maybe Msg jsAddressToMsg addr val = - --case addr of - -- "ClaimAction" :: [] -> - -- Decode.decodeValue - -- (Decode.oneOf - -- [ Decode.field "transactionId" Decode.string |> Decode.map Ok - -- , Decode.succeed (Err val) - -- ] - -- ) - -- val - -- |> Result.map (Just << GotClaimActionResponse) - -- |> Result.withDefault Nothing - -- - -- "GetUint64Name" :: [] -> - -- Decode.decodeValue - -- (Decode.oneOf - -- [ Decode.field "uint64name" Decode.string |> Decode.map Ok - -- , Decode.succeed (Err val) - -- ] - -- ) - -- val - -- |> Result.map (Just << GotUint64Name) - -- |> Result.withDefault Nothing - -- - -- _ -> - -- Nothing - Nothing + case addr of + "GotActionMsg" :: remainAddress -> + Action.jsAddressToMsg remainAddress val + |> Maybe.map GotActionMsg + + _ -> + Nothing msgToString : Msg -> List String @@ -461,14 +510,9 @@ msgToString msg = GotTime _ -> [ "GotTime" ] - GotActionMsg _ -> - [ "GotActionMsg" ] + GotActionMsg actionMsg -> + "GotActionMsg" :: Action.msgToString actionMsg - --Tick _ -> - -- [ "Tick" ] - -- - --GotProofTime _ _ -> - -- [ "GotProofTime" ] CompletedLoadCommunity r -> [ "CompletedLoadCommunity", UR.resultToString r ] @@ -477,35 +521,3 @@ msgToString msg = ClickedCloseObjective -> [ "ClickedCloseObjective" ] - - - ---OpenProofSection _ -> --- [ "OpenAddPhotoProof" ] --- ---CloseProofSection _ -> --- [ "CloseAddPhotoProof" ] --- ---EnteredPhoto _ -> --- [ "EnteredPhoto" ] --- ---CompletedPhotoUpload r -> --- [ "CompletedPhotoUpload", UR.resultToString r ] --- ---OpenClaimConfirmation _ -> --- [ "OpenClaimConfirmation" ] --- ---CloseClaimConfirmation -> --- [ "CloseClaimConfirmation" ] --- ---ClaimAction _ -> --- [ "ClaimAction" ] --- ---GetUint64Name _ -> --- [ "GetUint64Name" ] --- ---GotUint64Name n -> --- [ "GotClaimActionResponse", UR.resultToString n ] --- ---GotClaimActionResponse r -> --- [ "GotClaimActionResponse", UR.resultToString r ] From dc726db1f4222dcc38c1ad6f84ab68b13a3b462c Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Tue, 2 Feb 2021 10:41:05 +0300 Subject: [PATCH 28/91] Remove obsolete code --- src/elm/Page/Community.elm | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index fa6ade74f..9a3cbc2f8 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -46,12 +46,8 @@ initModel : LoggedIn.Model -> Symbol -> Model initModel _ _ = { date = Nothing , pageStatus = Loading - , actionId = Nothing , openObjective = Nothing , claimingAction = Nothing - - --, claimConfirmationModalStatus = Closed - --, proof = Nothing } @@ -77,11 +73,7 @@ type alias Model = { date : Maybe Posix , claimingAction : Maybe Action.Model , pageStatus : PageStatus - , actionId : Maybe Int , openObjective : Maybe Int - - --, claimConfirmationModalStatus : ClaimConfirmationModalStatus - --, proof : Maybe Proof } From 443236a1585037ff8f759136cc5d62f747d2ac58 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Tue, 2 Feb 2021 11:37:37 +0300 Subject: [PATCH 29/91] Cleanup imports --- src/elm/Community.elm | 40 ++++++++++++------------- src/elm/Page/Community.elm | 4 +-- src/elm/Page/Community/ActionEditor.elm | 3 +- src/elm/Page/Community/Objectives.elm | 3 +- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/elm/Community.elm b/src/elm/Community.elm index b2bd3e531..dfd20015b 100755 --- a/src/elm/Community.elm +++ b/src/elm/Community.elm @@ -42,7 +42,7 @@ module Community exposing import Cambiatus.Enum.VerificationType exposing (VerificationType(..)) import Cambiatus.Object -import Cambiatus.Object.Action as Action +import Cambiatus.Object.Action as ActionObject import Cambiatus.Object.Check as Check import Cambiatus.Object.Claim as Claim exposing (ChecksOptionalArguments) import Cambiatus.Object.Community as Community @@ -359,22 +359,22 @@ type alias Action = actionSelectionSet : SelectionSet Action Cambiatus.Object.Action actionSelectionSet = SelectionSet.succeed Action - |> with Action.id - |> with Action.description - |> with Action.reward - |> with Action.verifierReward - |> with (Eos.nameSelectionSet Action.creatorId) - |> with (Action.validators Profile.minimalSelectionSet) - |> with Action.usages - |> with Action.usagesLeft - |> with Action.deadline - |> with Action.verificationType - |> with Action.verifications - |> with Action.isCompleted - |> with (SelectionSet.map (Maybe.withDefault False) Action.hasProofPhoto) - |> with (SelectionSet.map (Maybe.withDefault False) Action.hasProofCode) - |> with Action.photoProofInstructions - |> with Action.position + |> with ActionObject.id + |> with ActionObject.description + |> with ActionObject.reward + |> with ActionObject.verifierReward + |> with (Eos.nameSelectionSet ActionObject.creatorId) + |> with (ActionObject.validators Profile.minimalSelectionSet) + |> with ActionObject.usages + |> with ActionObject.usagesLeft + |> with ActionObject.deadline + |> with ActionObject.verificationType + |> with ActionObject.verifications + |> with ActionObject.isCompleted + |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofPhoto) + |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofCode) + |> with ActionObject.photoProofInstructions + |> with ActionObject.position type Verification @@ -626,9 +626,9 @@ checkSelectionSet = verificationActionSelectionSet : SelectionSet ActionResponse Cambiatus.Object.Action verificationActionSelectionSet = SelectionSet.succeed ActionResponse - |> with Action.id - |> with Action.description - |> with (Action.objective verificationObjectiveSelectionSet) + |> with ActionObject.id + |> with ActionObject.description + |> with (ActionObject.objective verificationObjectiveSelectionSet) verificationObjectiveSelectionSet : SelectionSet ObjectiveResponse Cambiatus.Object.Objective diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index 9a3cbc2f8..d99f8d30a 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -9,7 +9,7 @@ module Page.Community exposing , view ) -import Action +import Action exposing (Action) import Api.Graphql import Browser exposing (UrlRequest(..)) import Community exposing (Model) @@ -86,7 +86,7 @@ type PageStatus type ActiveSection = ObjectivesAndActions - | ClaimWithProofs Community.Action + | ClaimWithProofs Action diff --git a/src/elm/Page/Community/ActionEditor.elm b/src/elm/Page/Community/ActionEditor.elm index f7e8810dc..db9e88ffb 100755 --- a/src/elm/Page/Community/ActionEditor.elm +++ b/src/elm/Page/Community/ActionEditor.elm @@ -8,6 +8,7 @@ module Page.Community.ActionEditor exposing , view ) +import Action exposing (Action) import Api.Graphql import Cambiatus.Enum.VerificationType as VerificationType import Cambiatus.Scalar exposing (DateTime(..)) @@ -162,7 +163,7 @@ initForm = } -editForm : Form -> Community.Action -> Form +editForm : Form -> Action -> Form editForm form action = let dateValidator : Maybe (Validator String) diff --git a/src/elm/Page/Community/Objectives.elm b/src/elm/Page/Community/Objectives.elm index 517223460..400ce81f5 100644 --- a/src/elm/Page/Community/Objectives.elm +++ b/src/elm/Page/Community/Objectives.elm @@ -1,5 +1,6 @@ module Page.Community.Objectives exposing (Model, Msg, init, msgToString, update, view) +import Action exposing (Action) import Api.Graphql import Cambiatus.Enum.VerificationType as VerificationType import Community exposing (Model) @@ -192,7 +193,7 @@ viewObjective ({ shared } as loggedIn) model community index objective = ] -viewAction : LoggedIn.Model -> Model -> Int -> Community.Action -> Html Msg +viewAction : LoggedIn.Model -> Model -> Int -> Action -> Html Msg viewAction ({ shared } as loggedIn) model objectiveId action = let posixDeadline : Posix From ed39e47b61cb6e432e4cffaa575a700c38292be4 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 3 Feb 2021 09:08:35 +0300 Subject: [PATCH 30/91] Move claiming helpers to the Action module --- src/elm/Action.elm | 36 +++++++++++++++++++++++++++++++++--- src/elm/Claim.elm | 33 --------------------------------- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index be2c6307f..bcd3f7aa7 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -4,7 +4,6 @@ import Api import Avatar import Cambiatus.Enum.VerificationType as VerificationType exposing (VerificationType) import Cambiatus.Scalar exposing (DateTime) -import Claim import Eos exposing (Symbol) import Eos.Account as Eos import File exposing (File) @@ -19,6 +18,7 @@ import Page import Profile import Session.LoggedIn as LoggedIn exposing (External(..)) import Session.Shared exposing (Translators) +import Sha256 exposing (sha256) import Strftime import Task import Time exposing (Posix, posixToMillis) @@ -72,6 +72,15 @@ type alias Action = } +type alias ClaimedAction = + { actionId : Int + , maker : Eos.Name + , proofPhoto : String + , proofCode : String + , proofTime : Int + } + + type alias Model = { claimConfirmationModalStatus : ClaimConfirmationModalStatus , action : Action @@ -729,7 +738,7 @@ update ({ shared } as loggedIn) msg model = , proofCode = proofCode_ , proofTime = proofTime } - |> Claim.encodeClaimAction + |> encodeClaimAction } ] } @@ -767,7 +776,7 @@ update ({ shared } as loggedIn) msg model = ( Just (Proof proofPhoto (Just proofCode)), actionId ) -> let verificationCode = - Claim.generateVerificationCode actionId uint64name proofCode.claimTimestamp + generateVerificationCode actionId uint64name proofCode.claimTimestamp newProofCode = Just @@ -910,3 +919,24 @@ msgToString msg = GotClaimActionResponse r -> [ "GotClaimActionResponse", UR.resultToString r ] + + +encodeClaimAction : ClaimedAction -> Encode.Value +encodeClaimAction c = + Encode.object + [ ( "action_id", Encode.int c.actionId ) + , ( "maker", Eos.encodeName c.maker ) + , ( "proof_photo", Encode.string c.proofPhoto ) + , ( "proof_code", Encode.string c.proofCode ) + , ( "proof_time", Encode.int c.proofTime ) + ] + + +generateVerificationCode : Int -> String -> Int -> String +generateVerificationCode actionId makerAccountUint64 proofTimeSeconds = + (String.fromInt actionId + ++ makerAccountUint64 + ++ String.fromInt proofTimeSeconds + ) + |> sha256 + |> String.slice 0 8 diff --git a/src/elm/Claim.elm b/src/elm/Claim.elm index 3236508d3..fafa5b637 100644 --- a/src/elm/Claim.elm +++ b/src/elm/Claim.elm @@ -6,9 +6,7 @@ module Claim exposing , Msg(..) , Paginated , claimPaginatedSelectionSet - , encodeClaimAction , encodeVerification - , generateVerificationCode , isValidated , isValidator , isVotable @@ -45,7 +43,6 @@ import Profile import Route exposing (Route) import Session.LoggedIn as LoggedIn import Session.Shared exposing (Translators) -import Sha256 exposing (sha256) import Strftime import Time import Utils @@ -107,26 +104,6 @@ type alias Action = -- Claim Action -type alias ClaimAction = - { actionId : Int - , maker : Eos.Name - , proofPhoto : String - , proofCode : String - , proofTime : Int - } - - -encodeClaimAction : ClaimAction -> Encode.Value -encodeClaimAction c = - Encode.object - [ ( "action_id", Encode.int c.actionId ) - , ( "maker", Eos.encodeName c.maker ) - , ( "proof_photo", Encode.string c.proofPhoto ) - , ( "proof_code", Encode.string c.proofCode ) - , ( "proof_time", Encode.int c.proofTime ) - ] - - isValidated : Model -> Eos.Name -> Bool isValidated claim user = claim.status /= Pending || List.any (\c -> c.validator.account == user) claim.checks @@ -169,16 +146,6 @@ encodeVerification claimId validator vote = ] -generateVerificationCode : Int -> String -> Int -> String -generateVerificationCode actionId makerAccountUint64 proofTimeSeconds = - (String.fromInt actionId - ++ makerAccountUint64 - ++ String.fromInt proofTimeSeconds - ) - |> sha256 - |> String.slice 0 8 - - -- GraphQL From cb94ce2d22690d33d63a6071cb21b4bd03c26dd0 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 3 Feb 2021 10:30:38 +0300 Subject: [PATCH 31/91] WIP: Exclude objective from action type --- src/elm/Action.elm | 43 ++++++++++++++- src/elm/Claim.elm | 69 +++++++++++++------------ src/elm/Community.elm | 13 ++--- src/elm/Page/Community/ActionEditor.elm | 2 +- src/elm/Page/Community/Objectives.elm | 2 +- src/elm/Page/Dashboard.elm | 3 +- src/elm/Page/Dashboard/Analysis.elm | 3 +- src/elm/Page/Dashboard/Claim.elm | 28 +++++++--- src/elm/Page/Profile/Claims.elm | 3 +- 9 files changed, 112 insertions(+), 54 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index bcd3f7aa7..77500bb6a 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -1,12 +1,29 @@ -module Action exposing (Action, Model, Msg(..), UpdateResultAction, init, jsAddressToMsg, msgToString, subscriptions, update, viewAction, viewClaimConfirmation, viewClaimWithProofs) +module Action exposing + ( Action + , Model + , Msg(..) + , UpdateResultAction + , init + , jsAddressToMsg + , msgToString + , selectionSet + , subscriptions + , update + , viewAction + , viewClaimConfirmation + , viewClaimWithProofs + ) import Api import Avatar import Cambiatus.Enum.VerificationType as VerificationType exposing (VerificationType) +import Cambiatus.Object +import Cambiatus.Object.Action as ActionObject import Cambiatus.Scalar exposing (DateTime) import Eos exposing (Symbol) import Eos.Account as Eos import File exposing (File) +import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) import Html exposing (Html, button, div, img, input, label, p, span, text) import Html.Attributes exposing (accept, class, classList, disabled, multiple, src, style, type_) import Html.Events exposing (onClick) @@ -56,7 +73,7 @@ type alias Action = { id : Int , description : String , reward : Float - , verificationReward : Float + , verifierReward : Float , creator : Eos.Name , validators : List Profile.Minimal , usages : Int @@ -940,3 +957,25 @@ generateVerificationCode actionId makerAccountUint64 proofTimeSeconds = ) |> sha256 |> String.slice 0 8 + + +selectionSet : SelectionSet Action Cambiatus.Object.Action +selectionSet = + SelectionSet.succeed Action + |> with ActionObject.id + |> with ActionObject.description + |> with ActionObject.reward + |> with ActionObject.verifierReward + |> with (Eos.nameSelectionSet ActionObject.creatorId) + |> with (ActionObject.validators Profile.minimalSelectionSet) + |> with ActionObject.usages + |> with ActionObject.usagesLeft + |> with ActionObject.deadline + |> with ActionObject.verificationType + --|> with (SelectionSet.map ActionObject.objective actionObjectiveIdSelectionSet) + |> with ActionObject.verifications + |> with ActionObject.isCompleted + |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofPhoto) + |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofCode) + |> with ActionObject.photoProofInstructions + |> with ActionObject.position diff --git a/src/elm/Claim.elm b/src/elm/Claim.elm index fafa5b637..db3e5808b 100644 --- a/src/elm/Claim.elm +++ b/src/elm/Claim.elm @@ -19,11 +19,11 @@ module Claim exposing , viewVoteClaimModal ) +import Action exposing (Action) import Api.Relay exposing (Edge, PageConnection) import Cambiatus.Enum.ClaimStatus as ClaimStatus import Cambiatus.Enum.VerificationType exposing (VerificationType(..)) import Cambiatus.Object -import Cambiatus.Object.Action as Action import Cambiatus.Object.Check as Check import Cambiatus.Object.Claim as Claim import Cambiatus.Object.ClaimConnection @@ -84,23 +84,22 @@ type alias Check = } -type alias Action = - { id : Int - , description : String - , reward : Float - , verifierReward : Float - , validators : List Profile.Minimal - , verifications : Int - , verificationType : VerificationType - , objective : Objective - , createdAt : DateTime - , hasProofPhoto : Bool - , hasProofCode : Bool - , instructions : Maybe String - } - - +--type alias ActionFromClaimModule = +-- { id : Int +-- , description : String +-- , reward : Float +-- , verifierReward : Float +-- , validators : List Profile.Minimal +-- , verifications : Int +-- , verificationType : VerificationType +-- , objective : Objective +-- , createdAt : DateTime +-- , hasProofPhoto : Bool +-- , hasProofCode : Bool +-- , instructions : Maybe String +-- } +-- -- Claim Action @@ -174,7 +173,7 @@ selectionSet = |> with Claim.id |> with (SelectionSet.map claimStatusMap Claim.status) |> with (Claim.claimer Profile.minimalSelectionSet) - |> with (Claim.action actionSelectionSet) + |> with (Claim.action Action.selectionSet) |> with (Claim.checks (\_ -> { input = Absent }) checkSelectionSet) |> with Claim.createdAt |> with (SelectionSet.map emptyStringToNothing Claim.proofPhoto) @@ -207,21 +206,22 @@ claimStatusMap v = Pending -actionSelectionSet : SelectionSet Action Cambiatus.Object.Action -actionSelectionSet = - SelectionSet.succeed Action - |> with Action.id - |> with Action.description - |> with Action.reward - |> with Action.verifierReward - |> with (Action.validators Profile.minimalSelectionSet) - |> with Action.verifications - |> with Action.verificationType - |> with (Action.objective Community.objectiveSelectionSet) - |> with Action.createdAt - |> with (SelectionSet.map (Maybe.withDefault False) Action.hasProofPhoto) - |> with (SelectionSet.map (Maybe.withDefault False) Action.hasProofCode) - |> with Action.photoProofInstructions + +--actionSelectionSetClaim : SelectionSet ActionFromClaimModule Cambiatus.Object.Action +--actionSelectionSetClaim = +-- SelectionSet.succeed ActionFromClaimModule +-- |> with ActionObject.id +-- |> with ActionObject.description +-- |> with ActionObject.reward +-- |> with ActionObject.verifierReward +-- |> with (ActionObject.validators Profile.minimalSelectionSet) +-- |> with ActionObject.verifications +-- |> with ActionObject.verificationType +-- |> with (ActionObject.objective Community.objectiveSelectionSet) +-- |> with ActionObject.createdAt +-- |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofPhoto) +-- |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofCode) +-- |> with ActionObject.photoProofInstructions checkSelectionSet : SelectionSet Check Cambiatus.Object.Check @@ -336,7 +336,8 @@ viewClaimCard { selectedCommunity, shared, accountName } claim = claimRoute = Route.Claim - claim.action.objective.id + --claim.action.objectiveId + 666 claim.action.id claim.id in diff --git a/src/elm/Community.elm b/src/elm/Community.elm index dfd20015b..698f00950 100755 --- a/src/elm/Community.elm +++ b/src/elm/Community.elm @@ -1,5 +1,5 @@ module Community exposing - ( Action + ( ActionFromCommunityModule , ActionVerification , ActionVerificationsResponse , Balance @@ -283,7 +283,7 @@ type alias Objective = { id : Int , description : String , creator : Eos.Name - , actions : List Action + , actions : List ActionFromCommunityModule , community : Metadata , isCompleted : Bool } @@ -336,11 +336,11 @@ encodeUpdateObjectiveAction c = -- ACTION -type alias Action = +type alias ActionFromCommunityModule = { id : Int , description : String , reward : Float - , verificationReward : Float + , verifierReward : Float , creator : Eos.Name , validators : List Profile.Minimal , usages : Int @@ -356,9 +356,9 @@ type alias Action = } -actionSelectionSet : SelectionSet Action Cambiatus.Object.Action +actionSelectionSet : SelectionSet ActionFromCommunityModule Cambiatus.Object.Action actionSelectionSet = - SelectionSet.succeed Action + SelectionSet.succeed ActionFromCommunityModule |> with ActionObject.id |> with ActionObject.description |> with ActionObject.reward @@ -369,6 +369,7 @@ actionSelectionSet = |> with ActionObject.usagesLeft |> with ActionObject.deadline |> with ActionObject.verificationType + --|> with (SelectionSet.map (\s -> s.id) (ActionObject.objective objectiveSelectionSet)) |> with ActionObject.verifications |> with ActionObject.isCompleted |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofPhoto) diff --git a/src/elm/Page/Community/ActionEditor.elm b/src/elm/Page/Community/ActionEditor.elm index db9e88ffb..e9dc8768b 100755 --- a/src/elm/Page/Community/ActionEditor.elm +++ b/src/elm/Page/Community/ActionEditor.elm @@ -220,7 +220,7 @@ editForm form action = verifierRewardValidator = defaultVerificationReward - |> updateInput (String.fromFloat action.verificationReward) + |> updateInput (String.fromFloat action.verifierReward) photoProof = case ( action.hasProofPhoto, action.hasProofCode ) of diff --git a/src/elm/Page/Community/Objectives.elm b/src/elm/Page/Community/Objectives.elm index 400ce81f5..0cbce1ff4 100644 --- a/src/elm/Page/Community/Objectives.elm +++ b/src/elm/Page/Community/Objectives.elm @@ -256,7 +256,7 @@ viewAction ({ shared } as loggedIn) model objectiveId action = [ p [ class "input-label" ] [ text_ "community.actions.validation_reward" ] , p [ class "uppercase text-body text-white" ] - [ String.fromFloat action.verificationReward + [ String.fromFloat action.verifierReward ++ " " ++ Eos.symbolToString model.communityId |> text diff --git a/src/elm/Page/Dashboard.elm b/src/elm/Page/Dashboard.elm index bbb1ebf5e..521759e5a 100755 --- a/src/elm/Page/Dashboard.elm +++ b/src/elm/Page/Dashboard.elm @@ -780,7 +780,8 @@ update msg model loggedIn = value = String.fromFloat claim.action.verifierReward ++ " " - ++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol + --++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol + ++ Eos.symbolToSymbolCodeString loggedIn.selectedCommunity cmd = case pageInfo of diff --git a/src/elm/Page/Dashboard/Analysis.elm b/src/elm/Page/Dashboard/Analysis.elm index bff10bb0d..f65058cda 100644 --- a/src/elm/Page/Dashboard/Analysis.elm +++ b/src/elm/Page/Dashboard/Analysis.elm @@ -416,7 +416,8 @@ update msg model loggedIn = value = String.fromFloat claim.action.verifierReward ++ " " - ++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol + --++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol + ++ Eos.symbolToSymbolCodeString loggedIn.selectedCommunity in { model | status = Loaded claims pageInfo diff --git a/src/elm/Page/Dashboard/Claim.elm b/src/elm/Page/Dashboard/Claim.elm index 538a687cd..3cc165fa2 100644 --- a/src/elm/Page/Dashboard/Claim.elm +++ b/src/elm/Page/Dashboard/Claim.elm @@ -94,7 +94,7 @@ view ({ shared } as loggedIn) model = , div [ class "mx-auto container px-4" ] [ viewTitle shared claim , viewProofs shared.translators claim - , viewDetails shared model claim + , viewDetails loggedIn model claim , viewVoters loggedIn claim ] , case model.claimModalStatus of @@ -235,8 +235,8 @@ viewTitle shared claim = ] -viewDetails : Shared -> Model -> Claim.Model -> Html msg -viewDetails shared model claim = +viewDetails : LoggedIn.Model -> Model -> Claim.Model -> Html msg +viewDetails { shared, selectedCommunity } model claim = let text_ s = text (shared.translators.t s) @@ -277,7 +277,13 @@ viewDetails shared model claim = [ class "pt-2 text-body" , classList [ ( "text-red line-through", isRejected ) ] ] - [ text (String.fromFloat claim.action.reward ++ " " ++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol) ] + [ text + (String.fromFloat claim.action.reward + ++ " " + --++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol + ++ Eos.symbolToSymbolCodeString selectedCommunity + ) + ] ] ] ] @@ -287,7 +293,8 @@ viewDetails shared model claim = [ text_ "claim.objective" ] , p [ class "pt-2 text-body" ] - [ text claim.action.objective.description ] + [ text "OBJECTIVE DESCIPTION1!!!!" --claim.action.objective.description + ] ] , div [ class "mb-8" ] [ p @@ -295,7 +302,13 @@ viewDetails shared model claim = [ text_ "claim.your_reward" ] , p [ class "pt-2 text-body" ] - [ text (String.fromFloat claim.action.verifierReward ++ " " ++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol) ] + [ text + (String.fromFloat claim.action.verifierReward + ++ " " + --++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol + ++ Eos.symbolToSymbolCodeString selectedCommunity + ) + ] ] ] @@ -461,7 +474,8 @@ update msg model loggedIn = value = String.fromFloat claim.action.verifierReward ++ " " - ++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol + --++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol + ++ Eos.symbolToSymbolCodeString loggedIn.selectedCommunity in { model | claimModalStatus = Claim.Closed diff --git a/src/elm/Page/Profile/Claims.elm b/src/elm/Page/Profile/Claims.elm index 62e222212..04892f647 100644 --- a/src/elm/Page/Profile/Claims.elm +++ b/src/elm/Page/Profile/Claims.elm @@ -258,7 +258,8 @@ update msg model loggedIn = value = String.fromFloat claim.action.verifierReward ++ " " - ++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol + --++ Eos.symbolToSymbolCodeString claim.action.objective.community.symbol + ++ Eos.symbolToSymbolCodeString loggedIn.selectedCommunity in { model | status = Loaded profile From b22f366763c4dd51a724cdf57a90d6e1f551610c Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 3 Feb 2021 10:42:46 +0300 Subject: [PATCH 32/91] Move viewAciton to the Comunity page module --- src/elm/Action.elm | 209 ---------------------------------- src/elm/Page/Community.elm | 225 +++++++++++++++++++++++++++++++++++-- 2 files changed, 216 insertions(+), 218 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 77500bb6a..688eed0a5 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -9,7 +9,6 @@ module Action exposing , selectionSet , subscriptions , update - , viewAction , viewClaimConfirmation , viewClaimWithProofs ) @@ -136,214 +135,6 @@ type ReasonToCloseProofSection | TimerExpired - --- VIEW ACTION - - -viewAction : Translators -> Bool -> Symbol -> Maybe Posix -> Action -> Html Msg -viewAction translators canEdit symbol maybeDate action = - let - { t, tr } = - translators - - text_ s = - text (t s) - - posixDeadline : Posix - posixDeadline = - action.deadline - |> Utils.posixDateTime - - deadlineStr : String - deadlineStr = - posixDeadline - |> Strftime.format "%d %B %Y" Time.utc - - pastDeadline : Bool - pastDeadline = - case action.deadline of - Just _ -> - case maybeDate of - Just today -> - posixToMillis today > posixToMillis posixDeadline - - Nothing -> - False - - Nothing -> - False - - rewardStrike : String - rewardStrike = - if pastDeadline || (action.usagesLeft < 1 && action.usages > 0) then - " line-through" - - else - "" - - dateColor : String - dateColor = - if pastDeadline then - " text-red" - - else - " text-indigo-500" - - usagesColor : String - usagesColor = - if action.usagesLeft >= 1 || action.usages == 0 then - " text-indigo-500" - - else - " text-red" - - ( claimColors, claimText ) = - if pastDeadline || (action.usagesLeft < 1 && action.usages > 0) then - ( " button-disabled", "dashboard.closed" ) - - else - ( " button button-primary", "dashboard.claim" ) - - claimSize = - if canEdit then - " w-4/5" - - else - " w-1/2" - - validatorAvatars = - List.take 3 action.validators - |> List.indexedMap - (\vIndex v -> - let - margin = - if vIndex /= 0 then - " -ml-5" - - else - "" - in - ("h-10 w-10 border-white border-4 rounded-full bg-white" ++ margin) - |> Avatar.view v.avatar - ) - |> (\vals -> - let - numValidators = - List.length action.validators - in - if numValidators > 3 then - vals - ++ [ div - [ class "h-10 w-10 flex flex-col border-white border-4 bg-grey rounded-full -ml-5" ] - [ p [ class "text-date-purple m-auto text-xs font-black leading-none tracking-wide" ] - [ text ("+" ++ String.fromInt (numValidators - 3)) ] - ] - ] - - else - vals - ) - - rewardStr = - String.fromFloat action.reward ++ " " ++ Eos.symbolToSymbolCodeString symbol - - ( usages, usagesLeft ) = - ( String.fromInt action.usages, String.fromInt action.usagesLeft ) - - validationType : String - validationType = - action.verificationType - |> VerificationType.toString - - isClosed = - pastDeadline - || (action.usages > 0 && action.usagesLeft == 0) - - viewClaimButton = - button - [ class ("h-10 uppercase rounded-lg ml-1" ++ claimColors ++ claimSize) - , onClick - (if isClosed then - NoOp - - else - OpenClaimConfirmation action - ) - ] - [ if action.hasProofPhoto then - span [ class "inline-block w-4 align-middle mr-2" ] [ Icons.camera "" ] - - else - text "" - , span [ class "inline-block align-middle" ] [ text_ claimText ] - ] - in - if action.isCompleted then - text "" - - else - div [ class "py-6 px-2" ] - [ div [ class "flex flex-col border-l-8 border-light-grey rounded-l-sm pl-2 sm:pl-6" ] - [ span [ class "text-text-grey text-sm sm:text-base" ] - [ text action.description ] - , div [ class "flex flex-col sm:flex-row sm:items-center sm:justify-between" ] - [ div [ class "text-xs mt-5 sm:w-1/3" ] - [ case action.deadline of - Just _ -> - div [] - [ span [ class "capitalize text-text-grey" ] [ text_ "community.actions.available_until" ] - , span [ class dateColor ] [ text deadlineStr ] - , span [] [ text_ "community.actions.or" ] - ] - - Nothing -> - text "" - , if action.usages > 0 then - p [ class usagesColor ] - [ text (tr "community.actions.usages" [ ( "usages", usages ), ( "usagesLeft", usagesLeft ) ]) ] - - else - text "" - ] - , div [ class "sm:self-end" ] - [ div [ class "mt-3 flex flex-row items-center" ] - (if validationType == "CLAIMABLE" then - validatorAvatars - - else - [ span [ class "text-date-purple uppercase text-sm mr-1" ] - [ text_ "community.actions.automatic_analyzers" ] - , img [ src "/icons/tooltip.svg" ] [] - ] - ) - , div [ class "capitalize text-text-grey text-sm sm:text-right" ] - [ text_ "community.actions.verifiers" ] - ] - ] - , div [ class "mt-5 flex flex-row items-baseline" ] - [ div [ class ("text-green text-base mt-5 flex-grow-1" ++ rewardStrike) ] - [ span [] [ text (t "community.actions.reward" ++ ": ") ] - , span [ class "font-medium" ] [ text rewardStr ] - ] - , div [ class "hidden sm:flex sm:visible flex-row justify-end flex-grow-1" ] - [ if validationType == "CLAIMABLE" then - viewClaimButton - - else - text "" - ] - ] - ] - , div [ class "flex flex-row mt-8 justify-between sm:hidden" ] - [ if validationType == "CLAIMABLE" then - viewClaimButton - - else - text "" - ] - ] - - viewClaimConfirmation : Translators -> ClaimConfirmationModalStatus -> Html Msg viewClaimConfirmation { t } claimConfirmationModalStatus = let diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index d99f8d30a..e927d4708 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -11,21 +11,26 @@ module Page.Community exposing import Action exposing (Action) import Api.Graphql +import Avatar import Browser exposing (UrlRequest(..)) +import Cambiatus.Enum.VerificationType as VerificationType import Community exposing (Model) import Eos exposing (Symbol) import Graphql.Http -import Html exposing (Html, button, div, img, p, text) +import Html exposing (Html, button, div, img, p, span, text) import Html.Attributes exposing (class, classList, id, src) import Html.Events exposing (onClick) +import Icons import Json.Encode exposing (Value) import Page import Route import Session.LoggedIn as LoggedIn exposing (External(..), FeedbackStatus(..), mapExternal) import Session.Shared exposing (Translators) +import Strftime import Task -import Time exposing (Posix) +import Time exposing (Posix, posixToMillis) import UpdateResult as UR +import Utils @@ -268,13 +273,11 @@ viewObjective loggedIn model metadata index objective = |> List.sortBy (\a -> a.position |> Maybe.withDefault 0) |> List.map (\action -> - Html.map GotActionMsg - (Action.viewAction loggedIn.shared.translators - (LoggedIn.isAccount metadata.creator loggedIn) - metadata.symbol - model.date - action - ) + viewAction loggedIn.shared.translators + (LoggedIn.isAccount metadata.creator loggedIn) + metadata.symbol + model.date + action ) in if objective.isCompleted then @@ -513,3 +516,207 @@ msgToString msg = ClickedCloseObjective -> [ "ClickedCloseObjective" ] + + +viewAction : Translators -> Bool -> Symbol -> Maybe Posix -> Action -> Html Msg +viewAction translators canEdit symbol maybeDate action = + let + { t, tr } = + translators + + text_ s = + text (t s) + + posixDeadline : Posix + posixDeadline = + action.deadline + |> Utils.posixDateTime + + deadlineStr : String + deadlineStr = + posixDeadline + |> Strftime.format "%d %B %Y" Time.utc + + pastDeadline : Bool + pastDeadline = + case action.deadline of + Just _ -> + case maybeDate of + Just today -> + posixToMillis today > posixToMillis posixDeadline + + Nothing -> + False + + Nothing -> + False + + rewardStrike : String + rewardStrike = + if pastDeadline || (action.usagesLeft < 1 && action.usages > 0) then + " line-through" + + else + "" + + dateColor : String + dateColor = + if pastDeadline then + " text-red" + + else + " text-indigo-500" + + usagesColor : String + usagesColor = + if action.usagesLeft >= 1 || action.usages == 0 then + " text-indigo-500" + + else + " text-red" + + ( claimColors, claimText ) = + if pastDeadline || (action.usagesLeft < 1 && action.usages > 0) then + ( " button-disabled", "dashboard.closed" ) + + else + ( " button button-primary", "dashboard.claim" ) + + claimSize = + if canEdit then + " w-4/5" + + else + " w-1/2" + + validatorAvatars = + List.take 3 action.validators + |> List.indexedMap + (\vIndex v -> + let + margin = + if vIndex /= 0 then + " -ml-5" + + else + "" + in + ("h-10 w-10 border-white border-4 rounded-full bg-white" ++ margin) + |> Avatar.view v.avatar + ) + |> (\vals -> + let + numValidators = + List.length action.validators + in + if numValidators > 3 then + vals + ++ [ div + [ class "h-10 w-10 flex flex-col border-white border-4 bg-grey rounded-full -ml-5" ] + [ p [ class "text-date-purple m-auto text-xs font-black leading-none tracking-wide" ] + [ text ("+" ++ String.fromInt (numValidators - 3)) ] + ] + ] + + else + vals + ) + + rewardStr = + String.fromFloat action.reward ++ " " ++ Eos.symbolToSymbolCodeString symbol + + ( usages, usagesLeft ) = + ( String.fromInt action.usages, String.fromInt action.usagesLeft ) + + validationType : String + validationType = + action.verificationType + |> VerificationType.toString + + isClosed = + pastDeadline + || (action.usages > 0 && action.usagesLeft == 0) + + viewClaimButton = + button + [ class ("h-10 uppercase rounded-lg ml-1" ++ claimColors ++ claimSize) + , onClick + (if isClosed then + NoOp + + else + (GotActionMsg << Action.OpenClaimConfirmation) action + ) + ] + [ if action.hasProofPhoto then + span [ class "inline-block w-4 align-middle mr-2" ] [ Icons.camera "" ] + + else + text "" + , span [ class "inline-block align-middle" ] [ text_ claimText ] + ] + in + if action.isCompleted then + text "" + + else + div [ class "py-6 px-2" ] + [ div [ class "flex flex-col border-l-8 border-light-grey rounded-l-sm pl-2 sm:pl-6" ] + [ span [ class "text-text-grey text-sm sm:text-base" ] + [ text action.description ] + , div [ class "flex flex-col sm:flex-row sm:items-center sm:justify-between" ] + [ div [ class "text-xs mt-5 sm:w-1/3" ] + [ case action.deadline of + Just _ -> + div [] + [ span [ class "capitalize text-text-grey" ] [ text_ "community.actions.available_until" ] + , span [ class dateColor ] [ text deadlineStr ] + , span [] [ text_ "community.actions.or" ] + ] + + Nothing -> + text "" + , if action.usages > 0 then + p [ class usagesColor ] + [ text (tr "community.actions.usages" [ ( "usages", usages ), ( "usagesLeft", usagesLeft ) ]) ] + + else + text "" + ] + , div [ class "sm:self-end" ] + [ div [ class "mt-3 flex flex-row items-center" ] + (if validationType == "CLAIMABLE" then + validatorAvatars + + else + [ span [ class "text-date-purple uppercase text-sm mr-1" ] + [ text_ "community.actions.automatic_analyzers" ] + , img [ src "/icons/tooltip.svg" ] [] + ] + ) + , div [ class "capitalize text-text-grey text-sm sm:text-right" ] + [ text_ "community.actions.verifiers" ] + ] + ] + , div [ class "mt-5 flex flex-row items-baseline" ] + [ div [ class ("text-green text-base mt-5 flex-grow-1" ++ rewardStrike) ] + [ span [] [ text (t "community.actions.reward" ++ ": ") ] + , span [ class "font-medium" ] [ text rewardStr ] + ] + , div [ class "hidden sm:flex sm:visible flex-row justify-end flex-grow-1" ] + [ if validationType == "CLAIMABLE" then + viewClaimButton + + else + text "" + ] + ] + ] + , div [ class "flex flex-row mt-8 justify-between sm:hidden" ] + [ if validationType == "CLAIMABLE" then + viewClaimButton + + else + text "" + ] + ] From 24a3f581ebdb2b5321f7ba3259c61af44d6239f5 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 3 Feb 2021 12:55:53 +0300 Subject: [PATCH 33/91] Add page for claim with photo --- src/elm/Action.elm | 497 +----------------- src/elm/Claim.elm | 6 +- src/elm/Main.elm | 14 + src/elm/Page/Community.elm | 159 ++---- src/elm/Page/Community/ClaimWithPhoto.elm | 591 ++++++++++++++++++++++ src/elm/Route.elm | 7 + src/elm/Session/LoggedIn.elm | 1 + 7 files changed, 675 insertions(+), 600 deletions(-) create mode 100644 src/elm/Page/Community/ClaimWithPhoto.elm diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 688eed0a5..93e760156 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -1,45 +1,34 @@ module Action exposing ( Action + , ClaimConfirmationModalStatus(..) , Model , Msg(..) - , UpdateResultAction + , encodeClaimAction , init , jsAddressToMsg , msgToString , selectionSet - , subscriptions , update , viewClaimConfirmation - , viewClaimWithProofs ) -import Api -import Avatar -import Cambiatus.Enum.VerificationType as VerificationType exposing (VerificationType) +import Cambiatus.Enum.VerificationType exposing (VerificationType) import Cambiatus.Object import Cambiatus.Object.Action as ActionObject import Cambiatus.Scalar exposing (DateTime) import Eos exposing (Symbol) import Eos.Account as Eos -import File exposing (File) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, button, div, img, input, label, p, span, text) -import Html.Attributes exposing (accept, class, classList, disabled, multiple, src, style, type_) +import Html exposing (Html, button, div, text) +import Html.Attributes exposing (class, classList, disabled) import Html.Events exposing (onClick) -import Http -import Icons import Json.Decode as Decode import Json.Encode as Encode exposing (Value) -import Page import Profile import Session.LoggedIn as LoggedIn exposing (External(..)) import Session.Shared exposing (Translators) import Sha256 exposing (sha256) -import Strftime -import Task -import Time exposing (Posix, posixToMillis) import UpdateResult as UR -import Utils import View.Modal as Modal @@ -49,25 +38,6 @@ type ClaimConfirmationModalStatus | Closed -type Proof - = Proof ProofPhotoStatus (Maybe ProofCode) - - -type alias ProofCode = - { code : Maybe String - , claimTimestamp : Int - , secondsAfterClaim : Int - , availabilityPeriod : Int - } - - -type ProofPhotoStatus - = NoPhotoAdded - | Uploading - | UploadFailed Http.Error - | Uploaded String - - type alias Action = { id : Int , description : String @@ -100,7 +70,6 @@ type alias ClaimedAction = type alias Model = { claimConfirmationModalStatus : ClaimConfirmationModalStatus , action : Action - , proof : Maybe Proof } @@ -108,31 +77,15 @@ init : Action -> Model init action = { claimConfirmationModalStatus = Closed , action = action - , proof = Nothing -- TODO: Should it be Nothing? } type Msg = NoOp - -- Action | OpenClaimConfirmation Action | CloseClaimConfirmation | ClaimAction Action | GotClaimActionResponse (Result Value String) - -- Proofs - | OpenProofSection Action - | CloseProofSection ReasonToCloseProofSection - | GotProofTime Int Posix - | GetUint64Name String - | GotUint64Name (Result Value String) - | Tick Time.Posix - | EnteredPhoto (List File) - | CompletedPhotoUpload (Result Http.Error String) - - -type ReasonToCloseProofSection - = CancelClicked - | TimerExpired viewClaimConfirmation : Translators -> ClaimConfirmationModalStatus -> Html Msg @@ -186,7 +139,9 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = let acceptMsg = if action.hasProofPhoto then - OpenProofSection action + --OpenProofSection action + -- TODO: Go to the Claim with Photo page + NoOp else ClaimAction action @@ -200,181 +155,11 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = text "" -viewPhotoUploader : Translators -> ProofPhotoStatus -> Html Msg -viewPhotoUploader { t } proofPhotoStatus = - let - uploadedAttrs = - case proofPhotoStatus of - Uploaded url -> - [ class " bg-no-repeat bg-center bg-cover" - , style "background-image" ("url(" ++ url ++ ")") - ] - - _ -> - [] - in - label - (class "relative bg-purple-500 w-full md:w-2/3 h-56 rounded-sm flex justify-center items-center cursor-pointer" - :: uploadedAttrs - ) - [ input - [ class "hidden-img-input" - , type_ "file" - , accept "image/*" - , Page.onFileChange EnteredPhoto - , multiple False - ] - [] - , div [] - [ case proofPhotoStatus of - Uploading -> - div [ class "spinner spinner-light" ] [] - - Uploaded _ -> - span [ class "absolute bottom-0 right-0 mr-4 mb-4 bg-orange-300 w-8 h-8 p-2 rounded-full" ] - [ Icons.camera "" ] - - _ -> - div [ class "text-white text-body font-bold text-center" ] - [ div [ class "w-10 mx-auto mb-2" ] [ Icons.camera "" ] - , div [] [ text (t "community.actions.proof.upload_photo_hint") ] - ] - ] - ] - - -viewClaimWithProofs : Maybe Proof -> Translators -> Action -> Html Msg -viewClaimWithProofs proofs translators action = - let - { t } = - translators - - isUploadingInProgress = - case proofs of - Just (Proof Uploading _) -> - True - - _ -> - False - in - div [ class "bg-white border-t border-gray-300" ] - [ div [ class "container p-4 mx-auto" ] - [ div [ class "heading-bold leading-7 font-bold" ] [ text <| t "community.actions.proof.title" ] - , p [ class "mb-4" ] - [ text <| - Maybe.withDefault "" action.photoProofInstructions - ] - , case proofs of - Just (Proof _ (Just { code, secondsAfterClaim, availabilityPeriod })) -> - case code of - Just c -> - viewProofCode - translators - c - secondsAfterClaim - availabilityPeriod - - _ -> - text "" - - _ -> - text "" - , div [ class "mb-4" ] - [ span [ class "input-label block mb-2" ] - [ text (t "community.actions.proof.photo") ] - , case proofs of - Just (Proof photoStatus _) -> - viewPhotoUploader translators photoStatus - - _ -> - text "" - ] - , div [ class "md:flex" ] - [ button - [ class "modal-cancel" - , onClick - (if isUploadingInProgress then - NoOp - - else - CloseProofSection CancelClicked - ) - , classList [ ( "button-disabled", isUploadingInProgress ) ] - , disabled isUploadingInProgress - ] - [ text (t "menu.cancel") ] - , button - [ class "modal-accept" - , classList [ ( "button-disabled", isUploadingInProgress ) ] - , onClick - (if isUploadingInProgress then - NoOp - - else - ClaimAction action - ) - , disabled isUploadingInProgress - ] - [ text (t "menu.send") ] - ] - ] - ] - - -viewProofCode : Translators -> String -> Int -> Int -> Html msg -viewProofCode { t } proofCode secondsAfterClaim proofCodeValiditySeconds = - let - remainingSeconds = - proofCodeValiditySeconds - secondsAfterClaim - - timerMinutes = - remainingSeconds // 60 - - timerSeconds = - remainingSeconds - (timerMinutes * 60) - - toString timeVal = - if timeVal < 10 then - "0" ++ String.fromInt timeVal - - else - String.fromInt timeVal - - timer = - toString timerMinutes ++ ":" ++ toString timerSeconds - in - div [ class "mb-4" ] - [ span [ class "input-label block mb-1" ] - [ text (t "community.actions.form.verification_code") ] - , div [ class "text-2xl text-black font-bold inline-block align-middle mr-2" ] - [ text proofCode ] - , span [ class "whitespace-no-wrap text-body rounded-full bg-lightred px-3 py-1 text-white" ] - [ text (t "community.actions.proof.code_period_label") - , text " " - , text timer - ] - ] - - -subscriptions : Model -> Sub Msg -subscriptions model = - case model.proof of - Just (Proof _ (Just _)) -> - Time.every 1000 Tick - - _ -> - Sub.none - - -- UPDATE -type alias UpdateResultAction = - UR.UpdateResult Model Msg (External Msg) - - -update : LoggedIn.Model -> Msg -> Model -> UpdateResultAction +update : LoggedIn.Model -> Msg -> Model -> UR.UpdateResult Model Msg (External Msg) update ({ shared } as loggedIn) msg model = let { t } = @@ -385,147 +170,16 @@ update ({ shared } as loggedIn) msg model = { model | claimConfirmationModalStatus = Open action } |> UR.init - OpenProofSection action -> - let - runProofCodeTimer = - Task.perform (GotProofTime action.id) Time.now - in - if action.hasProofPhoto then - { model - | claimConfirmationModalStatus = Closed - , proof = Just (Proof NoPhotoAdded Nothing) - } - |> UR.init - |> UR.addCmd - (if action.hasProofCode then - runProofCodeTimer - - else - Cmd.none - ) - |> UR.addPort - { responseAddress = NoOp - , responseData = Encode.null - , data = - Encode.object - [ ( "id", Encode.string "communityPage" ) - , ( "name", Encode.string "scrollIntoView" ) - ] - } - - else - model |> UR.init - - EnteredPhoto (file :: _) -> - let - uploadImage = - Api.uploadImage shared file CompletedPhotoUpload - - newProof = - case model.proof of - Just (Proof _ proofCode) -> - Just (Proof Uploading proofCode) - - _ -> - Nothing - in - { model - | proof = newProof - } - |> UR.init - |> UR.addCmd uploadImage - |> UR.addExt HideFeedback - - EnteredPhoto [] -> - model - |> UR.init - - CompletedPhotoUpload (Ok url) -> - let - newProofs = - case model.proof of - Just (Proof _ proofCode) -> - Just (Proof (Uploaded url) proofCode) - - _ -> - Nothing - in - { model | proof = newProofs } - |> UR.init - - CompletedPhotoUpload (Err error) -> - let - newProofs = - case model.proof of - Just (Proof _ proofCode) -> - Just (Proof (UploadFailed error) proofCode) - - _ -> - Nothing - in - { model | proof = newProofs } - |> UR.init - |> UR.logHttpError msg error - CloseClaimConfirmation -> { model | claimConfirmationModalStatus = Closed } |> UR.init - CloseProofSection reason -> - { model - | claimConfirmationModalStatus = Closed - , proof = Nothing - } - |> UR.init - |> UR.addExt - (case reason of - TimerExpired -> - ShowFeedback LoggedIn.Failure (t "community.actions.proof.time_expired") - - CancelClicked -> - HideFeedback - ) - ClaimAction action -> let - hasPhotoError = - case model.proof of - Just (Proof (Uploaded _) _) -> - False - - Just (Proof _ _) -> - -- Error: photo wasn't uploaded while claiming with proof - True - - Nothing -> - False - newModel = - case model.proof of - Just (Proof _ _) -> - -- Claim with proof has no confirmation - model - - Nothing -> - { model | claimConfirmationModalStatus = InProgress } - - ( proofPhotoUrl, proofCode_, proofTime ) = - case model.proof of - Just (Proof (Uploaded url) (Just { code, claimTimestamp })) -> - ( url, Maybe.withDefault "" code, claimTimestamp ) - - Just (Proof (Uploaded url) Nothing) -> - ( url, "", 0 ) - - _ -> - ( "", "", 0 ) + { model | claimConfirmationModalStatus = InProgress } in - if hasPhotoError then - model - |> UR.init - |> UR.addExt (ShowFeedback LoggedIn.Failure (t "community.actions.proof.no_photo_error")) - - else if LoggedIn.isAuth loggedIn then + if LoggedIn.isAuth loggedIn then newModel |> UR.init |> UR.addPort @@ -542,9 +196,9 @@ update ({ shared } as loggedIn) msg model = , data = { actionId = action.id , maker = loggedIn.accountName - , proofPhoto = proofPhotoUrl - , proofCode = proofCode_ - , proofTime = proofTime + , proofPhoto = "" + , proofCode = "" + , proofTime = 0 } |> encodeClaimAction } @@ -564,7 +218,6 @@ update ({ shared } as loggedIn) msg model = in { model | claimConfirmationModalStatus = Closed - , proof = Nothing } |> UR.init |> UR.addExt (ShowFeedback LoggedIn.Success message) @@ -576,83 +229,6 @@ update ({ shared } as loggedIn) msg model = |> UR.init |> UR.addExt (ShowFeedback LoggedIn.Failure (t "dashboard.check_claim.failure")) - GetUint64Name _ -> - model |> UR.init - - GotUint64Name (Ok uint64name) -> - case ( model.proof, model.action.id ) of - ( Just (Proof proofPhoto (Just proofCode)), actionId ) -> - let - verificationCode = - generateVerificationCode actionId uint64name proofCode.claimTimestamp - - newProofCode = - Just - { proofCode - | code = Just verificationCode - } - in - { model | proof = Just (Proof proofPhoto newProofCode) } - |> UR.init - - _ -> - model - |> UR.init - - GotUint64Name (Err _) -> - model |> UR.init - - Tick timer -> - case model.proof of - Just (Proof proofPhoto (Just proofCode)) -> - let - secondsAfterClaim = - (Time.posixToMillis timer // 1000) - proofCode.claimTimestamp - - isProofCodeActive = - (proofCode.availabilityPeriod - secondsAfterClaim) > 0 - in - if isProofCodeActive then - let - newProofCode = - Just - { proofCode - | secondsAfterClaim = secondsAfterClaim - } - in - { model | proof = Just (Proof proofPhoto newProofCode) } |> UR.init - - else - update loggedIn (CloseProofSection TimerExpired) model - - _ -> - model |> UR.init - - GotProofTime actionId posix -> - let - initProofCodeParts = - Just - { code = Nothing - , claimTimestamp = Time.posixToMillis posix // 1000 - , secondsAfterClaim = 0 - , availabilityPeriod = 30 * 60 - } - in - { model - | --actionId = Just actionId - proof = Just (Proof NoPhotoAdded initProofCodeParts) - } - |> UR.init - |> UR.addPort - { responseAddress = GetUint64Name (Eos.nameToString loggedIn.accountName) - , responseData = Encode.null - , data = - Encode.object - [ ( "name", Encode.string "accountNameToUint64" ) - , ( "accountName", Encode.string (Eos.nameToString loggedIn.accountName) ) - ] - } - NoOp -> model |> UR.init @@ -671,17 +247,6 @@ jsAddressToMsg addr val = |> Result.map (Just << GotClaimActionResponse) |> Result.withDefault Nothing - "GetUint64Name" :: [] -> - Decode.decodeValue - (Decode.oneOf - [ Decode.field "uint64name" Decode.string |> Decode.map Ok - , Decode.succeed (Err val) - ] - ) - val - |> Result.map (Just << GotUint64Name) - |> Result.withDefault Nothing - _ -> Nothing @@ -692,24 +257,6 @@ msgToString msg = NoOp -> [ "NoOp" ] - Tick _ -> - [ "Tick" ] - - GotProofTime _ _ -> - [ "GotProofTime" ] - - OpenProofSection _ -> - [ "OpenProofSection" ] - - CloseProofSection _ -> - [ "CloseProofSection" ] - - EnteredPhoto _ -> - [ "EnteredPhoto" ] - - CompletedPhotoUpload r -> - [ "CompletedPhotoUpload", UR.resultToString r ] - OpenClaimConfirmation _ -> [ "OpenClaimConfirmation" ] @@ -719,12 +266,6 @@ msgToString msg = ClaimAction _ -> [ "ClaimAction" ] - GetUint64Name _ -> - [ "GetUint64Name" ] - - GotUint64Name n -> - [ "GotUint64Name", UR.resultToString n ] - GotClaimActionResponse r -> [ "GotClaimActionResponse", UR.resultToString r ] @@ -740,16 +281,6 @@ encodeClaimAction c = ] -generateVerificationCode : Int -> String -> Int -> String -generateVerificationCode actionId makerAccountUint64 proofTimeSeconds = - (String.fromInt actionId - ++ makerAccountUint64 - ++ String.fromInt proofTimeSeconds - ) - |> sha256 - |> String.slice 0 8 - - selectionSet : SelectionSet Action Cambiatus.Object.Action selectionSet = SelectionSet.succeed Action diff --git a/src/elm/Claim.elm b/src/elm/Claim.elm index db3e5808b..dd98d7e0b 100644 --- a/src/elm/Claim.elm +++ b/src/elm/Claim.elm @@ -337,13 +337,15 @@ viewClaimCard { selectedCommunity, shared, accountName } claim = claimRoute = Route.Claim --claim.action.objectiveId - 666 + -- the route just works without objectiveId even with direct loading from browser's address bar + 0 claim.action.id claim.id + |> Debug.log "route to claim" in div [ class "w-full sm:w-1/2 lg:w-1/3 xl:w-1/4 px-2" ] [ div - [ class "flex flex-col p-4 my-2 rounded-lg bg-white hover:shadow cursor-pointer" + [ class "flex flex-col p-4 my-2 rounded-lg bg-white hover:shadow cursor-pointer bg-red" , id ("claim" ++ String.fromInt claim.id) -- We can't just use `a` with `href` here because there are other `a`-nodes inside. diff --git a/src/elm/Main.elm b/src/elm/Main.elm index 53e488b21..b3ecbdc11 100755 --- a/src/elm/Main.elm +++ b/src/elm/Main.elm @@ -11,6 +11,7 @@ import Page exposing (Session) import Page.ComingSoon as ComingSoon import Page.Community as CommunityPage import Page.Community.ActionEditor as ActionEditor +import Page.Community.ClaimWithPhoto as ClaimWithPhoto import Page.Community.Editor as CommunityEditor import Page.Community.Invite as Invite import Page.Community.ObjectiveEditor as ObjectiveEditor @@ -154,6 +155,7 @@ type Status | ObjectiveEditor ObjectiveEditor.Model | ActionEditor ActionEditor.Model | Claim Claim.Model + | ClaimAction ClaimWithPhoto.Model | Notification Notification.Model | Dashboard Dashboard.Model | Login Login.Model @@ -189,6 +191,7 @@ type Msg | GotCommunitySettingsFeaturesMsg CommunitySettingsFeatures.Msg | GotObjectivesMsg Objectives.Msg | GotActionEditorMsg ActionEditor.Msg + | GotClaimActionMsg ClaimWithPhoto.Msg | GotObjectiveEditorMsg ObjectiveEditor.Msg | GotVerifyClaimMsg Claim.Msg | GotDashboardMsg Dashboard.Msg @@ -817,6 +820,11 @@ changeRouteTo maybeRoute model = >> updateStatusWith ActionEditor GotActionEditorMsg model |> withLoggedIn (Route.EditAction symbol objectiveId actionId) + Just (Route.ClaimAction symbol objectiveId actionId) -> + (\l -> ClaimWithPhoto.init l symbol objectiveId (Just actionId)) + >> updateStatusWith ClaimAction GotClaimActionMsg model + |> withLoggedIn (Route.ClaimAction symbol objectiveId actionId) + Just (Route.Claim objectiveId actionId claimId) -> (\l -> Claim.init l claimId) >> updateStatusWith Claim GotVerifyClaimMsg model @@ -980,6 +988,9 @@ msgToString msg = GotActionEditorMsg subMsg -> "GotActionEditor" :: ActionEditor.msgToString subMsg + GotClaimActionMsg subMsg -> + "GotClaimActionMsg" :: ClaimWithPhoto.msgToString subMsg + GotVerifyClaimMsg subMsg -> "GotVerifyClaimMsg" :: Claim.msgToString subMsg @@ -1170,6 +1181,9 @@ view model = ActionEditor subModel -> viewLoggedIn subModel LoggedIn.ActionEditor GotActionEditorMsg ActionEditor.view + ClaimAction subModel -> + viewLoggedIn subModel LoggedIn.ClaimAction GotClaimActionMsg ClaimWithPhoto.view + Claim subModel -> viewLoggedIn subModel LoggedIn.Claim GotVerifyClaimMsg Claim.view diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index e927d4708..cf32f897b 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -12,7 +12,6 @@ module Page.Community exposing import Action exposing (Action) import Api.Graphql import Avatar -import Browser exposing (UrlRequest(..)) import Cambiatus.Enum.VerificationType as VerificationType import Community exposing (Model) import Eos exposing (Symbol) @@ -61,13 +60,8 @@ initModel _ _ = subscriptions : Model -> Sub Msg -subscriptions model = - case model.claimingAction of - Just ca -> - Sub.map GotActionMsg (Action.subscriptions ca) - - Nothing -> - Sub.none +subscriptions _ = + Sub.none @@ -84,14 +78,13 @@ type alias Model = type PageStatus = Loading - | Loaded Community.Model ActiveSection + | Loaded Community.Model | NotFound | Failed (Graphql.Http.Error (Maybe Community.Model)) type ActiveSection = ObjectivesAndActions - | ClaimWithProofs Action @@ -109,7 +102,7 @@ view loggedIn model = title = case model.pageStatus of - Loaded community _ -> + Loaded community -> community.title Loading -> @@ -129,57 +122,46 @@ view loggedIn model = Failed e -> Page.fullPageGraphQLError (t "community.objectives.title") e - Loaded community pageStatus -> - case pageStatus of - ObjectivesAndActions -> - div [] - [ Page.viewHeader loggedIn community.title Route.Dashboard - , div [ class "bg-white p-4" ] - [ div [ class "container mx-auto px-4" ] - [ div [ class "h-24 w-24 rounded-full mx-auto" ] - [ img [ src community.logo, class "max-h-full m-auto object-scale-down" ] [] - ] - , div [ class "flex flex-wrap w-full items-center" ] - [ p [ class "text-4xl font-bold" ] - [ text community.title ] - ] - , p [ class "text-grey-200 text-sm" ] [ text community.description ] - ] + Loaded community -> + div [] + [ Page.viewHeader loggedIn community.title Route.Dashboard + , div [ class "bg-white p-4" ] + [ div [ class "container mx-auto px-4" ] + [ div [ class "h-24 w-24 rounded-full mx-auto" ] + [ img [ src community.logo, class "max-h-full m-auto object-scale-down" ] [] ] - , div [ class "container mx-auto" ] - [ if community.hasObjectives then - div [ class "px-4 pb-4" ] - [ Html.map GotActionMsg - (case model.claimingAction of - Just ca -> - Action.viewClaimConfirmation - loggedIn.shared.translators - ca.claimConfirmationModalStatus - - Nothing -> - text "" - ) - , div [ class "container bg-white py-6 sm:py-8 px-3 sm:px-6 rounded-lg mt-4" ] - (Page.viewTitle (t "community.objectives.title_plural") - :: List.indexedMap (viewObjective loggedIn model community) - community.objectives - ) - ] - - else - text "" - , viewCommunityStats loggedIn.shared.translators community + , div [ class "flex flex-wrap w-full items-center" ] + [ p [ class "text-4xl font-bold" ] + [ text community.title ] ] + , p [ class "text-grey-200 text-sm" ] [ text community.description ] ] + ] + , div [ class "container mx-auto" ] + [ if community.hasObjectives then + div [ class "px-4 pb-4" ] + [ Html.map GotActionMsg + (case model.claimingAction of + Just ca -> + Action.viewClaimConfirmation + loggedIn.shared.translators + ca.claimConfirmationModalStatus + + Nothing -> + text "" + ) + , div [ class "container bg-white py-6 sm:py-8 px-3 sm:px-6 rounded-lg mt-4" ] + (Page.viewTitle (t "community.objectives.title_plural") + :: List.indexedMap (viewObjective loggedIn model community) + community.objectives + ) + ] - ClaimWithProofs action -> - case model.claimingAction of - Just ca -> - Html.map GotActionMsg - (Action.viewClaimWithProofs ca.proof loggedIn.shared.translators action) - - Nothing -> - text "" + else + text "" + , viewCommunityStats loggedIn.shared.translators community + ] + ] in { title = title , content = @@ -381,9 +363,10 @@ update msg model ({ shared } as loggedIn) = updatePageStatus m = { m | pageStatus = + -- TODO: Do we really need this update? case model.pageStatus of - Loaded community (ClaimWithProofs _) -> - Loaded community ObjectivesAndActions + Loaded community -> + Loaded community _ -> m.pageStatus @@ -396,60 +379,6 @@ update msg model ({ shared } as loggedIn) = model |> UR.init - Action.OpenProofSection action -> - case model.claimingAction of - Just ca -> - if ca.action.hasProofPhoto then - let - updatePageStatus m = - { m - | pageStatus = - case model.pageStatus of - Loaded community _ -> - Loaded community (ClaimWithProofs action) - - _ -> - model.pageStatus - } - in - updateClaimingAction ca - |> UR.mapModel updatePageStatus - - else - model - |> UR.init - - Nothing -> - model - |> UR.init - - Action.CloseProofSection _ -> - case model.claimingAction of - Just ca -> - if ca.action.hasProofPhoto then - let - updatePageStatus m = - { m - | pageStatus = - case model.pageStatus of - Loaded community (ClaimWithProofs _) -> - Loaded community ObjectivesAndActions - - _ -> - model.pageStatus - } - in - updateClaimingAction ca - |> UR.mapModel updatePageStatus - - else - model - |> UR.init - - Nothing -> - model - |> UR.init - _ -> case model.claimingAction of Just ca -> @@ -463,7 +392,7 @@ update msg model ({ shared } as loggedIn) = case community of Just c -> { model - | pageStatus = Loaded c ObjectivesAndActions + | pageStatus = Loaded c } |> UR.init diff --git a/src/elm/Page/Community/ClaimWithPhoto.elm b/src/elm/Page/Community/ClaimWithPhoto.elm new file mode 100644 index 000000000..1a51bbb63 --- /dev/null +++ b/src/elm/Page/Community/ClaimWithPhoto.elm @@ -0,0 +1,591 @@ +module Page.Community.ClaimWithPhoto exposing (..) + +import Action exposing (Action, ClaimConfirmationModalStatus(..)) +import Api +import Eos exposing (Symbol) +import Eos.Account as Eos +import File exposing (File) +import Html exposing (Html, button, div, input, label, p, span, text) +import Html.Attributes exposing (accept, class, classList, disabled, multiple, style, type_) +import Html.Events exposing (onClick) +import Http +import Icons +import Json.Decode as Decode +import Json.Encode as Encode exposing (Value) +import Page +import Session.LoggedIn as LoggedIn exposing (External(..)) +import Session.Shared exposing (Translators) +import Sha256 exposing (sha256) +import Task +import Time exposing (Posix) +import UpdateResult as UR + + +type alias Model = + { claimConfirmationModalStatus : Action.ClaimConfirmationModalStatus + , action : Maybe Action -- TODO: Use result since action must be loaded + , proof : Proof + } + + +type alias ObjectiveId = + Int + + +type alias ActionId = + Int + + +init : LoggedIn.Model -> Symbol -> ObjectiveId -> Maybe ActionId -> ( Model, Cmd Msg ) +init loggedIn symbol objectiveId actionId = + ( { claimConfirmationModalStatus = Action.Closed + , action = Nothing + , proof = Proof NoPhotoAdded Nothing + } + , Cmd.none + ) + + +type Msg + = NoOp + | OpenProofSection Action + | CloseProofSection ReasonToCloseProofSection + | GotProofTime Int Posix + | GetUint64Name String + | GotUint64Name (Result Value String) + | Tick Time.Posix + | EnteredPhoto (List File) + | CompletedPhotoUpload (Result Http.Error String) + | ClaimAction Action + | GotClaimActionResponse (Result Value String) + + +type Proof + = Proof ProofPhotoStatus (Maybe ProofCode) + + +type alias ProofCode = + { code : Maybe String + , claimTimestamp : Int + , secondsAfterClaim : Int + , availabilityPeriod : Int + } + + +type ProofPhotoStatus + = NoPhotoAdded + | Uploading + | UploadFailed Http.Error + | Uploaded String + + +type ReasonToCloseProofSection + = CancelClicked + | TimerExpired + + +viewPhotoUploader : Translators -> ProofPhotoStatus -> Html Msg +viewPhotoUploader { t } proofPhotoStatus = + let + uploadedAttrs = + case proofPhotoStatus of + Uploaded url -> + [ class " bg-no-repeat bg-center bg-cover" + , style "background-image" ("url(" ++ url ++ ")") + ] + + _ -> + [] + in + label + (class "relative bg-purple-500 w-full md:w-2/3 h-56 rounded-sm flex justify-center items-center cursor-pointer" + :: uploadedAttrs + ) + [ input + [ class "hidden-img-input" + , type_ "file" + , accept "image/*" + , Page.onFileChange EnteredPhoto + , multiple False + ] + [] + , div [] + [ case proofPhotoStatus of + Uploading -> + div [ class "spinner spinner-light" ] [] + + Uploaded _ -> + span [ class "absolute bottom-0 right-0 mr-4 mb-4 bg-orange-300 w-8 h-8 p-2 rounded-full" ] + [ Icons.camera "" ] + + _ -> + div [ class "text-white text-body font-bold text-center" ] + [ div [ class "w-10 mx-auto mb-2" ] [ Icons.camera "" ] + , div [] [ text (t "community.actions.proof.upload_photo_hint") ] + ] + ] + ] + + +view : LoggedIn.Model -> Model -> { title : String, content : Html Msg } +view loggedIn model = + { title = "Claim with photo" + , content = + case model.action of + Just a -> + viewClaimWithProofs model.proof loggedIn.shared.translators a + + Nothing -> + text "this is claim with photo page" + } + + +viewClaimWithProofs : Proof -> Translators -> Action -> Html Msg +viewClaimWithProofs proofs translators action = + let + { t } = + translators + + isUploadingInProgress = + case proofs of + Proof Uploading _ -> + True + + _ -> + False + in + div [ class "bg-white border-t border-gray-300" ] + [ div [ class "container p-4 mx-auto" ] + [ div [ class "heading-bold leading-7 font-bold" ] [ text <| t "community.actions.proof.title" ] + , p [ class "mb-4" ] + [ text <| + Maybe.withDefault "" action.photoProofInstructions + ] + , case proofs of + Proof _ (Just { code, secondsAfterClaim, availabilityPeriod }) -> + case code of + Just c -> + viewProofCode + translators + c + secondsAfterClaim + availabilityPeriod + + _ -> + text "" + + _ -> + text "" + , div [ class "mb-4" ] + [ span [ class "input-label block mb-2" ] + [ text (t "community.actions.proof.photo") ] + , case proofs of + Proof photoStatus _ -> + viewPhotoUploader translators photoStatus + ] + , div [ class "md:flex" ] + [ button + [ class "modal-cancel" + , onClick + (if isUploadingInProgress then + NoOp + + else + CloseProofSection CancelClicked + ) + , classList [ ( "button-disabled", isUploadingInProgress ) ] + , disabled isUploadingInProgress + ] + [ text (t "menu.cancel") ] + , button + [ class "modal-accept" + , classList [ ( "button-disabled", isUploadingInProgress ) ] + , onClick + (if isUploadingInProgress then + NoOp + + else + ClaimAction action + ) + , disabled isUploadingInProgress + ] + [ text (t "menu.send") ] + ] + ] + ] + + +viewProofCode : Translators -> String -> Int -> Int -> Html msg +viewProofCode { t } proofCode secondsAfterClaim proofCodeValiditySeconds = + let + remainingSeconds = + proofCodeValiditySeconds - secondsAfterClaim + + timerMinutes = + remainingSeconds // 60 + + timerSeconds = + remainingSeconds - (timerMinutes * 60) + + toString timeVal = + if timeVal < 10 then + "0" ++ String.fromInt timeVal + + else + String.fromInt timeVal + + timer = + toString timerMinutes ++ ":" ++ toString timerSeconds + in + div [ class "mb-4" ] + [ span [ class "input-label block mb-1" ] + [ text (t "community.actions.form.verification_code") ] + , div [ class "text-2xl text-black font-bold inline-block align-middle mr-2" ] + [ text proofCode ] + , span [ class "whitespace-no-wrap text-body rounded-full bg-lightred px-3 py-1 text-white" ] + [ text (t "community.actions.proof.code_period_label") + , text " " + , text timer + ] + ] + + +subscriptions : Model -> Sub Msg +subscriptions _ = + Time.every 1000 Tick + + +update : LoggedIn.Model -> Msg -> Model -> UR.UpdateResult Model Msg (External Msg) +update ({ shared } as loggedIn) msg model = + let + { t } = + shared.translators + in + case msg of + OpenProofSection action -> + let + runProofCodeTimer = + Task.perform (GotProofTime action.id) Time.now + in + if action.hasProofPhoto then + { model + | claimConfirmationModalStatus = Action.Closed + , proof = Proof NoPhotoAdded Nothing + } + |> UR.init + |> UR.addCmd + (if action.hasProofCode then + runProofCodeTimer + + else + Cmd.none + ) + |> UR.addPort + { responseAddress = NoOp + , responseData = Encode.null + , data = + Encode.object + [ ( "id", Encode.string "communityPage" ) + , ( "name", Encode.string "scrollIntoView" ) + ] + } + + else + model |> UR.init + + EnteredPhoto (file :: _) -> + let + uploadImage = + Api.uploadImage shared file CompletedPhotoUpload + + newProof = + case model.proof of + Proof _ proofCode -> + Proof Uploading proofCode + in + { model + | proof = newProof + } + |> UR.init + |> UR.addCmd uploadImage + |> UR.addExt HideFeedback + + EnteredPhoto [] -> + model + |> UR.init + + CompletedPhotoUpload (Ok url) -> + let + newProofs = + case model.proof of + Proof _ proofCode -> + Proof (Uploaded url) proofCode + in + { model | proof = newProofs } + |> UR.init + + CompletedPhotoUpload (Err error) -> + let + newProofs = + case model.proof of + Proof _ proofCode -> + Proof (UploadFailed error) proofCode + in + { model | proof = newProofs } + |> UR.init + |> UR.logHttpError msg error + + CloseProofSection reason -> + { model + | claimConfirmationModalStatus = Action.Closed + } + -- TODO: Show expired message + |> UR.init + |> UR.addExt + (case reason of + TimerExpired -> + ShowFeedback LoggedIn.Failure (t "community.actions.proof.time_expired") + + CancelClicked -> + HideFeedback + ) + + GetUint64Name _ -> + model |> UR.init + + GotUint64Name (Ok uint64name) -> + case ( model.proof, model.action ) of + ( Proof proofPhoto (Just proofCode), Just action ) -> + let + verificationCode = + generateVerificationCode action.id uint64name proofCode.claimTimestamp + + newProofCode = + Just + { proofCode + | code = Just verificationCode + } + in + { model | proof = Proof proofPhoto newProofCode } + |> UR.init + + _ -> + model + |> UR.init + + GotUint64Name (Err _) -> + model |> UR.init + + Tick timer -> + case model.proof of + Proof proofPhoto (Just proofCode) -> + let + secondsAfterClaim = + (Time.posixToMillis timer // 1000) - proofCode.claimTimestamp + + isProofCodeActive = + (proofCode.availabilityPeriod - secondsAfterClaim) > 0 + in + if isProofCodeActive then + let + newProofCode = + Just + { proofCode + | secondsAfterClaim = secondsAfterClaim + } + in + { model | proof = Proof proofPhoto newProofCode } |> UR.init + + else + update loggedIn (CloseProofSection TimerExpired) model + + _ -> + model |> UR.init + + GotProofTime actionId posix -> + let + initProofCodeParts = + Just + { code = Nothing + , claimTimestamp = Time.posixToMillis posix // 1000 + , secondsAfterClaim = 0 + , availabilityPeriod = 30 * 60 + } + in + { model | proof = Proof NoPhotoAdded initProofCodeParts } + |> UR.init + |> UR.addPort + { responseAddress = GetUint64Name (Eos.nameToString loggedIn.accountName) + , responseData = Encode.null + , data = + Encode.object + [ ( "name", Encode.string "accountNameToUint64" ) + , ( "accountName", Encode.string (Eos.nameToString loggedIn.accountName) ) + ] + } + + ClaimAction action -> + -- TODO: Remove duplication in port + let + hasPhotoError = + case model.proof of + Proof (Uploaded _) _ -> + False + + Proof _ _ -> + -- Error: photo wasn't uploaded while claiming with proof + True + + newModel = + case model.proof of + Proof _ _ -> + -- Claim with proof has no confirmation + model + + ( proofPhotoUrl, proofCode_, proofTime ) = + case model.proof of + Proof (Uploaded url) (Just { code, claimTimestamp }) -> + ( url, Maybe.withDefault "" code, claimTimestamp ) + + Proof (Uploaded url) Nothing -> + ( url, "", 0 ) + + _ -> + ( "", "", 0 ) + in + if hasPhotoError then + model + |> UR.init + |> UR.addExt (ShowFeedback LoggedIn.Failure (t "community.actions.proof.no_photo_error")) + + else if LoggedIn.isAuth loggedIn then + newModel + |> UR.init + |> UR.addPort + { responseAddress = ClaimAction action + , responseData = Encode.null + , data = + Eos.encodeTransaction + [ { accountName = shared.contracts.community + , name = "claimaction" + , authorization = + { actor = loggedIn.accountName + , permissionName = Eos.samplePermission + } + , data = + { actionId = action.id + , maker = loggedIn.accountName + , proofPhoto = proofPhotoUrl + , proofCode = proofCode_ + , proofTime = proofTime + } + |> Action.encodeClaimAction + } + ] + } + + else + newModel + |> UR.init + |> UR.addExt (Just (ClaimAction action) |> RequiredAuthentication) + + NoOp -> + model |> UR.init + + GotClaimActionResponse (Ok _) -> + -- TODO: remove duplicates + let + message = + shared.translators.tr "dashboard.check_claim.success" + [ ( "symbolCode", Eos.symbolToSymbolCodeString loggedIn.selectedCommunity ) ] + in + { model + | claimConfirmationModalStatus = Closed + } + |> UR.init + |> UR.addExt (ShowFeedback LoggedIn.Success message) + + GotClaimActionResponse (Err _) -> + -- TODO: remove duplicates + { model + | claimConfirmationModalStatus = Closed + } + |> UR.init + |> UR.addExt (ShowFeedback LoggedIn.Failure (t "dashboard.check_claim.failure")) + + +jsAddressToMsg : List String -> Value -> Maybe Msg +jsAddressToMsg addr val = + -- TODO: Remove duplicates + case addr of + "ClaimAction" :: [] -> + Decode.decodeValue + (Decode.oneOf + [ Decode.field "transactionId" Decode.string |> Decode.map Ok + , Decode.succeed (Err val) + ] + ) + val + |> Result.map (Just << GotClaimActionResponse) + |> Result.withDefault Nothing + + "GetUint64Name" :: [] -> + Decode.decodeValue + (Decode.oneOf + [ Decode.field "uint64name" Decode.string |> Decode.map Ok + , Decode.succeed (Err val) + ] + ) + val + |> Result.map (Just << GotUint64Name) + |> Result.withDefault Nothing + + _ -> + Nothing + + +msgToString : Msg -> List String +msgToString msg = + case msg of + Tick _ -> + [ "Tick" ] + + GotProofTime _ _ -> + [ "GotProofTime" ] + + OpenProofSection _ -> + [ "OpenProofSection" ] + + CloseProofSection _ -> + [ "CloseProofSection" ] + + EnteredPhoto _ -> + [ "EnteredPhoto" ] + + CompletedPhotoUpload r -> + [ "CompletedPhotoUpload", UR.resultToString r ] + + GetUint64Name _ -> + [ "GetUint64Name" ] + + GotUint64Name n -> + [ "GotUint64Name", UR.resultToString n ] + + GotClaimActionResponse r -> + [ "GotClaimActionResponse", UR.resultToString r ] + + NoOp -> + [ "NoOp" ] + + ClaimAction _ -> + [ "ClaimAction" ] + + +generateVerificationCode : Int -> String -> Int -> String +generateVerificationCode actionId makerAccountUint64 proofTimeSeconds = + (String.fromInt actionId + ++ makerAccountUint64 + ++ String.fromInt proofTimeSeconds + ) + |> sha256 + |> String.slice 0 8 diff --git a/src/elm/Route.elm b/src/elm/Route.elm index 4fcebbf3f..c66d1b44a 100755 --- a/src/elm/Route.elm +++ b/src/elm/Route.elm @@ -36,6 +36,7 @@ type Route | EditObjective Symbol Int | NewAction Symbol Int | EditAction Symbol Int Int + | ClaimAction Symbol Int Int | Claim Int Int Int | Shop Shop.Filter | NewSale @@ -97,6 +98,7 @@ parser url = , Url.map EditObjective (s "community" Eos.symbolUrlParser s "objectives" int s "edit") , Url.map NewAction (s "community" Eos.symbolUrlParser s "objectives" int s "action" s "new") , Url.map EditAction (s "community" Eos.symbolUrlParser s "objectives" int s "action" int s "edit") + , Url.map ClaimAction (s "community" Eos.symbolUrlParser s "objectives" int s "action" int s "claim") , Url.map Claim (s "objectives" int s "action" int s "claim" int) , Url.map Shop (s "shop" @@ -285,6 +287,11 @@ routeToString route = , [] ) + ClaimAction symbol objectiveId actionId -> + ( [ "community", Eos.symbolToString symbol, "objectives", String.fromInt objectiveId, "action", String.fromInt actionId, "claim" ] + , [] + ) + Claim objectiveId actionId claimId -> ( [ "objectives" , String.fromInt objectiveId diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 9e2301fcc..246c3c0f7 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -219,6 +219,7 @@ type Page | Objectives | ObjectiveEditor | ActionEditor + | ClaimAction | Claim | News | Learn From 632f7e8003f5584c041fd5bf01dca3b5cb466eba Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 3 Feb 2021 14:02:08 +0300 Subject: [PATCH 34/91] WIP: make claim with photo page work --- src/elm/Action.elm | 3 +- src/elm/Claim.elm | 4 +- src/elm/Main.elm | 17 ++++--- src/elm/Page/Community.elm | 11 ++--- src/elm/Page/Community/ClaimWithPhoto.elm | 55 ++++++++++++++++++++--- 5 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 93e760156..3175d5b00 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -16,7 +16,7 @@ import Cambiatus.Enum.VerificationType exposing (VerificationType) import Cambiatus.Object import Cambiatus.Object.Action as ActionObject import Cambiatus.Scalar exposing (DateTime) -import Eos exposing (Symbol) +import Eos import Eos.Account as Eos import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) import Html exposing (Html, button, div, text) @@ -27,7 +27,6 @@ import Json.Encode as Encode exposing (Value) import Profile import Session.LoggedIn as LoggedIn exposing (External(..)) import Session.Shared exposing (Translators) -import Sha256 exposing (sha256) import UpdateResult as UR import View.Modal as Modal diff --git a/src/elm/Claim.elm b/src/elm/Claim.elm index dd98d7e0b..94fbd655a 100644 --- a/src/elm/Claim.elm +++ b/src/elm/Claim.elm @@ -29,7 +29,6 @@ import Cambiatus.Object.Claim as Claim import Cambiatus.Object.ClaimConnection import Cambiatus.Object.ClaimEdge import Cambiatus.Scalar exposing (DateTime(..)) -import Community exposing (Objective) import Eos import Eos.Account as Eos import Graphql.OptionalArgument exposing (OptionalArgument(..)) @@ -337,11 +336,10 @@ viewClaimCard { selectedCommunity, shared, accountName } claim = claimRoute = Route.Claim --claim.action.objectiveId - -- the route just works without objectiveId even with direct loading from browser's address bar + -- TODO: the route just works without objectiveId even with direct loading from browser's address bar 0 claim.action.id claim.id - |> Debug.log "route to claim" in div [ class "w-full sm:w-1/2 lg:w-1/3 xl:w-1/4 px-2" ] [ div diff --git a/src/elm/Main.elm b/src/elm/Main.elm index b3ecbdc11..354ce4207 100755 --- a/src/elm/Main.elm +++ b/src/elm/Main.elm @@ -155,7 +155,7 @@ type Status | ObjectiveEditor ObjectiveEditor.Model | ActionEditor ActionEditor.Model | Claim Claim.Model - | ClaimAction ClaimWithPhoto.Model + | ClaimWithPhoto ClaimWithPhoto.Model | Notification Notification.Model | Dashboard Dashboard.Model | Login Login.Model @@ -191,7 +191,7 @@ type Msg | GotCommunitySettingsFeaturesMsg CommunitySettingsFeatures.Msg | GotObjectivesMsg Objectives.Msg | GotActionEditorMsg ActionEditor.Msg - | GotClaimActionMsg ClaimWithPhoto.Msg + | GotClaimWithPhotoMsg ClaimWithPhoto.Msg | GotObjectiveEditorMsg ObjectiveEditor.Msg | GotVerifyClaimMsg Claim.Msg | GotDashboardMsg Dashboard.Msg @@ -421,6 +421,11 @@ update msg model = >> updateLoggedInUResult ActionEditor GotActionEditorMsg model |> withLoggedIn + ( GotClaimWithPhotoMsg subMsg, ClaimWithPhoto subModel ) -> + ClaimWithPhoto.update subMsg subModel + >> updateLoggedInUResult ClaimWithPhoto GotClaimWithPhotoMsg model + |> withLoggedIn + ( GotVerifyClaimMsg subMsg, Claim subModel ) -> Claim.update subMsg subModel >> updateLoggedInUResult Claim GotVerifyClaimMsg model @@ -822,7 +827,7 @@ changeRouteTo maybeRoute model = Just (Route.ClaimAction symbol objectiveId actionId) -> (\l -> ClaimWithPhoto.init l symbol objectiveId (Just actionId)) - >> updateStatusWith ClaimAction GotClaimActionMsg model + >> updateStatusWith ClaimWithPhoto GotClaimWithPhotoMsg model |> withLoggedIn (Route.ClaimAction symbol objectiveId actionId) Just (Route.Claim objectiveId actionId claimId) -> @@ -988,7 +993,7 @@ msgToString msg = GotActionEditorMsg subMsg -> "GotActionEditor" :: ActionEditor.msgToString subMsg - GotClaimActionMsg subMsg -> + GotClaimWithPhotoMsg subMsg -> "GotClaimActionMsg" :: ClaimWithPhoto.msgToString subMsg GotVerifyClaimMsg subMsg -> @@ -1181,8 +1186,8 @@ view model = ActionEditor subModel -> viewLoggedIn subModel LoggedIn.ActionEditor GotActionEditorMsg ActionEditor.view - ClaimAction subModel -> - viewLoggedIn subModel LoggedIn.ClaimAction GotClaimActionMsg ClaimWithPhoto.view + ClaimWithPhoto subModel -> + viewLoggedIn subModel LoggedIn.ClaimAction GotClaimWithPhotoMsg ClaimWithPhoto.view Claim subModel -> viewLoggedIn subModel LoggedIn.Claim GotVerifyClaimMsg Claim.view diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index cf32f897b..cadad0bbe 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -16,7 +16,7 @@ import Cambiatus.Enum.VerificationType as VerificationType import Community exposing (Model) import Eos exposing (Symbol) import Graphql.Http -import Html exposing (Html, button, div, img, p, span, text) +import Html exposing (Html, a, button, div, img, p, span, text) import Html.Attributes exposing (class, classList, id, src) import Html.Events exposing (onClick) import Icons @@ -83,10 +83,6 @@ type PageStatus | Failed (Graphql.Http.Error (Maybe Community.Model)) -type ActiveSection - = ObjectivesAndActions - - -- VIEW @@ -258,6 +254,7 @@ viewObjective loggedIn model metadata index objective = viewAction loggedIn.shared.translators (LoggedIn.isAccount metadata.creator loggedIn) metadata.symbol + objective.id model.date action ) @@ -447,8 +444,8 @@ msgToString msg = [ "ClickedCloseObjective" ] -viewAction : Translators -> Bool -> Symbol -> Maybe Posix -> Action -> Html Msg -viewAction translators canEdit symbol maybeDate action = +viewAction : Translators -> Bool -> Symbol -> Int -> Maybe Posix -> Action -> Html Msg +viewAction translators canEdit symbol objId maybeDate action = let { t, tr } = translators diff --git a/src/elm/Page/Community/ClaimWithPhoto.elm b/src/elm/Page/Community/ClaimWithPhoto.elm index 1a51bbb63..362766dc5 100644 --- a/src/elm/Page/Community/ClaimWithPhoto.elm +++ b/src/elm/Page/Community/ClaimWithPhoto.elm @@ -1,10 +1,22 @@ -module Page.Community.ClaimWithPhoto exposing (..) +module Page.Community.ClaimWithPhoto exposing + ( Model + , Msg(..) + , init + , jsAddressToMsg + , msgToString + , subscriptions + , update + , view + ) import Action exposing (Action, ClaimConfirmationModalStatus(..)) import Api +import Api.Graphql +import Community import Eos exposing (Symbol) import Eos.Account as Eos import File exposing (File) +import Graphql.Http import Html exposing (Html, button, div, input, label, p, span, text) import Html.Attributes exposing (accept, class, classList, disabled, multiple, style, type_) import Html.Events exposing (onClick) @@ -23,6 +35,7 @@ import UpdateResult as UR type alias Model = { claimConfirmationModalStatus : Action.ClaimConfirmationModalStatus + , status : Status , action : Maybe Action -- TODO: Use result since action must be loaded , proof : Proof } @@ -39,15 +52,19 @@ type alias ActionId = init : LoggedIn.Model -> Symbol -> ObjectiveId -> Maybe ActionId -> ( Model, Cmd Msg ) init loggedIn symbol objectiveId actionId = ( { claimConfirmationModalStatus = Action.Closed + , status = Loading , action = Nothing , proof = Proof NoPhotoAdded Nothing } - , Cmd.none + , Api.Graphql.query loggedIn.shared + (Community.communityQuery symbol) + CommunityLoaded ) type Msg = NoOp + | CommunityLoaded (Result (Graphql.Http.Error (Maybe Community.Model)) (Maybe Community.Model)) | OpenProofSection Action | CloseProofSection ReasonToCloseProofSection | GotProofTime Int Posix @@ -255,13 +272,38 @@ subscriptions _ = Time.every 1000 Tick -update : LoggedIn.Model -> Msg -> Model -> UR.UpdateResult Model Msg (External Msg) -update ({ shared } as loggedIn) msg model = +type Status + = Loading + | Loaded Community.Model + -- Errors + | LoadFailed (Graphql.Http.Error (Maybe Community.Model)) + | NotFound + + +update : Msg -> Model -> LoggedIn.Model -> UR.UpdateResult Model Msg (External Msg) +update msg model ({ shared } as loggedIn) = let { t } = shared.translators in case msg of + CommunityLoaded (Err err) -> + { model | status = LoadFailed err } + |> UR.init + |> UR.logGraphqlError msg err + + CommunityLoaded (Ok c) -> + case c of + Just community -> + { model + | status = Loaded community + } + |> UR.init + + Nothing -> + { model | status = NotFound } + |> UR.init + OpenProofSection action -> let runProofCodeTimer = @@ -397,7 +439,7 @@ update ({ shared } as loggedIn) msg model = { model | proof = Proof proofPhoto newProofCode } |> UR.init else - update loggedIn (CloseProofSection TimerExpired) model + update (CloseProofSection TimerExpired) model loggedIn _ -> model |> UR.init @@ -547,6 +589,9 @@ jsAddressToMsg addr val = msgToString : Msg -> List String msgToString msg = case msg of + CommunityLoaded res -> + [ "CommunityLoaded" ] + Tick _ -> [ "Tick" ] From d171312e399716f47f833d61cef994d925623d3c Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 3 Feb 2021 14:48:05 +0300 Subject: [PATCH 35/91] WIP: create claimactionport function --- src/elm/Action.elm | 66 +++++++++++++++-------- src/elm/Page/Community/ClaimWithPhoto.elm | 37 ++++++++++--- src/elm/Search.elm | 5 +- 3 files changed, 76 insertions(+), 32 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 3175d5b00..9cf5a71d1 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -3,6 +3,7 @@ module Action exposing , ClaimConfirmationModalStatus(..) , Model , Msg(..) + , claimActionPort , encodeClaimAction , init , jsAddressToMsg @@ -16,14 +17,15 @@ import Cambiatus.Enum.VerificationType exposing (VerificationType) import Cambiatus.Object import Cambiatus.Object.Action as ActionObject import Cambiatus.Scalar exposing (DateTime) -import Eos -import Eos.Account as Eos +import Eos exposing (Symbol) +import Eos.Account as Eos exposing (Name) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) import Html exposing (Html, button, div, text) import Html.Attributes exposing (class, classList, disabled) import Html.Events exposing (onClick) import Json.Decode as Decode import Json.Encode as Encode exposing (Value) +import Ports import Profile import Session.LoggedIn as LoggedIn exposing (External(..)) import Session.Shared exposing (Translators) @@ -158,8 +160,17 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = -- UPDATE +type alias ActionUpdate = + { isAuthorized : Bool + , accountName : Name + , selectedCommunity : Symbol + , contractsCommunity : String --Shared.contracts.community + } + + update : LoggedIn.Model -> Msg -> Model -> UR.UpdateResult Model Msg (External Msg) update ({ shared } as loggedIn) msg model = + -- TODO: Don't apply ports here, don't use loggedIn. It must be simple, local component without any global state let { t } = shared.translators @@ -182,27 +193,11 @@ update ({ shared } as loggedIn) msg model = newModel |> UR.init |> UR.addPort - { responseAddress = ClaimAction action - , responseData = Encode.null - , data = - Eos.encodeTransaction - [ { accountName = shared.contracts.community - , name = "claimaction" - , authorization = - { actor = loggedIn.accountName - , permissionName = Eos.samplePermission - } - , data = - { actionId = action.id - , maker = loggedIn.accountName - , proofPhoto = "" - , proofCode = "" - , proofTime = 0 - } - |> encodeClaimAction - } - ] - } + (claimActionPort + action + shared.contracts.community + loggedIn.accountName + ) else newModel @@ -232,6 +227,31 @@ update ({ shared } as loggedIn) msg model = model |> UR.init +claimActionPort : Action -> String -> Name -> Ports.JavascriptOutModel Msg +claimActionPort action contractsCommunity accountName = + { responseAddress = ClaimAction action + , responseData = Encode.null + , data = + Eos.encodeTransaction + [ { accountName = contractsCommunity + , name = "claimaction" + , authorization = + { actor = accountName + , permissionName = Eos.samplePermission + } + , data = + { actionId = action.id + , maker = accountName + , proofPhoto = "" + , proofCode = "" + , proofTime = 0 + } + |> encodeClaimAction + } + ] + } + + jsAddressToMsg : List String -> Value -> Maybe Msg jsAddressToMsg addr val = case addr of diff --git a/src/elm/Page/Community/ClaimWithPhoto.elm b/src/elm/Page/Community/ClaimWithPhoto.elm index 362766dc5..7d2daa058 100644 --- a/src/elm/Page/Community/ClaimWithPhoto.elm +++ b/src/elm/Page/Community/ClaimWithPhoto.elm @@ -25,6 +25,7 @@ import Icons import Json.Decode as Decode import Json.Encode as Encode exposing (Value) import Page +import Route import Session.LoggedIn as LoggedIn exposing (External(..)) import Session.Shared exposing (Translators) import Sha256 exposing (sha256) @@ -145,15 +146,35 @@ viewPhotoUploader { t } proofPhotoStatus = view : LoggedIn.Model -> Model -> { title : String, content : Html Msg } -view loggedIn model = - { title = "Claim with photo" - , content = - case model.action of - Just a -> - viewClaimWithProofs model.proof loggedIn.shared.translators a +view ({ shared } as loggedIn) model = + let + { t } = + shared.translators + + content = + case model.status of + Loading -> + Page.fullPageLoading shared + + Loaded community -> + div [ class "bg-white" ] + [ Page.viewHeader loggedIn (t "community.actions.title") (Route.Community community.symbol) + , case model.action of + Just a -> + viewClaimWithProofs model.proof loggedIn.shared.translators a - Nothing -> - text "this is claim with photo page" + Nothing -> + text "this is claim with photo page" + ] + + LoadFailed err -> + Page.fullPageGraphQLError (t "error.invalidSymbol") err + + NotFound -> + Page.fullPageNotFound (t "community.actions.form.not_found") "" + in + { title = "Claim with photo" + , content = content } diff --git a/src/elm/Search.elm b/src/elm/Search.elm index 9ea986b6d..d9939c05e 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -411,7 +411,10 @@ viewActions symbol ({ actions, offers } as results) = , text " " , text <| Eos.symbolToSymbolCodeString symbol ] - , button [ class "self-end button button-primary" ] [ text "Claim" ] + , button + [ class "self-end button button-primary" + ] + [ text "Claim" ] ] ] ] From 47353dcdff041f5a3fea49ecef8377bcb5045d85 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Wed, 3 Feb 2021 15:37:28 +0300 Subject: [PATCH 36/91] Remove LoggedIn data from Action module --- src/elm/Action.elm | 74 +++++++-------------------------- src/elm/Page/Community.elm | 83 +++++++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 88 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 9cf5a71d1..0fd4c3dfb 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -17,7 +17,7 @@ import Cambiatus.Enum.VerificationType exposing (VerificationType) import Cambiatus.Object import Cambiatus.Object.Action as ActionObject import Cambiatus.Scalar exposing (DateTime) -import Eos exposing (Symbol) +import Eos import Eos.Account as Eos exposing (Name) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) import Html exposing (Html, button, div, text) @@ -27,7 +27,6 @@ import Json.Decode as Decode import Json.Encode as Encode exposing (Value) import Ports import Profile -import Session.LoggedIn as LoggedIn exposing (External(..)) import Session.Shared exposing (Translators) import UpdateResult as UR import View.Modal as Modal @@ -85,7 +84,7 @@ type Msg = NoOp | OpenClaimConfirmation Action | CloseClaimConfirmation - | ClaimAction Action + | ClaimAction | GotClaimActionResponse (Result Value String) @@ -145,7 +144,7 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = NoOp else - ClaimAction action + ClaimAction in modalContent acceptMsg False @@ -160,76 +159,31 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = -- UPDATE -type alias ActionUpdate = - { isAuthorized : Bool - , accountName : Name - , selectedCommunity : Symbol - , contractsCommunity : String --Shared.contracts.community - } - - -update : LoggedIn.Model -> Msg -> Model -> UR.UpdateResult Model Msg (External Msg) -update ({ shared } as loggedIn) msg model = - -- TODO: Don't apply ports here, don't use loggedIn. It must be simple, local component without any global state - let - { t } = - shared.translators - in +update : Translators -> Msg -> Model -> Model +update { t } msg model = case msg of OpenClaimConfirmation action -> { model | claimConfirmationModalStatus = Open action } - |> UR.init CloseClaimConfirmation -> { model | claimConfirmationModalStatus = Closed } - |> UR.init - ClaimAction action -> - let - newModel = - { model | claimConfirmationModalStatus = InProgress } - in - if LoggedIn.isAuth loggedIn then - newModel - |> UR.init - |> UR.addPort - (claimActionPort - action - shared.contracts.community - loggedIn.accountName - ) - - else - newModel - |> UR.init - |> UR.addExt (Just (ClaimAction action) |> RequiredAuthentication) + ClaimAction -> + { model | claimConfirmationModalStatus = InProgress } GotClaimActionResponse (Ok _) -> - let - message = - shared.translators.tr "dashboard.check_claim.success" - [ ( "symbolCode", Eos.symbolToSymbolCodeString loggedIn.selectedCommunity ) ] - in - { model - | claimConfirmationModalStatus = Closed - } - |> UR.init - |> UR.addExt (ShowFeedback LoggedIn.Success message) + { model | claimConfirmationModalStatus = Closed } GotClaimActionResponse (Err _) -> - { model - | claimConfirmationModalStatus = Closed - } - |> UR.init - |> UR.addExt (ShowFeedback LoggedIn.Failure (t "dashboard.check_claim.failure")) + { model | claimConfirmationModalStatus = Closed } NoOp -> - model |> UR.init + model -claimActionPort : Action -> String -> Name -> Ports.JavascriptOutModel Msg -claimActionPort action contractsCommunity accountName = - { responseAddress = ClaimAction action +claimActionPort : msg -> Action -> String -> Name -> Ports.JavascriptOutModel msg +claimActionPort msg action contractsCommunity accountName = + { responseAddress = msg , responseData = Encode.null , data = Eos.encodeTransaction @@ -282,7 +236,7 @@ msgToString msg = CloseClaimConfirmation -> [ "CloseClaimConfirmation" ] - ClaimAction _ -> + ClaimAction -> [ "ClaimAction" ] GotClaimActionResponse r -> diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index cadad0bbe..cfdec4530 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -22,8 +22,9 @@ import Html.Events exposing (onClick) import Icons import Json.Encode exposing (Value) import Page +import Ports exposing (JavascriptOutModel) import Route -import Session.LoggedIn as LoggedIn exposing (External(..), FeedbackStatus(..), mapExternal) +import Session.LoggedIn as LoggedIn exposing (External(..), FeedbackStatus(..)) import Session.Shared exposing (Translators) import Strftime import Task @@ -325,6 +326,10 @@ type Msg update : Msg -> Model -> LoggedIn.Model -> UpdateResult update msg model ({ shared } as loggedIn) = + let + { t } = + shared.translators + in case msg of NoOp -> UR.init model @@ -334,52 +339,74 @@ update msg model ({ shared } as loggedIn) = GotActionMsg actionMsg -> let - updateClaimingAction : Action.Model -> UpdateResult + updateClaimingAction : Action.Model -> Model updateClaimingAction actionModel = - let - cb : External Action.Msg -> UpdateResult -> UpdateResult - cb = - \extMsgAction uResult -> - uResult - |> UR.addExt (mapExternal GotActionMsg extMsgAction) - in - Action.update loggedIn actionMsg actionModel - |> UR.map - (\a -> { model | claimingAction = Just a }) - GotActionMsg - cb + { model + | claimingAction = + Action.update shared.translators actionMsg actionModel + |> Just + } in case actionMsg of Action.OpenClaimConfirmation action -> updateClaimingAction (Action.init action) + |> UR.init - Action.GotClaimActionResponse (Ok _) -> + Action.ClaimAction -> case model.claimingAction of Just ca -> let - updatePageStatus m = - { m - | pageStatus = - -- TODO: Do we really need this update? - case model.pageStatus of - Loaded community -> - Loaded community - - _ -> - m.pageStatus - } + claimPort : JavascriptOutModel Msg + claimPort = + Action.claimActionPort + (GotActionMsg Action.ClaimAction) + ca.action + shared.contracts.community + loggedIn.accountName in - updateClaimingAction ca - |> UR.mapModel updatePageStatus + if LoggedIn.isAuth loggedIn then + updateClaimingAction ca + |> UR.init + |> UR.addPort claimPort + + else + updateClaimingAction ca + |> UR.init + |> UR.addExt + (Just (GotActionMsg Action.ClaimAction) + |> RequiredAuthentication + ) Nothing -> model |> UR.init + Action.GotClaimActionResponse resp -> + case ( model.claimingAction, resp ) of + ( Just ca, Ok _ ) -> + let + message = + shared.translators.tr "dashboard.check_claim.success" + [ ( "symbolCode", Eos.symbolToSymbolCodeString loggedIn.selectedCommunity ) ] + in + updateClaimingAction ca + |> UR.init + |> UR.addExt (ShowFeedback LoggedIn.Success message) + + ( Just ca, Err _ ) -> + updateClaimingAction ca + |> UR.init + |> UR.addExt (ShowFeedback LoggedIn.Failure (t "dashboard.check_claim.failure")) + + ( Nothing, _ ) -> + model + |> UR.init + _ -> case model.claimingAction of Just ca -> updateClaimingAction ca + |> UR.init Nothing -> model From 71ecaf47d3ed5eddcc892ed09e148888def9a275 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 4 Feb 2021 08:28:22 +0300 Subject: [PATCH 37/91] Improve naming --- src/elm/Action.elm | 34 +++++++++++++++++----------------- src/elm/Claim.elm | 2 +- src/elm/Page/Community.elm | 26 +++++++++++++------------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 0fd4c3dfb..0f475ab59 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -82,10 +82,10 @@ init action = type Msg = NoOp - | OpenClaimConfirmation Action - | CloseClaimConfirmation - | ClaimAction - | GotClaimActionResponse (Result Value String) + | ClaimConfirmationOpen Action + | ClaimConfirmationClosed + | ActionClaimed + | GotActionClaimedResponse (Result Value String) viewClaimConfirmation : Translators -> ClaimConfirmationModalStatus -> Html Msg @@ -97,7 +97,7 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = modalContent acceptMsg isInProgress = div [] [ Modal.initWith - { closeMsg = CloseClaimConfirmation + { closeMsg = ClaimConfirmationClosed , isVisible = True } |> Modal.withHeader (t "claim.modal.title") @@ -111,7 +111,7 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = NoOp else - CloseClaimConfirmation + ClaimConfirmationClosed ) , disabled isInProgress ] @@ -144,7 +144,7 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = NoOp else - ClaimAction + ActionClaimed in modalContent acceptMsg False @@ -162,19 +162,19 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = update : Translators -> Msg -> Model -> Model update { t } msg model = case msg of - OpenClaimConfirmation action -> + ClaimConfirmationOpen action -> { model | claimConfirmationModalStatus = Open action } - CloseClaimConfirmation -> + ClaimConfirmationClosed -> { model | claimConfirmationModalStatus = Closed } - ClaimAction -> + ActionClaimed -> { model | claimConfirmationModalStatus = InProgress } - GotClaimActionResponse (Ok _) -> + GotActionClaimedResponse (Ok _) -> { model | claimConfirmationModalStatus = Closed } - GotClaimActionResponse (Err _) -> + GotActionClaimedResponse (Err _) -> { model | claimConfirmationModalStatus = Closed } NoOp -> @@ -217,7 +217,7 @@ jsAddressToMsg addr val = ] ) val - |> Result.map (Just << GotClaimActionResponse) + |> Result.map (Just << GotActionClaimedResponse) |> Result.withDefault Nothing _ -> @@ -230,16 +230,16 @@ msgToString msg = NoOp -> [ "NoOp" ] - OpenClaimConfirmation _ -> + ClaimConfirmationOpen _ -> [ "OpenClaimConfirmation" ] - CloseClaimConfirmation -> + ClaimConfirmationClosed -> [ "CloseClaimConfirmation" ] - ClaimAction -> + ActionClaimed -> [ "ClaimAction" ] - GotClaimActionResponse r -> + GotActionClaimedResponse r -> [ "GotClaimActionResponse", UR.resultToString r ] diff --git a/src/elm/Claim.elm b/src/elm/Claim.elm index 94fbd655a..b66363493 100644 --- a/src/elm/Claim.elm +++ b/src/elm/Claim.elm @@ -343,7 +343,7 @@ viewClaimCard { selectedCommunity, shared, accountName } claim = in div [ class "w-full sm:w-1/2 lg:w-1/3 xl:w-1/4 px-2" ] [ div - [ class "flex flex-col p-4 my-2 rounded-lg bg-white hover:shadow cursor-pointer bg-red" + [ class "flex flex-col p-4 my-2 rounded-lg bg-white hover:shadow cursor-pointer" , id ("claim" ++ String.fromInt claim.id) -- We can't just use `a` with `href` here because there are other `a`-nodes inside. diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index cfdec4530..e46609703 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -52,7 +52,7 @@ initModel _ _ = { date = Nothing , pageStatus = Loading , openObjective = Nothing - , claimingAction = Nothing + , actionToClaim = Nothing } @@ -71,7 +71,7 @@ subscriptions _ = type alias Model = { date : Maybe Posix - , claimingAction : Maybe Action.Model + , actionToClaim : Maybe Action.Model , pageStatus : PageStatus , openObjective : Maybe Int } @@ -138,7 +138,7 @@ view loggedIn model = [ if community.hasObjectives then div [ class "px-4 pb-4" ] [ Html.map GotActionMsg - (case model.claimingAction of + (case model.actionToClaim of Just ca -> Action.viewClaimConfirmation loggedIn.shared.translators @@ -342,24 +342,24 @@ update msg model ({ shared } as loggedIn) = updateClaimingAction : Action.Model -> Model updateClaimingAction actionModel = { model - | claimingAction = + | actionToClaim = Action.update shared.translators actionMsg actionModel |> Just } in case actionMsg of - Action.OpenClaimConfirmation action -> + Action.ClaimConfirmationOpen action -> updateClaimingAction (Action.init action) |> UR.init - Action.ClaimAction -> - case model.claimingAction of + Action.ActionClaimed -> + case model.actionToClaim of Just ca -> let claimPort : JavascriptOutModel Msg claimPort = Action.claimActionPort - (GotActionMsg Action.ClaimAction) + (GotActionMsg Action.ActionClaimed) ca.action shared.contracts.community loggedIn.accountName @@ -373,7 +373,7 @@ update msg model ({ shared } as loggedIn) = updateClaimingAction ca |> UR.init |> UR.addExt - (Just (GotActionMsg Action.ClaimAction) + (Just (GotActionMsg Action.ActionClaimed) |> RequiredAuthentication ) @@ -381,8 +381,8 @@ update msg model ({ shared } as loggedIn) = model |> UR.init - Action.GotClaimActionResponse resp -> - case ( model.claimingAction, resp ) of + Action.GotActionClaimedResponse resp -> + case ( model.actionToClaim, resp ) of ( Just ca, Ok _ ) -> let message = @@ -403,7 +403,7 @@ update msg model ({ shared } as loggedIn) = |> UR.init _ -> - case model.claimingAction of + case model.actionToClaim of Just ca -> updateClaimingAction ca |> UR.init @@ -598,7 +598,7 @@ viewAction translators canEdit symbol objId maybeDate action = NoOp else - (GotActionMsg << Action.OpenClaimConfirmation) action + (GotActionMsg << Action.ClaimConfirmationOpen) action ) ] [ if action.hasProofPhoto then From bdde187781ce098c6b1e76f4a75956bfe3286a6e Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 4 Feb 2021 10:24:28 +0300 Subject: [PATCH 38/91] Make claim with photo work on search results --- src/elm/Action.elm | 61 ++++++++++++++++++++++++++++++++++++-- src/elm/Community.elm | 54 +++------------------------------ src/elm/Page/Community.elm | 6 ++-- src/elm/Search.elm | 39 +++++++++++++++++------- 4 files changed, 95 insertions(+), 65 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 0f475ab59..4c34ac494 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -5,28 +5,33 @@ module Action exposing , Msg(..) , claimActionPort , encodeClaimAction + , getClaimWithPhotoRoute , init , jsAddressToMsg , msgToString , selectionSet , update + , viewClaimButton , viewClaimConfirmation ) import Cambiatus.Enum.VerificationType exposing (VerificationType) import Cambiatus.Object import Cambiatus.Object.Action as ActionObject +import Cambiatus.Object.Objective import Cambiatus.Scalar exposing (DateTime) import Eos import Eos.Account as Eos exposing (Name) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, button, div, text) +import Html exposing (Html, a, button, div, span, text) import Html.Attributes exposing (class, classList, disabled) import Html.Events exposing (onClick) +import Icons import Json.Decode as Decode import Json.Encode as Encode exposing (Value) import Ports import Profile +import Route import Session.Shared exposing (Translators) import UpdateResult as UR import View.Modal as Modal @@ -40,6 +45,7 @@ type ClaimConfirmationModalStatus type alias Action = { id : Int + , objectiveId : Int , description : String , reward : Float , verifierReward : Float @@ -73,6 +79,10 @@ type alias Model = } +type alias ObjectiveId = + Int + + init : Action -> Model init action = { claimConfirmationModalStatus = Closed @@ -86,6 +96,15 @@ type Msg | ClaimConfirmationClosed | ActionClaimed | GotActionClaimedResponse (Result Value String) + | ClaimWithPhotoLinkClicked + + +getClaimWithPhotoRoute : Eos.Symbol -> Int -> Int -> Route.Route +getClaimWithPhotoRoute community objectiveId actionId = + Route.ClaimAction + community + objectiveId + actionId viewClaimConfirmation : Translators -> ClaimConfirmationModalStatus -> Html Msg @@ -162,6 +181,7 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = update : Translators -> Msg -> Model -> Model update { t } msg model = case msg of + -- TODO: this update function looks to simple to have it here... ClaimConfirmationOpen action -> { model | claimConfirmationModalStatus = Open action } @@ -177,6 +197,9 @@ update { t } msg model = GotActionClaimedResponse (Err _) -> { model | claimConfirmationModalStatus = Closed } + ClaimWithPhotoLinkClicked -> + model + NoOp -> model @@ -239,6 +262,9 @@ msgToString msg = ActionClaimed -> [ "ClaimAction" ] + ClaimWithPhotoLinkClicked -> + [ "ClaimWithPhotoLinkClicked" ] + GotActionClaimedResponse r -> [ "GotClaimActionResponse", UR.resultToString r ] @@ -254,10 +280,22 @@ encodeClaimAction c = ] +type alias Objective = + { id : Int + } + + +objectiveSelectionSet : SelectionSet Objective Cambiatus.Object.Objective +objectiveSelectionSet = + SelectionSet.succeed Objective + |> with Cambiatus.Object.Objective.id + + selectionSet : SelectionSet Action Cambiatus.Object.Action selectionSet = SelectionSet.succeed Action |> with ActionObject.id + |> with (SelectionSet.map (\s -> s.id) (ActionObject.objective objectiveSelectionSet)) |> with ActionObject.description |> with ActionObject.reward |> with ActionObject.verifierReward @@ -267,10 +305,29 @@ selectionSet = |> with ActionObject.usagesLeft |> with ActionObject.deadline |> with ActionObject.verificationType - --|> with (SelectionSet.map ActionObject.objective actionObjectiveIdSelectionSet) |> with ActionObject.verifications |> with ActionObject.isCompleted |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofPhoto) |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofCode) |> with ActionObject.photoProofInstructions |> with ActionObject.position + + +viewClaimButton : { a | hasProofPhoto : Bool, objectiveId : Int, id : Int } -> Eos.Symbol -> Html Msg +viewClaimButton action symbol = + -- TODO: Handle action.deadline and action.isCompleted. + if action.hasProofPhoto then + a + [ Route.href (getClaimWithPhotoRoute symbol action.objectiveId action.id) + , onClick ClaimWithPhotoLinkClicked + , class "self-end button button-primary" + ] + [ span [ class "inline-block w-4 align-middle mr-2" ] [ Icons.camera "" ] + , span [ class "inline-block align-middle" ] [ text "Claim" ] + ] + + else + button + [ class "self-end button button-primary" ] + [ span [ class "inline-block align-middle" ] [ text "Claim" ] + ] diff --git a/src/elm/Community.elm b/src/elm/Community.elm index 698f00950..f17a0b400 100755 --- a/src/elm/Community.elm +++ b/src/elm/Community.elm @@ -1,6 +1,5 @@ module Community exposing - ( ActionFromCommunityModule - , ActionVerification + ( ActionVerification , ActionVerificationsResponse , Balance , ClaimResponse @@ -40,6 +39,7 @@ module Community exposing , toVerifications ) +import Action exposing (Action) import Cambiatus.Enum.VerificationType exposing (VerificationType(..)) import Cambiatus.Object import Cambiatus.Object.Action as ActionObject @@ -283,7 +283,7 @@ type alias Objective = { id : Int , description : String , creator : Eos.Name - , actions : List ActionFromCommunityModule + , actions : List Action , community : Metadata , isCompleted : Bool } @@ -295,7 +295,7 @@ objectiveSelectionSet = |> with Objective.id |> with Objective.description |> with (Eos.nameSelectionSet Objective.creatorId) - |> with (Objective.actions identity actionSelectionSet) + |> with (Objective.actions identity Action.selectionSet) |> with (Objective.community communitiesSelectionSet) |> with Objective.isCompleted @@ -332,52 +332,6 @@ encodeUpdateObjectiveAction c = ] - --- ACTION - - -type alias ActionFromCommunityModule = - { id : Int - , description : String - , reward : Float - , verifierReward : Float - , creator : Eos.Name - , validators : List Profile.Minimal - , usages : Int - , usagesLeft : Int - , deadline : Maybe DateTime - , verificationType : VerificationType - , verifications : Int - , isCompleted : Bool - , hasProofPhoto : Bool - , hasProofCode : Bool - , photoProofInstructions : Maybe String - , position : Maybe Int - } - - -actionSelectionSet : SelectionSet ActionFromCommunityModule Cambiatus.Object.Action -actionSelectionSet = - SelectionSet.succeed ActionFromCommunityModule - |> with ActionObject.id - |> with ActionObject.description - |> with ActionObject.reward - |> with ActionObject.verifierReward - |> with (Eos.nameSelectionSet ActionObject.creatorId) - |> with (ActionObject.validators Profile.minimalSelectionSet) - |> with ActionObject.usages - |> with ActionObject.usagesLeft - |> with ActionObject.deadline - |> with ActionObject.verificationType - --|> with (SelectionSet.map (\s -> s.id) (ActionObject.objective objectiveSelectionSet)) - |> with ActionObject.verifications - |> with ActionObject.isCompleted - |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofPhoto) - |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofCode) - |> with ActionObject.photoProofInstructions - |> with ActionObject.position - - type Verification = Manually Verifiers | Automatically String diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index e46609703..ad7a94ace 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -255,7 +255,6 @@ viewObjective loggedIn model metadata index objective = viewAction loggedIn.shared.translators (LoggedIn.isAccount metadata.creator loggedIn) metadata.symbol - objective.id model.date action ) @@ -471,8 +470,8 @@ msgToString msg = [ "ClickedCloseObjective" ] -viewAction : Translators -> Bool -> Symbol -> Int -> Maybe Posix -> Action -> Html Msg -viewAction translators canEdit symbol objId maybeDate action = +viewAction : Translators -> Bool -> Symbol -> Maybe Posix -> Action -> Html Msg +viewAction translators canEdit symbol maybeDate action = let { t, tr } = translators @@ -591,6 +590,7 @@ viewAction translators canEdit symbol objId maybeDate action = || (action.usages > 0 && action.usagesLeft == 0) viewClaimButton = + -- TODO: move this to Action module button [ class ("h-10 uppercase rounded-lg ml-1" ++ claimColors ++ claimSize) , onClick diff --git a/src/elm/Search.elm b/src/elm/Search.elm index d9939c05e..ee75cfe5a 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -1,8 +1,10 @@ module Search exposing (Model, Msg, closeSearch, init, isActive, subscriptions, update, viewBody, viewForm, viewRecentQueries) +import Action import Api.Graphql import Cambiatus.Object import Cambiatus.Object.Action +import Cambiatus.Object.Objective import Cambiatus.Object.Product import Cambiatus.Object.SearchResult import Cambiatus.Query @@ -10,8 +12,8 @@ import Eos exposing (Symbol) import Graphql.Http import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, br, button, div, h3, i, img, input, li, p, span, strong, text, ul) -import Html.Attributes exposing (class, placeholder, src, type_, value) +import Html exposing (Html, a, br, button, div, h3, i, img, input, li, p, span, strong, text, ul) +import Html.Attributes exposing (class, href, placeholder, src, type_, value) import Html.Events exposing (onClick, onFocus, onInput, onSubmit) import Icons import Json.Decode as Decode exposing (list, string) @@ -76,8 +78,10 @@ type alias FoundOffer = type alias FoundAction = { id : Int + , objectiveId : Int , description : String , reward : Float + , hasProofPhoto : Bool } @@ -113,12 +117,25 @@ offersSelectionSet = Cambiatus.Object.Product.image +type alias Objective = + { id : Int + } + + +objectiveSelectionSet : SelectionSet Objective Cambiatus.Object.Objective +objectiveSelectionSet = + SelectionSet.map Objective Cambiatus.Object.Objective.id + + actionsSelectionSet : SelectionSet FoundAction Cambiatus.Object.Action actionsSelectionSet = - SelectionSet.map3 FoundAction - Cambiatus.Object.Action.id - Cambiatus.Object.Action.description - Cambiatus.Object.Action.reward + SelectionSet.succeed FoundAction + |> with Cambiatus.Object.Action.id + -- TODO: simplify retrieving the objective id + |> with (SelectionSet.map (\s -> s.id) (Cambiatus.Object.Action.objective objectiveSelectionSet)) + |> with Cambiatus.Object.Action.description + |> with Cambiatus.Object.Action.reward + |> with (SelectionSet.map (Maybe.withDefault False) Cambiatus.Object.Action.hasProofPhoto) @@ -133,6 +150,7 @@ type Msg | QuerySubmitted | TabActivated FoundItemsKind | FoundItemClicked Route + | GotActionMsg Action.Msg closeSearch : Shared -> Model -> ( Model, Cmd Msg ) @@ -143,6 +161,9 @@ closeSearch shared model = update : Shared -> Model -> Msg -> ( Model, Cmd Msg ) update shared model msg = case msg of + GotActionMsg actionMsg -> + ( { model | state = Inactive }, Cmd.none ) + FoundItemClicked route -> let -- Make the search dropdown inactive before opening the found item's URL. @@ -411,10 +432,8 @@ viewActions symbol ({ actions, offers } as results) = , text " " , text <| Eos.symbolToSymbolCodeString symbol ] - , button - [ class "self-end button button-primary" - ] - [ text "Claim" ] + , Action.viewClaimButton action symbol + |> Html.map GotActionMsg ] ] ] From 83ff280859ed365926aba4a2e4c425ee409a42fa Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 4 Feb 2021 10:59:05 +0300 Subject: [PATCH 39/91] Use Aciton type in search --- src/elm/Action.elm | 22 ++++--- src/elm/Page/Community.elm | 2 +- src/elm/Search.elm | 122 ++++++++++++++++++------------------- 3 files changed, 71 insertions(+), 75 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 4c34ac494..6f5818cae 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -6,7 +6,7 @@ module Action exposing , claimActionPort , encodeClaimAction , getClaimWithPhotoRoute - , init + , initModel , jsAddressToMsg , msgToString , selectionSet @@ -83,8 +83,8 @@ type alias ObjectiveId = Int -init : Action -> Model -init action = +initModel : Action -> Model +initModel action = { claimConfirmationModalStatus = Closed , action = action } @@ -96,7 +96,7 @@ type Msg | ClaimConfirmationClosed | ActionClaimed | GotActionClaimedResponse (Result Value String) - | ClaimWithPhotoLinkClicked + | ActionWithPhotoLinkClicked getClaimWithPhotoRoute : Eos.Symbol -> Int -> Int -> Route.Route @@ -197,7 +197,7 @@ update { t } msg model = GotActionClaimedResponse (Err _) -> { model | claimConfirmationModalStatus = Closed } - ClaimWithPhotoLinkClicked -> + ActionWithPhotoLinkClicked -> model NoOp -> @@ -262,8 +262,8 @@ msgToString msg = ActionClaimed -> [ "ClaimAction" ] - ClaimWithPhotoLinkClicked -> - [ "ClaimWithPhotoLinkClicked" ] + ActionWithPhotoLinkClicked -> + [ "ActionWithPhotoLinkClicked" ] GotActionClaimedResponse r -> [ "GotClaimActionResponse", UR.resultToString r ] @@ -313,13 +313,13 @@ selectionSet = |> with ActionObject.position -viewClaimButton : { a | hasProofPhoto : Bool, objectiveId : Int, id : Int } -> Eos.Symbol -> Html Msg +viewClaimButton : Action -> Eos.Symbol -> Html Msg viewClaimButton action symbol = -- TODO: Handle action.deadline and action.isCompleted. if action.hasProofPhoto then a [ Route.href (getClaimWithPhotoRoute symbol action.objectiveId action.id) - , onClick ClaimWithPhotoLinkClicked + , onClick ActionWithPhotoLinkClicked , class "self-end button button-primary" ] [ span [ class "inline-block w-4 align-middle mr-2" ] [ Icons.camera "" ] @@ -328,6 +328,8 @@ viewClaimButton action symbol = else button - [ class "self-end button button-primary" ] + [ onClick (ClaimConfirmationOpen action) + , class "self-end button button-primary" + ] [ span [ class "inline-block align-middle" ] [ text "Claim" ] ] diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index ad7a94ace..ca1d3c1c7 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -348,7 +348,7 @@ update msg model ({ shared } as loggedIn) = in case actionMsg of Action.ClaimConfirmationOpen action -> - updateClaimingAction (Action.init action) + updateClaimingAction (Action.initModel action) |> UR.init Action.ActionClaimed -> diff --git a/src/elm/Search.elm b/src/elm/Search.elm index ee75cfe5a..39e4bbdc8 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -1,10 +1,8 @@ module Search exposing (Model, Msg, closeSearch, init, isActive, subscriptions, update, viewBody, viewForm, viewRecentQueries) -import Action +import Action exposing (Action) import Api.Graphql import Cambiatus.Object -import Cambiatus.Object.Action -import Cambiatus.Object.Objective import Cambiatus.Object.Product import Cambiatus.Object.SearchResult import Cambiatus.Query @@ -12,8 +10,8 @@ import Eos exposing (Symbol) import Graphql.Http import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, a, br, button, div, h3, i, img, input, li, p, span, strong, text, ul) -import Html.Attributes exposing (class, href, placeholder, src, type_, value) +import Html exposing (Html, br, button, div, h3, i, img, input, li, p, span, strong, text, ul) +import Html.Attributes exposing (class, placeholder, src, type_, value) import Html.Events exposing (onClick, onFocus, onInput, onSubmit) import Icons import Json.Decode as Decode exposing (list, string) @@ -34,6 +32,7 @@ type alias Model = , queryText : String , selectedCommunity : Symbol , found : Maybe SearchResult + , actionToClaim : Maybe Action.Model } @@ -44,6 +43,7 @@ init selectedCommunity = , queryText = "" , selectedCommunity = selectedCommunity , found = Nothing + , actionToClaim = Nothing } @@ -63,12 +63,12 @@ type FoundItemsKind type alias SearchResult = - { offers : List FoundOffer - , actions : List FoundAction + { offers : List Offer + , actions : List Action } -type alias FoundOffer = +type alias Offer = { id : Int , title : String , price : Float @@ -76,15 +76,6 @@ type alias FoundOffer = } -type alias FoundAction = - { id : Int - , objectiveId : Int - , description : String - , reward : Float - , hasProofPhoto : Bool - } - - -- GRAPHQL @@ -105,39 +96,18 @@ searchResultSelectionSet : String -> SelectionSet SearchResult Cambiatus.Object. searchResultSelectionSet queryString = SelectionSet.succeed SearchResult |> with (Cambiatus.Object.SearchResult.products (\_ -> { query = Present queryString }) offersSelectionSet) - |> with (Cambiatus.Object.SearchResult.actions (\_ -> { query = Present queryString }) actionsSelectionSet) + |> with (Cambiatus.Object.SearchResult.actions (\_ -> { query = Present queryString }) Action.selectionSet) -offersSelectionSet : SelectionSet FoundOffer Cambiatus.Object.Product +offersSelectionSet : SelectionSet Offer Cambiatus.Object.Product offersSelectionSet = - SelectionSet.map4 FoundOffer + SelectionSet.map4 Offer Cambiatus.Object.Product.id Cambiatus.Object.Product.title Cambiatus.Object.Product.price Cambiatus.Object.Product.image -type alias Objective = - { id : Int - } - - -objectiveSelectionSet : SelectionSet Objective Cambiatus.Object.Objective -objectiveSelectionSet = - SelectionSet.map Objective Cambiatus.Object.Objective.id - - -actionsSelectionSet : SelectionSet FoundAction Cambiatus.Object.Action -actionsSelectionSet = - SelectionSet.succeed FoundAction - |> with Cambiatus.Object.Action.id - -- TODO: simplify retrieving the objective id - |> with (SelectionSet.map (\s -> s.id) (Cambiatus.Object.Action.objective objectiveSelectionSet)) - |> with Cambiatus.Object.Action.description - |> with Cambiatus.Object.Action.reward - |> with (SelectionSet.map (Maybe.withDefault False) Cambiatus.Object.Action.hasProofPhoto) - - -- UPDATE @@ -162,7 +132,30 @@ update : Shared -> Model -> Msg -> ( Model, Cmd Msg ) update shared model msg = case msg of GotActionMsg actionMsg -> - ( { model | state = Inactive }, Cmd.none ) + let + updateClaimingAction : Action.Model -> Model + updateClaimingAction actionModel = + { model + | actionToClaim = + Action.update shared.translators actionMsg actionModel + |> Just + } + in + case actionMsg of + Action.ClaimConfirmationOpen action -> + let + _ = + Debug.log "open" action + in + ( updateClaimingAction (Action.initModel action) + , Cmd.none + ) + + Action.ActionWithPhotoLinkClicked -> + ( { model | state = Inactive }, Cmd.none ) + + _ -> + ( model, Cmd.none ) FoundItemClicked route -> let @@ -385,7 +378,7 @@ viewTabs results activeTab = viewOffers : Symbol -> SearchResult -> List (Html Msg) viewOffers symbol ({ offers, actions } as results) = let - viewOffer : FoundOffer -> Html Msg + viewOffer : Offer -> Html Msg viewOffer offer = li [ class "flex px-2 w-1/2 sm:w-1/3 md:w-1/4" ] @@ -416,31 +409,32 @@ viewOffers symbol ({ offers, actions } as results) = ] -viewActions : Symbol -> SearchResult -> List (Html Msg) -viewActions symbol ({ actions, offers } as results) = - let - viewAction action = - li [ class "relative mb-10 w-full sm:px-2 sm:w-1/2 lg:w-1/3" ] - [ i [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full fill-green" ] - , div [ class "px-4 pt-8 pb-4 text-sm font-light bg-purple-500 rounded-lg text-white" ] - [ p [ class "mb-8" ] [ text action.description ] - , div [ class "flex justify-between" ] - [ p [] - [ text "You gain" - , br [] [] - , span [ class "text-green font-medium" ] [ text <| String.fromFloat action.reward ] - , text " " - , text <| Eos.symbolToSymbolCodeString symbol - ] - , Action.viewClaimButton action symbol - |> Html.map GotActionMsg - ] +viewAction : Symbol -> Action -> Html Msg +viewAction symbol action = + li [ class "relative mb-10 w-full sm:px-2 sm:w-1/2 lg:w-1/3" ] + [ i [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full fill-green" ] + , div [ class "px-4 pt-8 pb-4 text-sm font-light bg-purple-500 rounded-lg text-white" ] + [ p [ class "mb-8" ] [ text action.description ] + , div [ class "flex justify-between" ] + [ p [] + [ text "You gain" + , br [] [] + , span [ class "text-green font-medium" ] [ text <| String.fromFloat action.reward ] + , text " " + , text <| Eos.symbolToSymbolCodeString symbol ] + , Action.viewClaimButton action symbol + |> Html.map GotActionMsg ] - in + ] + ] + + +viewActions : Symbol -> SearchResult -> List (Html Msg) +viewActions symbol ({ actions } as results) = [ viewTabs results Actions , ul [ class "flex px-4 sm:px-2 pt-12 flex-wrap justify-left" ] - (List.map viewAction actions) + (List.map (viewAction symbol) actions) ] From 4448f6d342679a17e97a02b383b6a6e8f36a1138 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 4 Feb 2021 12:15:32 +0300 Subject: [PATCH 40/91] Cleanup search module --- src/elm/Search.elm | 89 ++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index 39e4bbdc8..3cc26f3c3 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -375,8 +375,8 @@ viewTabs results activeTab = ] -viewOffers : Symbol -> SearchResult -> List (Html Msg) -viewOffers symbol ({ offers, actions } as results) = +viewOffers : Symbol -> List Offer -> Html Msg +viewOffers symbol offers = let viewOffer : Offer -> Html Msg viewOffer offer = @@ -403,42 +403,37 @@ viewOffers symbol ({ offers, actions } as results) = ] ] in - [ viewTabs results Offers - , ul [ class "offers-list flex flex-wrap mt-6 mb-8 mx-2 justify-left" ] + ul [ class "offers-list flex flex-wrap mt-6 mb-8 mx-2 justify-left" ] (List.map viewOffer offers) - ] - - -viewAction : Symbol -> Action -> Html Msg -viewAction symbol action = - li [ class "relative mb-10 w-full sm:px-2 sm:w-1/2 lg:w-1/3" ] - [ i [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full fill-green" ] - , div [ class "px-4 pt-8 pb-4 text-sm font-light bg-purple-500 rounded-lg text-white" ] - [ p [ class "mb-8" ] [ text action.description ] - , div [ class "flex justify-between" ] - [ p [] - [ text "You gain" - , br [] [] - , span [ class "text-green font-medium" ] [ text <| String.fromFloat action.reward ] - , text " " - , text <| Eos.symbolToSymbolCodeString symbol - ] - , Action.viewClaimButton action symbol - |> Html.map GotActionMsg - ] - ] - ] -viewActions : Symbol -> SearchResult -> List (Html Msg) -viewActions symbol ({ actions } as results) = - [ viewTabs results Actions - , ul [ class "flex px-4 sm:px-2 pt-12 flex-wrap justify-left" ] - (List.map (viewAction symbol) actions) - ] +viewActions : Symbol -> List Action -> Html Msg +viewActions symbol actions = + let + viewAction action = + li [ class "relative mb-10 w-full sm:px-2 sm:w-1/2 lg:w-1/3" ] + [ i [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full fill-green" ] + , div [ class "px-4 pt-8 pb-4 text-sm font-light bg-purple-500 rounded-lg text-white" ] + [ p [ class "mb-8" ] [ text action.description ] + , div [ class "flex justify-between" ] + [ p [] + [ text "You gain" + , br [] [] + , span [ class "text-green font-medium" ] [ text <| String.fromFloat action.reward ] + , text " " + , text <| Eos.symbolToSymbolCodeString symbol + ] + , Action.viewClaimButton action symbol + |> Html.map GotActionMsg + ] + ] + ] + in + ul [ class "flex px-4 sm:px-2 pt-12 flex-wrap justify-left" ] + (List.map viewAction actions) -viewResultsOverview : SearchResult -> List (Html Msg) +viewResultsOverview : SearchResult -> Html Msg viewResultsOverview { offers, actions } = let viewItem icon count singular plural showMsg = @@ -473,28 +468,36 @@ viewResultsOverview { offers, actions } = [ text "Show" ] ] in - [ strong [ class "block py-4" ] [ text "Here is what we found" ] - , ul [] - [ viewItem Icons.shop (List.length offers) "offer" "offers" (TabActivated Offers) - , viewItem Icons.flag (List.length actions) "action" "actions" (TabActivated Actions) + div [] + [ strong [ class "block py-4" ] [ text "Here is what we found" ] + , ul [] + [ viewItem Icons.shop (List.length offers) "offer" "offers" (TabActivated Offers) + , viewItem Icons.flag (List.length actions) "action" "actions" (TabActivated Actions) + ] ] - ] viewResults : Symbol -> State -> SearchResult -> Html Msg viewResults symbol state results = let - wrapWithClass c = + wrapWithClass c content = div [ class ("flex-grow " ++ c) ] + [ content ] in case state of ResultsShowed Offers -> - viewOffers symbol results - |> wrapWithClass "bg-gray-100" + div [] + [ viewTabs results Offers + , viewOffers symbol results.offers + |> wrapWithClass "bg-gray-100" + ] ResultsShowed Actions -> - viewActions symbol results - |> wrapWithClass "bg-gray-100" + div [] + [ viewTabs results Actions + , viewActions symbol results.actions + |> wrapWithClass "bg-gray-100" + ] _ -> viewResultsOverview results From 48e6f89e5c250c1dd4ddda78bd68743e23716231 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 4 Feb 2021 12:35:04 +0300 Subject: [PATCH 41/91] Make search work with global actions --- src/elm/Action.elm | 30 ++++++- src/elm/Search.elm | 143 ++++++++++--------------------- src/elm/Session/LoggedIn.elm | 157 ++++++++++++++++++++++++++++++++--- 3 files changed, 220 insertions(+), 110 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 6f5818cae..dc35b520b 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -13,6 +13,7 @@ module Action exposing , update , viewClaimButton , viewClaimConfirmation + , viewSearchActions ) import Cambiatus.Enum.VerificationType exposing (VerificationType) @@ -20,10 +21,10 @@ import Cambiatus.Object import Cambiatus.Object.Action as ActionObject import Cambiatus.Object.Objective import Cambiatus.Scalar exposing (DateTime) -import Eos +import Eos exposing (Symbol) import Eos.Account as Eos exposing (Name) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, a, button, div, span, text) +import Html exposing (Html, a, br, button, div, i, li, p, span, text, ul) import Html.Attributes exposing (class, classList, disabled) import Html.Events exposing (onClick) import Icons @@ -333,3 +334,28 @@ viewClaimButton action symbol = ] [ span [ class "inline-block align-middle" ] [ text "Claim" ] ] + + +viewSearchActions : Symbol -> List Action -> Html Msg +viewSearchActions symbol actions = + let + viewAction action = + li [ class "relative mb-10 w-full sm:px-2 sm:w-1/2 lg:w-1/3" ] + [ i [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full fill-green" ] + , div [ class "px-4 pt-8 pb-4 text-sm font-light bg-purple-500 rounded-lg text-white" ] + [ p [ class "mb-8" ] [ text action.description ] + , div [ class "flex justify-between" ] + [ p [] + [ text "You gain" + , br [] [] + , span [ class "text-green font-medium" ] [ text <| String.fromFloat action.reward ] + , text " " + , text <| Eos.symbolToSymbolCodeString symbol + ] + , viewClaimButton action symbol + ] + ] + ] + in + ul [ class "flex px-4 sm:px-2 pt-12 flex-wrap justify-left" ] + (List.map viewAction actions) diff --git a/src/elm/Search.elm b/src/elm/Search.elm index 3cc26f3c3..7b9ce2e11 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -1,4 +1,20 @@ -module Search exposing (Model, Msg, closeSearch, init, isActive, subscriptions, update, viewBody, viewForm, viewRecentQueries) +module Search exposing + ( FoundItemsKind(..) + , Model + , Msg + , State(..) + , closeSearch + , init + , isActive + , subscriptions + , update + , viewEmptyResults + , viewForm + , viewOffers + , viewRecentQueries + , viewResultsOverview + , viewTabs + ) import Action exposing (Action) import Api.Graphql @@ -289,6 +305,7 @@ viewForm model = ] +viewEmptyResults : String -> Html msg viewEmptyResults queryText = div [ class "flex-grow bg-white text-center" ] [ h3 [ class "mt-20 text-xl font-bold" ] @@ -304,23 +321,6 @@ viewEmptyResults queryText = ] -viewBody : Model -> Html Msg -viewBody model = - div [ class "container mx-auto flex flex-grow" ] - [ case model.found of - Just ({ actions, offers } as results) -> - case ( List.length actions, List.length offers ) of - ( 0, 0 ) -> - viewEmptyResults model.queryText - - _ -> - viewResults model.selectedCommunity model.state results - - Nothing -> - viewRecentQueries model - ] - - viewRecentQueries : Model -> Html Msg viewRecentQueries model = let @@ -375,64 +375,6 @@ viewTabs results activeTab = ] -viewOffers : Symbol -> List Offer -> Html Msg -viewOffers symbol offers = - let - viewOffer : Offer -> Html Msg - viewOffer offer = - li - [ class "flex px-2 w-1/2 sm:w-1/3 md:w-1/4" ] - [ div - [ class "rounded-md overflow-hidden bg-white flex-grow mb-4 pb-4 cursor-pointer hover:shadow" - , onClick (FoundItemClicked (Route.ViewSale (String.fromInt offer.id))) - ] - [ case offer.image of - Nothing -> - text "" - - Just url -> - img [ src url ] [] - , h3 [ class "p-3" ] [ text offer.title ] - , p [ class "px-3 leading-none" ] - [ span [ class "text-xl text-green font-medium" ] [ text <| String.fromFloat offer.price ] - , br [] [] - , span [ class "text-gray-300 text-xs" ] - [ text <| Eos.symbolToSymbolCodeString symbol - ] - ] - ] - ] - in - ul [ class "offers-list flex flex-wrap mt-6 mb-8 mx-2 justify-left" ] - (List.map viewOffer offers) - - -viewActions : Symbol -> List Action -> Html Msg -viewActions symbol actions = - let - viewAction action = - li [ class "relative mb-10 w-full sm:px-2 sm:w-1/2 lg:w-1/3" ] - [ i [ class "absolute top-0 left-0 right-0 -mt-6" ] [ Icons.flag "w-full fill-green" ] - , div [ class "px-4 pt-8 pb-4 text-sm font-light bg-purple-500 rounded-lg text-white" ] - [ p [ class "mb-8" ] [ text action.description ] - , div [ class "flex justify-between" ] - [ p [] - [ text "You gain" - , br [] [] - , span [ class "text-green font-medium" ] [ text <| String.fromFloat action.reward ] - , text " " - , text <| Eos.symbolToSymbolCodeString symbol - ] - , Action.viewClaimButton action symbol - |> Html.map GotActionMsg - ] - ] - ] - in - ul [ class "flex px-4 sm:px-2 pt-12 flex-wrap justify-left" ] - (List.map viewAction actions) - - viewResultsOverview : SearchResult -> Html Msg viewResultsOverview { offers, actions } = let @@ -477,31 +419,36 @@ viewResultsOverview { offers, actions } = ] -viewResults : Symbol -> State -> SearchResult -> Html Msg -viewResults symbol state results = +viewOffers : Symbol -> List Offer -> Html Msg +viewOffers symbol offers = let - wrapWithClass c content = - div [ class ("flex-grow " ++ c) ] - [ content ] - in - case state of - ResultsShowed Offers -> - div [] - [ viewTabs results Offers - , viewOffers symbol results.offers - |> wrapWithClass "bg-gray-100" - ] + viewOffer : Offer -> Html Msg + viewOffer offer = + li + [ class "flex px-2 w-1/2 sm:w-1/3 md:w-1/4" ] + [ div + [ class "rounded-md overflow-hidden bg-white flex-grow mb-4 pb-4 cursor-pointer hover:shadow" + , onClick (FoundItemClicked (Route.ViewSale (String.fromInt offer.id))) + ] + [ case offer.image of + Nothing -> + text "" - ResultsShowed Actions -> - div [] - [ viewTabs results Actions - , viewActions symbol results.actions - |> wrapWithClass "bg-gray-100" + Just url -> + img [ src url ] [] + , h3 [ class "p-3" ] [ text offer.title ] + , p [ class "px-3 leading-none" ] + [ span [ class "text-xl text-green font-medium" ] [ text <| String.fromFloat offer.price ] + , br [] [] + , span [ class "text-gray-300 text-xs" ] + [ text <| Eos.symbolToSymbolCodeString symbol + ] + ] + ] ] - - _ -> - viewResultsOverview results - |> wrapWithClass "bg-white p-4" + in + ul [ class "offers-list flex flex-wrap mt-6 mb-8 mx-2 justify-left" ] + (List.map viewOffer offers) isActive : Model -> Bool diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 246c3c0f7..2ad8b8c45 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -27,6 +27,7 @@ module Session.LoggedIn exposing , viewFooter ) +import Action import Api.Graphql import Auth import Avatar @@ -47,16 +48,16 @@ import Html exposing (Html, a, button, div, footer, img, nav, p, span, text) import Html.Attributes exposing (class, classList, src, style, type_) import Html.Events exposing (onClick, onMouseEnter) import Http -import I18Next exposing (Delims(..), Translations, t) +import I18Next exposing (Delims(..), Translations) import Icons import Json.Decode as Decode exposing (Value) import Json.Encode as Encode exposing (Value) import List.Extra as List import Notification exposing (Notification) -import Ports +import Ports exposing (JavascriptOutModel) import Profile exposing (Model) import Route exposing (Route) -import Search +import Search exposing (FoundItemsKind(..), State(..)) import Session.Shared as Shared exposing (Shared) import Shop import Task @@ -147,6 +148,7 @@ type alias Model = , hasObjectives : FeatureStatus , hasKyc : FeatureStatus , searchModel : Search.Model + , actionToClaim : Maybe Action.Model } @@ -175,6 +177,7 @@ initModel shared authModel accountName selectedCommunity = , hasObjectives = FeatureLoading , hasKyc = FeatureLoading , searchModel = Search.init selectedCommunity + , actionToClaim = Nothing } @@ -308,6 +311,15 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = ([ div [ class "bg-white" ] [ div [ class "container mx-auto" ] [ viewHeader model profile_ |> Html.map thisMsg + , case model.actionToClaim of + Just ca -> + Action.viewClaimConfirmation + shared.translators + ca.claimConfirmationModalStatus + |> Html.map (thisMsg << GotActionMsg) + + Nothing -> + text "" , -- Search form is separated from search results because it needs to -- be between community selector and user dropdown on Desktops. Search.viewForm model.searchModel @@ -321,8 +333,46 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = ] ] ++ (if Search.isActive model.searchModel then - [ Search.viewBody model.searchModel - |> Html.map (GotSearchMsg >> thisMsg) + [ div [ class "container mx-auto flex flex-grow" ] + [ case model.searchModel.found of + Just ({ actions, offers } as results) -> + case ( List.length actions, List.length offers ) of + ( 0, 0 ) -> + Search.viewEmptyResults model.searchModel.queryText + |> Html.map (GotSearchMsg >> thisMsg) + + _ -> + let + wrapWithClass c inner = + div [ class ("flex-grow " ++ c) ] + [ inner ] + in + case model.searchModel.state of + ResultsShowed Offers -> + div [] + [ Search.viewTabs results Offers + , Search.viewOffers model.selectedCommunity results.offers + |> wrapWithClass "bg-gray-100" + ] + |> Html.map (GotSearchMsg >> thisMsg) + + ResultsShowed Actions -> + div [] + [ Search.viewTabs results Actions + |> Html.map (GotSearchMsg >> thisMsg) + , Action.viewSearchActions model.selectedCommunity results.actions + |> wrapWithClass "bg-gray-100" + |> Html.map (GotActionMsg >> thisMsg) + ] + + _ -> + Search.viewResultsOverview results + |> wrapWithClass "bg-white p-4" + |> Html.map (GotSearchMsg >> thisMsg) + + Nothing -> + Search.viewRecentQueries model.searchModel |> Html.map (GotSearchMsg >> thisMsg) + ] ] else @@ -723,7 +773,7 @@ type ExternalMsg type Msg - = Ignored + = NoOp | CompletedLoadTranslation String (Result Http.Error Translations) | ClickedTryAgainTranslation | CompletedLoadProfile (Result (Graphql.Http.Error (Maybe Profile.Model)) (Maybe Profile.Model)) @@ -744,6 +794,7 @@ type Msg | SelectCommunity Symbol (Cmd Msg) | HideFeedbackLocal | GotSearchMsg Search.Msg + | GotActionMsg Action.Msg | SearchClosed @@ -753,14 +804,17 @@ update msg model = shared = model.shared + { t, tr } = + shared.translators + focusMainContent b alternative = if b then Dom.focus "main-content" - |> Task.attempt (\_ -> Ignored) + |> Task.attempt (\_ -> NoOp) else Dom.focus alternative - |> Task.attempt (\_ -> Ignored) + |> Task.attempt (\_ -> NoOp) closeAllModals = { model @@ -771,9 +825,85 @@ update msg model = } in case msg of - Ignored -> + NoOp -> UR.init model + GotActionMsg actionMsg -> + let + _ = + Debug.log "actionMsg from logged in" actionMsg + + updateClaimingAction : Action.Model -> Model + updateClaimingAction actionModel = + { model + | actionToClaim = + Action.update shared.translators actionMsg actionModel + |> Just + } + in + case actionMsg of + Action.ClaimConfirmationOpen action -> + updateClaimingAction (Action.initModel action) + |> UR.init + + Action.ActionClaimed -> + case model.actionToClaim of + Just ca -> + let + claimPort : JavascriptOutModel Msg + claimPort = + Action.claimActionPort + (GotActionMsg Action.ActionClaimed) + ca.action + shared.contracts.community + model.accountName + in + if isAuth model then + updateClaimingAction ca + |> UR.init + |> UR.addPort claimPort + + else + updateClaimingAction ca + |> UR.init + + -- TODO: !!! Maybe return UpdateResult from Action.view? + --|> UR.addExt (RequiredAuthentication (Just (GotActionMsg Action.ActionClaimed))) + Nothing -> + model + |> UR.init + + Action.GotActionClaimedResponse resp -> + case ( model.actionToClaim, resp ) of + ( Just ca, Ok _ ) -> + let + message = + tr "dashboard.check_claim.success" + [ ( "symbolCode", Eos.symbolToSymbolCodeString model.selectedCommunity ) ] + in + updateClaimingAction ca + |> (\m -> { m | feedback = Show Success message }) + |> UR.init + + ( Just ca, Err _ ) -> + updateClaimingAction ca + |> (\m -> { m | feedback = Show Failure (t "dashboard.check_claim.failure") }) + |> UR.init + + ( Nothing, _ ) -> + model + |> UR.init + + _ -> + case model.actionToClaim of + Just ca -> + updateClaimingAction ca + |> UR.init + + Nothing -> + model + |> UR.init + SearchClosed -> { model | searchModel = @@ -1084,6 +1214,10 @@ jsAddressToMsg addr val = |> Result.map CompletedLoadUnread |> Result.toMaybe + "GotActionMsg" :: remainAddress -> + Action.jsAddressToMsg remainAddress val + |> Maybe.map GotActionMsg + _ -> Nothing @@ -1091,7 +1225,7 @@ jsAddressToMsg addr val = msgToString : Msg -> List String msgToString msg = case msg of - Ignored -> + NoOp -> [ "Ignored" ] SearchClosed -> @@ -1100,6 +1234,9 @@ msgToString msg = GotSearchMsg _ -> [ "GotSearchMsg" ] + GotActionMsg actionMsg -> + "GotActionMsg" :: Action.msgToString actionMsg + CompletedLoadTranslation _ r -> [ "CompletedLoadTranslation", UR.resultToString r ] From 07d8de7a684c5a4a10440cdaa9d82ed5191646eb Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 4 Feb 2021 13:21:51 +0300 Subject: [PATCH 42/91] Make claim with photo link work on community page --- src/elm/Action.elm | 38 +++++------ src/elm/Page/Community.elm | 118 ++++++++--------------------------- src/elm/Search.elm | 4 +- src/elm/Session/LoggedIn.elm | 17 +++-- 4 files changed, 60 insertions(+), 117 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index dc35b520b..67dadfa4f 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -6,7 +6,7 @@ module Action exposing , claimActionPort , encodeClaimAction , getClaimWithPhotoRoute - , initModel + , initClaimingActionModel , jsAddressToMsg , msgToString , selectionSet @@ -84,9 +84,9 @@ type alias ObjectiveId = Int -initModel : Action -> Model -initModel action = - { claimConfirmationModalStatus = Closed +initClaimingActionModel : Action -> Model +initClaimingActionModel action = + { claimConfirmationModalStatus = Open action , action = action } @@ -97,7 +97,7 @@ type Msg | ClaimConfirmationClosed | ActionClaimed | GotActionClaimedResponse (Result Value String) - | ActionWithPhotoLinkClicked + | ActionWithPhotoLinkClicked Route.Route getClaimWithPhotoRoute : Eos.Symbol -> Int -> Int -> Route.Route @@ -108,8 +108,8 @@ getClaimWithPhotoRoute community objectiveId actionId = actionId -viewClaimConfirmation : Translators -> ClaimConfirmationModalStatus -> Html Msg -viewClaimConfirmation { t } claimConfirmationModalStatus = +viewClaimConfirmation : Eos.Symbol -> Translators -> ClaimConfirmationModalStatus -> Html Msg +viewClaimConfirmation symbol { t } claimConfirmationModalStatus = let text_ s = text (t s) @@ -161,7 +161,7 @@ viewClaimConfirmation { t } claimConfirmationModalStatus = if action.hasProofPhoto then --OpenProofSection action -- TODO: Go to the Claim with Photo page - NoOp + ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objectiveId action.id) else ActionClaimed @@ -183,26 +183,26 @@ update : Translators -> Msg -> Model -> Model update { t } msg model = case msg of -- TODO: this update function looks to simple to have it here... + NoOp -> + model + ClaimConfirmationOpen action -> { model | claimConfirmationModalStatus = Open action } - ClaimConfirmationClosed -> - { model | claimConfirmationModalStatus = Closed } - ActionClaimed -> { model | claimConfirmationModalStatus = InProgress } + ClaimConfirmationClosed -> + { model | claimConfirmationModalStatus = Closed } + GotActionClaimedResponse (Ok _) -> { model | claimConfirmationModalStatus = Closed } GotActionClaimedResponse (Err _) -> { model | claimConfirmationModalStatus = Closed } - ActionWithPhotoLinkClicked -> - model - - NoOp -> - model + ActionWithPhotoLinkClicked _ -> + { model | claimConfirmationModalStatus = Closed } claimActionPort : msg -> Action -> String -> Name -> Ports.JavascriptOutModel msg @@ -263,7 +263,7 @@ msgToString msg = ActionClaimed -> [ "ClaimAction" ] - ActionWithPhotoLinkClicked -> + ActionWithPhotoLinkClicked _ -> [ "ActionWithPhotoLinkClicked" ] GotActionClaimedResponse r -> @@ -319,8 +319,8 @@ viewClaimButton action symbol = -- TODO: Handle action.deadline and action.isCompleted. if action.hasProofPhoto then a - [ Route.href (getClaimWithPhotoRoute symbol action.objectiveId action.id) - , onClick ActionWithPhotoLinkClicked + [ --Route.href (getClaimWithPhotoRoute symbol action.objectiveId action.id) + onClick (ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objectiveId action.id)) , class "self-end button button-primary" ] [ span [ class "inline-block w-4 align-middle mr-2" ] [ Icons.camera "" ] diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index ca1d3c1c7..da174bacd 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -52,7 +52,6 @@ initModel _ _ = { date = Nothing , pageStatus = Loading , openObjective = Nothing - , actionToClaim = Nothing } @@ -71,7 +70,6 @@ subscriptions _ = type alias Model = { date : Maybe Posix - , actionToClaim : Maybe Action.Model , pageStatus : PageStatus , openObjective : Maybe Int } @@ -137,17 +135,7 @@ view loggedIn model = , div [ class "container mx-auto" ] [ if community.hasObjectives then div [ class "px-4 pb-4" ] - [ Html.map GotActionMsg - (case model.actionToClaim of - Just ca -> - Action.viewClaimConfirmation - loggedIn.shared.translators - ca.claimConfirmationModalStatus - - Nothing -> - text "" - ) - , div [ class "container bg-white py-6 sm:py-8 px-3 sm:px-6 rounded-lg mt-4" ] + [ div [ class "container bg-white py-6 sm:py-8 px-3 sm:px-6 rounded-lg mt-4" ] (Page.viewTitle (t "community.objectives.title_plural") :: List.indexedMap (viewObjective loggedIn model community) community.objectives @@ -320,7 +308,7 @@ type Msg -- Objective | ClickedOpenObjective Int | ClickedCloseObjective - | GotActionMsg Action.Msg + | GotCommunityActionMsg Action.Msg update : Msg -> Model -> LoggedIn.Model -> UpdateResult @@ -336,80 +324,30 @@ update msg model ({ shared } as loggedIn) = GotTime date -> UR.init { model | date = Just date } - GotActionMsg actionMsg -> + GotCommunityActionMsg actionMsg -> let - updateClaimingAction : Action.Model -> Model - updateClaimingAction actionModel = - { model - | actionToClaim = - Action.update shared.translators actionMsg actionModel - |> Just - } - in - case actionMsg of - Action.ClaimConfirmationOpen action -> - updateClaimingAction (Action.initModel action) - |> UR.init - - Action.ActionClaimed -> - case model.actionToClaim of - Just ca -> - let - claimPort : JavascriptOutModel Msg - claimPort = - Action.claimActionPort - (GotActionMsg Action.ActionClaimed) - ca.action - shared.contracts.community - loggedIn.accountName - in - if LoggedIn.isAuth loggedIn then - updateClaimingAction ca - |> UR.init - |> UR.addPort claimPort - - else - updateClaimingAction ca - |> UR.init - |> UR.addExt - (Just (GotActionMsg Action.ActionClaimed) - |> RequiredAuthentication - ) + loggedInWithUpdatedClaimingAction = + case loggedIn.searchModel.actionToClaim of + Just a -> + { loggedIn + | actionToClaim = + Action.update shared.translators actionMsg a + |> Just + } Nothing -> - model - |> UR.init - - Action.GotActionClaimedResponse resp -> - case ( model.actionToClaim, resp ) of - ( Just ca, Ok _ ) -> - let - message = - shared.translators.tr "dashboard.check_claim.success" - [ ( "symbolCode", Eos.symbolToSymbolCodeString loggedIn.selectedCommunity ) ] - in - updateClaimingAction ca - |> UR.init - |> UR.addExt (ShowFeedback LoggedIn.Success message) - - ( Just ca, Err _ ) -> - updateClaimingAction ca - |> UR.init - |> UR.addExt (ShowFeedback LoggedIn.Failure (t "dashboard.check_claim.failure")) - - ( Nothing, _ ) -> - model - |> UR.init - - _ -> - case model.actionToClaim of - Just ca -> - updateClaimingAction ca - |> UR.init - - Nothing -> - model - |> UR.init + case actionMsg of + Action.ClaimConfirmationOpen a -> + { loggedIn + | actionToClaim = Just (Action.initClaimingActionModel a) + } + + _ -> + loggedIn + in + model + |> UR.init + |> UR.addExt (UpdatedLoggedIn loggedInWithUpdatedClaimingAction) CompletedLoadCommunity (Ok community) -> case community of @@ -440,10 +378,6 @@ update msg model ({ shared } as loggedIn) = jsAddressToMsg : List String -> Value -> Maybe Msg jsAddressToMsg addr val = case addr of - "GotActionMsg" :: remainAddress -> - Action.jsAddressToMsg remainAddress val - |> Maybe.map GotActionMsg - _ -> Nothing @@ -457,8 +391,8 @@ msgToString msg = GotTime _ -> [ "GotTime" ] - GotActionMsg actionMsg -> - "GotActionMsg" :: Action.msgToString actionMsg + GotCommunityActionMsg _ -> + [ "GotCommunityActionMsg" ] CompletedLoadCommunity r -> [ "CompletedLoadCommunity", UR.resultToString r ] @@ -598,7 +532,7 @@ viewAction translators canEdit symbol maybeDate action = NoOp else - (GotActionMsg << Action.ClaimConfirmationOpen) action + (GotCommunityActionMsg << Action.ClaimConfirmationOpen) action ) ] [ if action.hasProofPhoto then diff --git a/src/elm/Search.elm b/src/elm/Search.elm index 7b9ce2e11..e6f2daef2 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -163,11 +163,11 @@ update shared model msg = _ = Debug.log "open" action in - ( updateClaimingAction (Action.initModel action) + ( updateClaimingAction (Action.initClaimingActionModel action) , Cmd.none ) - Action.ActionWithPhotoLinkClicked -> + Action.ActionWithPhotoLinkClicked _ -> ( { model | state = Inactive }, Cmd.none ) _ -> diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 2ad8b8c45..0d8088e8e 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -314,6 +314,7 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = , case model.actionToClaim of Just ca -> Action.viewClaimConfirmation + model.selectedCommunity shared.translators ca.claimConfirmationModalStatus |> Html.map (thisMsg << GotActionMsg) @@ -830,9 +831,6 @@ update msg model = GotActionMsg actionMsg -> let - _ = - Debug.log "actionMsg from logged in" actionMsg - updateClaimingAction : Action.Model -> Model updateClaimingAction actionModel = { model @@ -843,7 +841,7 @@ update msg model = in case actionMsg of Action.ClaimConfirmationOpen action -> - updateClaimingAction (Action.initModel action) + updateClaimingAction (Action.initClaimingActionModel action) |> UR.init Action.ActionClaimed -> @@ -873,6 +871,17 @@ update msg model = model |> UR.init + Action.ActionWithPhotoLinkClicked route -> + case model.actionToClaim of + Just ca -> + updateClaimingAction ca + |> UR.init + |> UR.addCmd (Route.replaceUrl model.shared.navKey route) + + Nothing -> + model + |> UR.init + Action.GotActionClaimedResponse resp -> case ( model.actionToClaim, resp ) of ( Just ca, Ok _ ) -> From 2c7b300f0c9a3b0d5113066589920e1adf3d406f Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 4 Feb 2021 14:20:40 +0300 Subject: [PATCH 43/91] Put back the claim description on the claim view page --- src/elm/Action.elm | 28 +++++++++-------------- src/elm/Claim.elm | 38 -------------------------------- src/elm/Page/Community.elm | 13 +++++------ src/elm/Page/Dashboard/Claim.elm | 2 +- src/elm/Search.elm | 31 +------------------------- src/elm/Session/LoggedIn.elm | 6 ++--- 6 files changed, 22 insertions(+), 96 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 67dadfa4f..0a1265914 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -11,7 +11,6 @@ module Action exposing , msgToString , selectionSet , update - , viewClaimButton , viewClaimConfirmation , viewSearchActions ) @@ -24,7 +23,7 @@ import Cambiatus.Scalar exposing (DateTime) import Eos exposing (Symbol) import Eos.Account as Eos exposing (Name) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, a, br, button, div, i, li, p, span, text, ul) +import Html exposing (Html, br, button, div, i, li, p, span, text, ul) import Html.Attributes exposing (class, classList, disabled) import Html.Events exposing (onClick) import Icons @@ -46,8 +45,8 @@ type ClaimConfirmationModalStatus type alias Action = { id : Int - , objectiveId : Int , description : String + , objective : Objective , reward : Float , verifierReward : Float , creator : Eos.Name @@ -80,10 +79,6 @@ type alias Model = } -type alias ObjectiveId = - Int - - initClaimingActionModel : Action -> Model initClaimingActionModel action = { claimConfirmationModalStatus = Open action @@ -159,9 +154,7 @@ viewClaimConfirmation symbol { t } claimConfirmationModalStatus = let acceptMsg = if action.hasProofPhoto then - --OpenProofSection action - -- TODO: Go to the Claim with Photo page - ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objectiveId action.id) + ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objective.id action.id) else ActionClaimed @@ -283,6 +276,7 @@ encodeClaimAction c = type alias Objective = { id : Int + , description : String } @@ -290,14 +284,15 @@ objectiveSelectionSet : SelectionSet Objective Cambiatus.Object.Objective objectiveSelectionSet = SelectionSet.succeed Objective |> with Cambiatus.Object.Objective.id + |> with Cambiatus.Object.Objective.description selectionSet : SelectionSet Action Cambiatus.Object.Action selectionSet = SelectionSet.succeed Action |> with ActionObject.id - |> with (SelectionSet.map (\s -> s.id) (ActionObject.objective objectiveSelectionSet)) |> with ActionObject.description + |> with (SelectionSet.map (\o -> { id = o.id, description = o.description }) (ActionObject.objective objectiveSelectionSet)) |> with ActionObject.reward |> with ActionObject.verifierReward |> with (Eos.nameSelectionSet ActionObject.creatorId) @@ -314,13 +309,12 @@ selectionSet = |> with ActionObject.position -viewClaimButton : Action -> Eos.Symbol -> Html Msg -viewClaimButton action symbol = +viewClaimButton : Action -> Html Msg +viewClaimButton action = -- TODO: Handle action.deadline and action.isCompleted. if action.hasProofPhoto then - a - [ --Route.href (getClaimWithPhotoRoute symbol action.objectiveId action.id) - onClick (ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objectiveId action.id)) + button + [ onClick (ClaimConfirmationOpen action) , class "self-end button button-primary" ] [ span [ class "inline-block w-4 align-middle mr-2" ] [ Icons.camera "" ] @@ -352,7 +346,7 @@ viewSearchActions symbol actions = , text " " , text <| Eos.symbolToSymbolCodeString symbol ] - , viewClaimButton action symbol + , viewClaimButton action ] ] ] diff --git a/src/elm/Claim.elm b/src/elm/Claim.elm index b66363493..0add2a7f4 100644 --- a/src/elm/Claim.elm +++ b/src/elm/Claim.elm @@ -22,7 +22,6 @@ module Claim exposing import Action exposing (Action) import Api.Relay exposing (Edge, PageConnection) import Cambiatus.Enum.ClaimStatus as ClaimStatus -import Cambiatus.Enum.VerificationType exposing (VerificationType(..)) import Cambiatus.Object import Cambiatus.Object.Check as Check import Cambiatus.Object.Claim as Claim @@ -83,25 +82,6 @@ type alias Check = } - ---type alias ActionFromClaimModule = --- { id : Int --- , description : String --- , reward : Float --- , verifierReward : Float --- , validators : List Profile.Minimal --- , verifications : Int --- , verificationType : VerificationType --- , objective : Objective --- , createdAt : DateTime --- , hasProofPhoto : Bool --- , hasProofCode : Bool --- , instructions : Maybe String --- } --- --- Claim Action - - isValidated : Model -> Eos.Name -> Bool isValidated claim user = claim.status /= Pending || List.any (\c -> c.validator.account == user) claim.checks @@ -205,24 +185,6 @@ claimStatusMap v = Pending - ---actionSelectionSetClaim : SelectionSet ActionFromClaimModule Cambiatus.Object.Action ---actionSelectionSetClaim = --- SelectionSet.succeed ActionFromClaimModule --- |> with ActionObject.id --- |> with ActionObject.description --- |> with ActionObject.reward --- |> with ActionObject.verifierReward --- |> with (ActionObject.validators Profile.minimalSelectionSet) --- |> with ActionObject.verifications --- |> with ActionObject.verificationType --- |> with (ActionObject.objective Community.objectiveSelectionSet) --- |> with ActionObject.createdAt --- |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofPhoto) --- |> with (SelectionSet.map (Maybe.withDefault False) ActionObject.hasProofCode) --- |> with ActionObject.photoProofInstructions - - checkSelectionSet : SelectionSet Check Cambiatus.Object.Check checkSelectionSet = SelectionSet.succeed Check diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index da174bacd..936872db1 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -22,7 +22,6 @@ import Html.Events exposing (onClick) import Icons import Json.Encode exposing (Value) import Page -import Ports exposing (JavascriptOutModel) import Route import Session.LoggedIn as LoggedIn exposing (External(..), FeedbackStatus(..)) import Session.Shared exposing (Translators) @@ -51,7 +50,7 @@ initModel : LoggedIn.Model -> Symbol -> Model initModel _ _ = { date = Nothing , pageStatus = Loading - , openObjective = Nothing + , openObjectiveId = Nothing } @@ -71,7 +70,7 @@ subscriptions _ = type alias Model = { date : Maybe Posix , pageStatus : PageStatus - , openObjective : Maybe Int + , openObjectiveId : Maybe Int } @@ -219,7 +218,7 @@ viewObjective loggedIn model metadata index objective = isOpen : Bool isOpen = - case model.openObjective of + case model.openObjectiveId of Just obj -> obj == index @@ -327,7 +326,7 @@ update msg model ({ shared } as loggedIn) = GotCommunityActionMsg actionMsg -> let loggedInWithUpdatedClaimingAction = - case loggedIn.searchModel.actionToClaim of + case loggedIn.actionToClaim of Just a -> { loggedIn | actionToClaim = @@ -367,11 +366,11 @@ update msg model ({ shared } as loggedIn) = |> UR.logGraphqlError msg err ClickedOpenObjective index -> - { model | openObjective = Just index } + { model | openObjectiveId = Just index } |> UR.init ClickedCloseObjective -> - { model | openObjective = Nothing } + { model | openObjectiveId = Nothing } |> UR.init diff --git a/src/elm/Page/Dashboard/Claim.elm b/src/elm/Page/Dashboard/Claim.elm index 3cc165fa2..126f4329f 100644 --- a/src/elm/Page/Dashboard/Claim.elm +++ b/src/elm/Page/Dashboard/Claim.elm @@ -293,7 +293,7 @@ viewDetails { shared, selectedCommunity } model claim = [ text_ "claim.objective" ] , p [ class "pt-2 text-body" ] - [ text "OBJECTIVE DESCIPTION1!!!!" --claim.action.objective.description + [ text claim.action.objective.description ] ] , div [ class "mb-8" ] diff --git a/src/elm/Search.elm b/src/elm/Search.elm index e6f2daef2..10f964687 100644 --- a/src/elm/Search.elm +++ b/src/elm/Search.elm @@ -26,7 +26,7 @@ import Eos exposing (Symbol) import Graphql.Http import Graphql.OptionalArgument exposing (OptionalArgument(..)) import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with) -import Html exposing (Html, br, button, div, h3, i, img, input, li, p, span, strong, text, ul) +import Html exposing (Html, br, button, div, h3, img, input, li, p, span, strong, text, ul) import Html.Attributes exposing (class, placeholder, src, type_, value) import Html.Events exposing (onClick, onFocus, onInput, onSubmit) import Icons @@ -48,7 +48,6 @@ type alias Model = , queryText : String , selectedCommunity : Symbol , found : Maybe SearchResult - , actionToClaim : Maybe Action.Model } @@ -59,7 +58,6 @@ init selectedCommunity = , queryText = "" , selectedCommunity = selectedCommunity , found = Nothing - , actionToClaim = Nothing } @@ -136,7 +134,6 @@ type Msg | QuerySubmitted | TabActivated FoundItemsKind | FoundItemClicked Route - | GotActionMsg Action.Msg closeSearch : Shared -> Model -> ( Model, Cmd Msg ) @@ -147,32 +144,6 @@ closeSearch shared model = update : Shared -> Model -> Msg -> ( Model, Cmd Msg ) update shared model msg = case msg of - GotActionMsg actionMsg -> - let - updateClaimingAction : Action.Model -> Model - updateClaimingAction actionModel = - { model - | actionToClaim = - Action.update shared.translators actionMsg actionModel - |> Just - } - in - case actionMsg of - Action.ClaimConfirmationOpen action -> - let - _ = - Debug.log "open" action - in - ( updateClaimingAction (Action.initClaimingActionModel action) - , Cmd.none - ) - - Action.ActionWithPhotoLinkClicked _ -> - ( { model | state = Inactive }, Cmd.none ) - - _ -> - ( model, Cmd.none ) - FoundItemClicked route -> let -- Make the search dropdown inactive before opening the found item's URL. diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 0d8088e8e..0fdb40278 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -312,11 +312,11 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = [ div [ class "container mx-auto" ] [ viewHeader model profile_ |> Html.map thisMsg , case model.actionToClaim of - Just ca -> + Just a -> Action.viewClaimConfirmation model.selectedCommunity shared.translators - ca.claimConfirmationModalStatus + a.claimConfirmationModalStatus |> Html.map (thisMsg << GotActionMsg) Nothing -> @@ -875,7 +875,7 @@ update msg model = case model.actionToClaim of Just ca -> updateClaimingAction ca - |> UR.init + |> update SearchClosed |> UR.addCmd (Route.replaceUrl model.shared.navKey route) Nothing -> From 5ab92f5f2000a86a896d9186b3b0da7d396488c8 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 4 Feb 2021 15:20:14 +0300 Subject: [PATCH 44/91] Make claim with photo page work --- src/elm/Action.elm | 2 +- src/elm/Main.elm | 16 +++-- src/elm/Page/Community/ClaimWithPhoto.elm | 86 ++++++++++++----------- src/elm/Route.elm | 6 +- src/elm/Session/LoggedIn.elm | 2 +- 5 files changed, 63 insertions(+), 49 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 0a1265914..8b941b75d 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -97,7 +97,7 @@ type Msg getClaimWithPhotoRoute : Eos.Symbol -> Int -> Int -> Route.Route getClaimWithPhotoRoute community objectiveId actionId = - Route.ClaimAction + Route.ClaimWithPhoto community objectiveId actionId diff --git a/src/elm/Main.elm b/src/elm/Main.elm index 354ce4207..68b690dc0 100755 --- a/src/elm/Main.elm +++ b/src/elm/Main.elm @@ -126,6 +126,10 @@ subscriptions model = ShopViewer.subscriptions subModel |> Sub.map GotShopViewerMsg + ClaimWithPhoto subModel -> + ClaimWithPhoto.subscriptions subModel + |> Sub.map GotClaimWithPhotoMsg + _ -> Sub.none ] @@ -263,7 +267,7 @@ update msg model = ) val in - case jsAddressResult of + case jsAddressResult |> Debug.log "jsAddressResult" of Ok jsAddress -> Maybe.map (\newMsg -> update newMsg model) @@ -825,10 +829,10 @@ changeRouteTo maybeRoute model = >> updateStatusWith ActionEditor GotActionEditorMsg model |> withLoggedIn (Route.EditAction symbol objectiveId actionId) - Just (Route.ClaimAction symbol objectiveId actionId) -> + Just (Route.ClaimWithPhoto symbol objectiveId actionId) -> (\l -> ClaimWithPhoto.init l symbol objectiveId (Just actionId)) >> updateStatusWith ClaimWithPhoto GotClaimWithPhotoMsg model - |> withLoggedIn (Route.ClaimAction symbol objectiveId actionId) + |> withLoggedIn (Route.ClaimWithPhoto symbol objectiveId actionId) Just (Route.Claim objectiveId actionId claimId) -> (\l -> Claim.init l claimId) @@ -934,6 +938,10 @@ jsAddressToMsg address val = Maybe.map GotActionEditorMsg (ActionEditor.jsAddressToMsg rAddress val) + "GotClaimWithPhotoMsg" :: rAddress -> + Maybe.map GotClaimWithPhotoMsg + (ClaimWithPhoto.jsAddressToMsg rAddress val) + "GotVerifyClaimMsg" :: rAddress -> Maybe.map GotVerifyClaimMsg (Claim.jsAddressToMsg rAddress val) @@ -994,7 +1002,7 @@ msgToString msg = "GotActionEditor" :: ActionEditor.msgToString subMsg GotClaimWithPhotoMsg subMsg -> - "GotClaimActionMsg" :: ClaimWithPhoto.msgToString subMsg + "GotClaimWithPhotoMsg" :: ClaimWithPhoto.msgToString subMsg GotVerifyClaimMsg subMsg -> "GotVerifyClaimMsg" :: Claim.msgToString subMsg diff --git a/src/elm/Page/Community/ClaimWithPhoto.elm b/src/elm/Page/Community/ClaimWithPhoto.elm index 7d2daa058..97666d2a2 100644 --- a/src/elm/Page/Community/ClaimWithPhoto.elm +++ b/src/elm/Page/Community/ClaimWithPhoto.elm @@ -35,9 +35,7 @@ import UpdateResult as UR type alias Model = - { claimConfirmationModalStatus : Action.ClaimConfirmationModalStatus - , status : Status - , action : Maybe Action -- TODO: Use result since action must be loaded + { status : Status , proof : Proof } @@ -52,14 +50,22 @@ type alias ActionId = init : LoggedIn.Model -> Symbol -> ObjectiveId -> Maybe ActionId -> ( Model, Cmd Msg ) init loggedIn symbol objectiveId actionId = - ( { claimConfirmationModalStatus = Action.Closed - , status = Loading - , action = Nothing + let + ( status, cmd ) = + case loggedIn.actionToClaim |> Debug.log "action" of + Just a -> + ( Loaded a, Task.succeed (OpenProofSection a.action) |> Task.perform identity ) + + Nothing -> + ( NotFound, Cmd.none ) + in + ( { status = status , proof = Proof NoPhotoAdded Nothing } - , Api.Graphql.query loggedIn.shared - (Community.communityQuery symbol) - CommunityLoaded + --, Api.Graphql.query loggedIn.shared + -- (Community.communityQuery symbol) + -- CommunityLoaded + , cmd ) @@ -156,12 +162,12 @@ view ({ shared } as loggedIn) model = Loading -> Page.fullPageLoading shared - Loaded community -> + Loaded _ -> div [ class "bg-white" ] - [ Page.viewHeader loggedIn (t "community.actions.title") (Route.Community community.symbol) - , case model.action of - Just a -> - viewClaimWithProofs model.proof loggedIn.shared.translators a + [ Page.viewHeader loggedIn (t "community.actions.title") (Route.Community loggedIn.selectedCommunity) + , case loggedIn.actionToClaim of + Just actionModel -> + viewClaimWithProofs model.proof loggedIn.shared.translators actionModel.action Nothing -> text "this is claim with photo page" @@ -295,7 +301,7 @@ subscriptions _ = type Status = Loading - | Loaded Community.Model + | Loaded Action.Model -- Errors | LoadFailed (Graphql.Http.Error (Maybe Community.Model)) | NotFound @@ -304,6 +310,9 @@ type Status update : Msg -> Model -> LoggedIn.Model -> UR.UpdateResult Model Msg (External Msg) update msg model ({ shared } as loggedIn) = let + _ = + Debug.log "msg" msg + { t } = shared.translators in @@ -317,7 +326,7 @@ update msg model ({ shared } as loggedIn) = case c of Just community -> { model - | status = Loaded community + | status = Loading } |> UR.init @@ -332,8 +341,8 @@ update msg model ({ shared } as loggedIn) = in if action.hasProofPhoto then { model - | claimConfirmationModalStatus = Action.Closed - , proof = Proof NoPhotoAdded Nothing + | --claimConfirmationModalStatus = Action.Closed + proof = Proof NoPhotoAdded Nothing } |> UR.init |> UR.addCmd @@ -343,15 +352,15 @@ update msg model ({ shared } as loggedIn) = else Cmd.none ) - |> UR.addPort - { responseAddress = NoOp - , responseData = Encode.null - , data = - Encode.object - [ ( "id", Encode.string "communityPage" ) - , ( "name", Encode.string "scrollIntoView" ) - ] - } + --|> UR.addPort + -- { responseAddress = NoOp + -- , responseData = Encode.null + -- , data = + -- Encode.object + -- [ ( "id", Encode.string "communityPage" ) + -- , ( "name", Encode.string "scrollIntoView" ) + -- ] + -- } else model |> UR.init @@ -399,9 +408,8 @@ update msg model ({ shared } as loggedIn) = |> UR.logHttpError msg error CloseProofSection reason -> - { model - | claimConfirmationModalStatus = Action.Closed - } + model + --claimConfirmationModalStatus = Action.Closed -- TODO: Show expired message |> UR.init |> UR.addExt @@ -417,11 +425,11 @@ update msg model ({ shared } as loggedIn) = model |> UR.init GotUint64Name (Ok uint64name) -> - case ( model.proof, model.action ) of - ( Proof proofPhoto (Just proofCode), Just action ) -> + case ( model.proof, loggedIn.actionToClaim ) of + ( Proof proofPhoto (Just proofCode), Just actionModel ) -> let verificationCode = - generateVerificationCode action.id uint64name proofCode.claimTimestamp + generateVerificationCode actionModel.action.id uint64name proofCode.claimTimestamp newProofCode = Just @@ -562,17 +570,15 @@ update msg model ({ shared } as loggedIn) = shared.translators.tr "dashboard.check_claim.success" [ ( "symbolCode", Eos.symbolToSymbolCodeString loggedIn.selectedCommunity ) ] in - { model - | claimConfirmationModalStatus = Closed - } + model + --claimConfirmationModalStatus = Closed |> UR.init |> UR.addExt (ShowFeedback LoggedIn.Success message) GotClaimActionResponse (Err _) -> -- TODO: remove duplicates - { model - | claimConfirmationModalStatus = Closed - } + model + -- claimConfirmationModalStatus = Closed |> UR.init |> UR.addExt (ShowFeedback LoggedIn.Failure (t "dashboard.check_claim.failure")) @@ -580,7 +586,7 @@ update msg model ({ shared } as loggedIn) = jsAddressToMsg : List String -> Value -> Maybe Msg jsAddressToMsg addr val = -- TODO: Remove duplicates - case addr of + case addr |> Debug.log "addr" of "ClaimAction" :: [] -> Decode.decodeValue (Decode.oneOf diff --git a/src/elm/Route.elm b/src/elm/Route.elm index c66d1b44a..5d0de5dcd 100755 --- a/src/elm/Route.elm +++ b/src/elm/Route.elm @@ -36,7 +36,7 @@ type Route | EditObjective Symbol Int | NewAction Symbol Int | EditAction Symbol Int Int - | ClaimAction Symbol Int Int + | ClaimWithPhoto Symbol Int Int | Claim Int Int Int | Shop Shop.Filter | NewSale @@ -98,7 +98,7 @@ parser url = , Url.map EditObjective (s "community" Eos.symbolUrlParser s "objectives" int s "edit") , Url.map NewAction (s "community" Eos.symbolUrlParser s "objectives" int s "action" s "new") , Url.map EditAction (s "community" Eos.symbolUrlParser s "objectives" int s "action" int s "edit") - , Url.map ClaimAction (s "community" Eos.symbolUrlParser s "objectives" int s "action" int s "claim") + , Url.map ClaimWithPhoto (s "community" Eos.symbolUrlParser s "objectives" int s "action" int s "claim") , Url.map Claim (s "objectives" int s "action" int s "claim" int) , Url.map Shop (s "shop" @@ -287,7 +287,7 @@ routeToString route = , [] ) - ClaimAction symbol objectiveId actionId -> + ClaimWithPhoto symbol objectiveId actionId -> ( [ "community", Eos.symbolToString symbol, "objectives", String.fromInt objectiveId, "action", String.fromInt actionId, "claim" ] , [] ) diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 0fdb40278..419860e56 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -148,7 +148,7 @@ type alias Model = , hasObjectives : FeatureStatus , hasKyc : FeatureStatus , searchModel : Search.Model - , actionToClaim : Maybe Action.Model + , actionToClaim : Maybe Action.Model -- TODO: Clean this action after claiming } From 777539c6d0974a33fc65236ff9dedd3af104790f Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Thu, 4 Feb 2021 18:36:45 +0300 Subject: [PATCH 45/91] Improve claim with photo page --- src/elm/Main.elm | 2 +- src/elm/Page/Community/ClaimWithPhoto.elm | 174 ++++++++++------------ 2 files changed, 78 insertions(+), 98 deletions(-) diff --git a/src/elm/Main.elm b/src/elm/Main.elm index 68b690dc0..b4313f504 100755 --- a/src/elm/Main.elm +++ b/src/elm/Main.elm @@ -267,7 +267,7 @@ update msg model = ) val in - case jsAddressResult |> Debug.log "jsAddressResult" of + case jsAddressResult of Ok jsAddress -> Maybe.map (\newMsg -> update newMsg model) diff --git a/src/elm/Page/Community/ClaimWithPhoto.elm b/src/elm/Page/Community/ClaimWithPhoto.elm index 97666d2a2..106ffd892 100644 --- a/src/elm/Page/Community/ClaimWithPhoto.elm +++ b/src/elm/Page/Community/ClaimWithPhoto.elm @@ -11,7 +11,6 @@ module Page.Community.ClaimWithPhoto exposing import Action exposing (Action, ClaimConfirmationModalStatus(..)) import Api -import Api.Graphql import Community import Eos exposing (Symbol) import Eos.Account as Eos @@ -52,9 +51,13 @@ init : LoggedIn.Model -> Symbol -> ObjectiveId -> Maybe ActionId -> ( Model, Cmd init loggedIn symbol objectiveId actionId = let ( status, cmd ) = - case loggedIn.actionToClaim |> Debug.log "action" of - Just a -> - ( Loaded a, Task.succeed (OpenProofSection a.action) |> Task.perform identity ) + case loggedIn.actionToClaim of + Just actionModel -> + if actionModel.action.hasProofPhoto then + ( Loaded actionModel, Task.succeed (OpenProofSection actionModel.action) |> Task.perform identity ) + + else + ( NotFound, Cmd.none ) Nothing -> ( NotFound, Cmd.none ) @@ -74,7 +77,7 @@ type Msg | CommunityLoaded (Result (Graphql.Http.Error (Maybe Community.Model)) (Maybe Community.Model)) | OpenProofSection Action | CloseProofSection ReasonToCloseProofSection - | GotProofTime Int Posix + | GotProofTime Posix | GetUint64Name String | GotUint64Name (Result Value String) | Tick Time.Posix @@ -162,6 +165,9 @@ view ({ shared } as loggedIn) model = Loading -> Page.fullPageLoading shared + TimeExpired -> + Page.fullPageNotFound "Time has expired" "hello subtitle" + Loaded _ -> div [ class "bg-white" ] [ Page.viewHeader loggedIn (t "community.actions.title") (Route.Community loggedIn.selectedCommunity) @@ -185,14 +191,14 @@ view ({ shared } as loggedIn) model = viewClaimWithProofs : Proof -> Translators -> Action -> Html Msg -viewClaimWithProofs proofs translators action = +viewClaimWithProofs (Proof photoStatus proofCode) translators action = let { t } = translators isUploadingInProgress = - case proofs of - Proof Uploading _ -> + case photoStatus of + Uploading -> True _ -> @@ -205,8 +211,8 @@ viewClaimWithProofs proofs translators action = [ text <| Maybe.withDefault "" action.photoProofInstructions ] - , case proofs of - Proof _ (Just { code, secondsAfterClaim, availabilityPeriod }) -> + , case proofCode of + Just { code, secondsAfterClaim, availabilityPeriod } -> case code of Just c -> viewProofCode @@ -223,9 +229,7 @@ viewClaimWithProofs proofs translators action = , div [ class "mb-4" ] [ span [ class "input-label block mb-2" ] [ text (t "community.actions.proof.photo") ] - , case proofs of - Proof photoStatus _ -> - viewPhotoUploader translators photoStatus + , viewPhotoUploader translators photoStatus ] , div [ class "md:flex" ] [ button @@ -295,23 +299,29 @@ viewProofCode { t } proofCode secondsAfterClaim proofCodeValiditySeconds = subscriptions : Model -> Sub Msg -subscriptions _ = - Time.every 1000 Tick +subscriptions model = + case model.proof of + Proof _ (Just _) -> + Time.every 1000 Tick + + _ -> + -- No proof code, no timer needed + Sub.none type Status = Loading | Loaded Action.Model - -- Errors | LoadFailed (Graphql.Http.Error (Maybe Community.Model)) | NotFound + | TimeExpired update : Msg -> Model -> LoggedIn.Model -> UR.UpdateResult Model Msg (External Msg) update msg model ({ shared } as loggedIn) = let - _ = - Debug.log "msg" msg + (Proof photoStatus proofCode) = + model.proof { t } = shared.translators @@ -337,46 +347,25 @@ update msg model ({ shared } as loggedIn) = OpenProofSection action -> let runProofCodeTimer = - Task.perform (GotProofTime action.id) Time.now - in - if action.hasProofPhoto then - { model - | --claimConfirmationModalStatus = Action.Closed - proof = Proof NoPhotoAdded Nothing - } - |> UR.init - |> UR.addCmd - (if action.hasProofCode then - runProofCodeTimer - - else - Cmd.none - ) - --|> UR.addPort - -- { responseAddress = NoOp - -- , responseData = Encode.null - -- , data = - -- Encode.object - -- [ ( "id", Encode.string "communityPage" ) - -- , ( "name", Encode.string "scrollIntoView" ) - -- ] - -- } + if action.hasProofCode then + Task.perform GotProofTime Time.now - else - model |> UR.init + else + Cmd.none + in + model + |> UR.init + |> UR.addCmd runProofCodeTimer + -- Don't show any messages at first + |> UR.addExt HideFeedback EnteredPhoto (file :: _) -> let uploadImage = Api.uploadImage shared file CompletedPhotoUpload - - newProof = - case model.proof of - Proof _ proofCode -> - Proof Uploading proofCode in { model - | proof = newProof + | proof = Proof Uploading proofCode } |> UR.init |> UR.addCmd uploadImage @@ -387,57 +376,53 @@ update msg model ({ shared } as loggedIn) = |> UR.init CompletedPhotoUpload (Ok url) -> - let - newProofs = - case model.proof of - Proof _ proofCode -> - Proof (Uploaded url) proofCode - in - { model | proof = newProofs } + { model + | proof = Proof (Uploaded url) proofCode + } |> UR.init CompletedPhotoUpload (Err error) -> - let - newProofs = - case model.proof of - Proof _ proofCode -> - Proof (UploadFailed error) proofCode - in - { model | proof = newProofs } + { model + | proof = Proof (UploadFailed error) proofCode + } |> UR.init |> UR.logHttpError msg error CloseProofSection reason -> - model - --claimConfirmationModalStatus = Action.Closed - -- TODO: Show expired message - |> UR.init - |> UR.addExt - (case reason of + let + ( status, ext ) = + case reason of TimerExpired -> - ShowFeedback LoggedIn.Failure (t "community.actions.proof.time_expired") + ( TimeExpired, ShowFeedback LoggedIn.Failure (t "community.actions.proof.time_expired") ) CancelClicked -> - HideFeedback - ) + -- TODO: Redirect to the place where the user came from + ( NotFound, HideFeedback ) + in + { model + | status = status + , proof = Proof NoPhotoAdded Nothing + } + |> UR.init + |> UR.addExt ext GetUint64Name _ -> model |> UR.init GotUint64Name (Ok uint64name) -> - case ( model.proof, loggedIn.actionToClaim ) of - ( Proof proofPhoto (Just proofCode), Just actionModel ) -> + case ( proofCode, loggedIn.actionToClaim ) of + ( Just pc, Just actionModel ) -> let verificationCode = - generateVerificationCode actionModel.action.id uint64name proofCode.claimTimestamp + generateVerificationCode actionModel.action.id uint64name pc.claimTimestamp newProofCode = Just - { proofCode + { pc | code = Just verificationCode } in - { model | proof = Proof proofPhoto newProofCode } + { model | proof = Proof photoStatus newProofCode } |> UR.init _ -> @@ -448,24 +433,24 @@ update msg model ({ shared } as loggedIn) = model |> UR.init Tick timer -> - case model.proof of - Proof proofPhoto (Just proofCode) -> + case proofCode of + Just pc -> let secondsAfterClaim = - (Time.posixToMillis timer // 1000) - proofCode.claimTimestamp + (Time.posixToMillis timer // 1000) - pc.claimTimestamp isProofCodeActive = - (proofCode.availabilityPeriod - secondsAfterClaim) > 0 + (pc.availabilityPeriod - secondsAfterClaim) > 0 in if isProofCodeActive then let newProofCode = Just - { proofCode + { pc | secondsAfterClaim = secondsAfterClaim } in - { model | proof = Proof proofPhoto newProofCode } |> UR.init + { model | proof = Proof photoStatus newProofCode } |> UR.init else update (CloseProofSection TimerExpired) model loggedIn @@ -473,14 +458,14 @@ update msg model ({ shared } as loggedIn) = _ -> model |> UR.init - GotProofTime actionId posix -> + GotProofTime posix -> let initProofCodeParts = Just { code = Nothing , claimTimestamp = Time.posixToMillis posix // 1000 , secondsAfterClaim = 0 - , availabilityPeriod = 30 * 60 + , availabilityPeriod = 5 --30 * 60 } in { model | proof = Proof NoPhotoAdded initProofCodeParts } @@ -497,22 +482,17 @@ update msg model ({ shared } as loggedIn) = ClaimAction action -> -- TODO: Remove duplication in port + -- TODO: ??? Stop timer after photo was uploaded successfully let hasPhotoError = case model.proof of Proof (Uploaded _) _ -> False - Proof _ _ -> + _ -> -- Error: photo wasn't uploaded while claiming with proof True - newModel = - case model.proof of - Proof _ _ -> - -- Claim with proof has no confirmation - model - ( proofPhotoUrl, proofCode_, proofTime ) = case model.proof of Proof (Uploaded url) (Just { code, claimTimestamp }) -> @@ -530,7 +510,7 @@ update msg model ({ shared } as loggedIn) = |> UR.addExt (ShowFeedback LoggedIn.Failure (t "community.actions.proof.no_photo_error")) else if LoggedIn.isAuth loggedIn then - newModel + model |> UR.init |> UR.addPort { responseAddress = ClaimAction action @@ -556,7 +536,7 @@ update msg model ({ shared } as loggedIn) = } else - newModel + model |> UR.init |> UR.addExt (Just (ClaimAction action) |> RequiredAuthentication) @@ -586,7 +566,7 @@ update msg model ({ shared } as loggedIn) = jsAddressToMsg : List String -> Value -> Maybe Msg jsAddressToMsg addr val = -- TODO: Remove duplicates - case addr |> Debug.log "addr" of + case addr of "ClaimAction" :: [] -> Decode.decodeValue (Decode.oneOf @@ -622,7 +602,7 @@ msgToString msg = Tick _ -> [ "Tick" ] - GotProofTime _ _ -> + GotProofTime _ -> [ "GotProofTime" ] OpenProofSection _ -> From e63191fbaab3ff93a6776a85dd8c0fe1f6e19411 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Fri, 5 Feb 2021 15:32:56 +0300 Subject: [PATCH 46/91] Share port between modules with Claim button --- src/elm/Action.elm | 30 ++--- src/elm/Page/Community.elm | 2 +- src/elm/Page/Community/ClaimWithPhoto.elm | 133 +++++++++++----------- src/elm/Session/LoggedIn.elm | 32 ++++-- 4 files changed, 111 insertions(+), 86 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 8b941b75d..c2b1ef720 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -1,6 +1,7 @@ module Action exposing ( Action , ClaimConfirmationModalStatus(..) + , ClaimedAction , Model , Msg(..) , claimActionPort @@ -76,6 +77,7 @@ type alias ClaimedAction = type alias Model = { claimConfirmationModalStatus : ClaimConfirmationModalStatus , action : Action + , proofData : Maybe { proofPhoto : String, proofCode : String, proofTime : Int } } @@ -83,6 +85,7 @@ initClaimingActionModel : Action -> Model initClaimingActionModel action = { claimConfirmationModalStatus = Open action , action = action + , proofData = Nothing } @@ -153,11 +156,12 @@ viewClaimConfirmation symbol { t } claimConfirmationModalStatus = Open action -> let acceptMsg = - if action.hasProofPhoto then - ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objective.id action.id) - - else - ActionClaimed + -- TODO: Decide what to do the modal when click "claim" button for claim with proof + --if action.hasProofPhoto then + -- ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objective.id action.id) + -- + --else + ActionClaimed in modalContent acceptMsg False @@ -198,8 +202,8 @@ update { t } msg model = { model | claimConfirmationModalStatus = Closed } -claimActionPort : msg -> Action -> String -> Name -> Ports.JavascriptOutModel msg -claimActionPort msg action contractsCommunity accountName = +claimActionPort : msg -> String -> ClaimedAction -> Ports.JavascriptOutModel msg +claimActionPort msg contractsCommunity { actionId, maker, proofPhoto, proofCode, proofTime } = { responseAddress = msg , responseData = Encode.null , data = @@ -207,15 +211,15 @@ claimActionPort msg action contractsCommunity accountName = [ { accountName = contractsCommunity , name = "claimaction" , authorization = - { actor = accountName + { actor = maker , permissionName = Eos.samplePermission } , data = - { actionId = action.id - , maker = accountName - , proofPhoto = "" - , proofCode = "" - , proofTime = 0 + { actionId = actionId + , maker = maker + , proofPhoto = proofPhoto + , proofCode = proofCode + , proofTime = proofTime } |> encodeClaimAction } diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index 936872db1..547484469 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -523,7 +523,7 @@ viewAction translators canEdit symbol maybeDate action = || (action.usages > 0 && action.usagesLeft == 0) viewClaimButton = - -- TODO: move this to Action module + -- TODO: move this to Action module? button [ class ("h-10 uppercase rounded-lg ml-1" ++ claimColors ++ claimSize) , onClick diff --git a/src/elm/Page/Community/ClaimWithPhoto.elm b/src/elm/Page/Community/ClaimWithPhoto.elm index 106ffd892..91bf04ac5 100644 --- a/src/elm/Page/Community/ClaimWithPhoto.elm +++ b/src/elm/Page/Community/ClaimWithPhoto.elm @@ -24,6 +24,7 @@ import Icons import Json.Decode as Decode import Json.Encode as Encode exposing (Value) import Page +import Ports exposing (JavascriptOutModel) import Route import Session.LoggedIn as LoggedIn exposing (External(..)) import Session.Shared exposing (Translators) @@ -83,8 +84,9 @@ type Msg | Tick Time.Posix | EnteredPhoto (List File) | CompletedPhotoUpload (Result Http.Error String) - | ClaimAction Action + --| ClaimAction Action | GotClaimActionResponse (Result Value String) + | GotActionWithPhotoMsg Action.Msg type Proof @@ -253,7 +255,8 @@ viewClaimWithProofs (Proof photoStatus proofCode) translators action = NoOp else - ClaimAction action + --GotActionWithPhotoMsg Action.ActionClaimed + (GotActionWithPhotoMsg << Action.ClaimConfirmationOpen) action ) , disabled isUploadingInProgress ] @@ -465,7 +468,7 @@ update msg model ({ shared } as loggedIn) = { code = Nothing , claimTimestamp = Time.posixToMillis posix // 1000 , secondsAfterClaim = 0 - , availabilityPeriod = 5 --30 * 60 + , availabilityPeriod = 30 * 60 } in { model | proof = Proof NoPhotoAdded initProofCodeParts } @@ -480,10 +483,11 @@ update msg model ({ shared } as loggedIn) = ] } - ClaimAction action -> - -- TODO: Remove duplication in port - -- TODO: ??? Stop timer after photo was uploaded successfully + GotActionWithPhotoMsg actionMsg -> let + _ = + Debug.log "photo model" model + hasPhotoError = case model.proof of Proof (Uploaded _) _ -> @@ -493,52 +497,65 @@ update msg model ({ shared } as loggedIn) = -- Error: photo wasn't uploaded while claiming with proof True - ( proofPhotoUrl, proofCode_, proofTime ) = - case model.proof of - Proof (Uploaded url) (Just { code, claimTimestamp }) -> - ( url, Maybe.withDefault "" code, claimTimestamp ) + proofData = + if hasPhotoError then + Nothing - Proof (Uploaded url) Nothing -> - ( url, "", 0 ) + else + let + ( proofPhotoUrl, proofCode_, proofTime ) = + case model.proof of + Proof (Uploaded url) (Just { code, claimTimestamp }) -> + ( url, Maybe.withDefault "" code, claimTimestamp ) - _ -> - ( "", "", 0 ) - in - if hasPhotoError then - model - |> UR.init - |> UR.addExt (ShowFeedback LoggedIn.Failure (t "community.actions.proof.no_photo_error")) - - else if LoggedIn.isAuth loggedIn then - model - |> UR.init - |> UR.addPort - { responseAddress = ClaimAction action - , responseData = Encode.null - , data = - Eos.encodeTransaction - [ { accountName = shared.contracts.community - , name = "claimaction" - , authorization = - { actor = loggedIn.accountName - , permissionName = Eos.samplePermission - } - , data = - { actionId = action.id - , maker = loggedIn.accountName - , proofPhoto = proofPhotoUrl - , proofCode = proofCode_ - , proofTime = proofTime - } - |> Action.encodeClaimAction - } - ] - } + Proof (Uploaded url) Nothing -> + ( url, "", 0 ) - else - model - |> UR.init - |> UR.addExt (Just (ClaimAction action) |> RequiredAuthentication) + _ -> + ( "", "", 0 ) + in + Just + { proofPhoto = proofPhotoUrl + , proofCode = proofCode_ + , proofTime = proofTime + } + + addProofData actionModel pd = + { actionModel | proofData = pd } + + loggedInWithUpdatedClaimingAction = + case loggedIn.actionToClaim of + Just actionModel -> + { loggedIn + | actionToClaim = + Action.update shared.translators + actionMsg + (addProofData actionModel proofData) + |> Just + } + + Nothing -> + case actionMsg of + Action.ClaimConfirmationOpen a -> + { loggedIn + | actionToClaim = + addProofData (Action.initClaimingActionModel a) proofData + |> Just + } + + _ -> + loggedIn + + ext = + if hasPhotoError then + ShowFeedback LoggedIn.Failure (t "community.actions.proof.no_photo_error") + + else + UpdatedLoggedIn (Debug.log "loggedInWithUpdatedClaimingAction" loggedInWithUpdatedClaimingAction) + in + model + |> UR.init + |> UR.addExt ext NoOp -> model |> UR.init @@ -565,19 +582,7 @@ update msg model ({ shared } as loggedIn) = jsAddressToMsg : List String -> Value -> Maybe Msg jsAddressToMsg addr val = - -- TODO: Remove duplicates case addr of - "ClaimAction" :: [] -> - Decode.decodeValue - (Decode.oneOf - [ Decode.field "transactionId" Decode.string |> Decode.map Ok - , Decode.succeed (Err val) - ] - ) - val - |> Result.map (Just << GotClaimActionResponse) - |> Result.withDefault Nothing - "GetUint64Name" :: [] -> Decode.decodeValue (Decode.oneOf @@ -599,6 +604,9 @@ msgToString msg = CommunityLoaded res -> [ "CommunityLoaded" ] + GotActionWithPhotoMsg _ -> + [ "GotActionWithPhotoMsg" ] + Tick _ -> [ "Tick" ] @@ -629,9 +637,6 @@ msgToString msg = NoOp -> [ "NoOp" ] - ClaimAction _ -> - [ "ClaimAction" ] - generateVerificationCode : Int -> String -> Int -> String generateVerificationCode actionId makerAccountUint64 proofTimeSeconds = diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 419860e56..594b40cb5 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -845,28 +845,44 @@ update msg model = |> UR.init Action.ActionClaimed -> - case model.actionToClaim of - Just ca -> + case model.actionToClaim |> Debug.log "Action.ActionClaimed from LoggedIn" of + Just ({ action } as actionModel) -> let + ( proofPhoto, proofCode, proofTime ) = + case actionModel.proofData of + Just pd -> + ( pd.proofPhoto, pd.proofCode, pd.proofTime ) + + Nothing -> + ( "", "", 0 ) + + claimedAction = + { actionId = action.id + , maker = model.accountName + , proofPhoto = proofPhoto + , proofCode = proofCode + , proofTime = proofTime + } + claimPort : JavascriptOutModel Msg claimPort = Action.claimActionPort (GotActionMsg Action.ActionClaimed) - ca.action shared.contracts.community - model.accountName + claimedAction in if isAuth model then - updateClaimingAction ca + updateClaimingAction actionModel |> UR.init |> UR.addPort claimPort else - updateClaimingAction ca + updateClaimingAction actionModel + |> Debug.log "we stuck here" + -- TODO: Make it work! + --|> UR.addExt (RequiredAuthentication (Just (GotActionMsg Action.ActionClaimed))) |> UR.init - -- TODO: !!! Maybe return UpdateResult from Action.view? - --|> UR.addExt (RequiredAuthentication (Just (GotActionMsg Action.ActionClaimed))) Nothing -> model |> UR.init From 1c5306aaf154245f9ee07f04bb916b08f96dc89b Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Fri, 5 Feb 2021 18:02:00 +0300 Subject: [PATCH 47/91] Simplify GotAcitonMsg handler --- src/elm/Session/LoggedIn.elm | 122 +++++++++++++++-------------------- 1 file changed, 52 insertions(+), 70 deletions(-) diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 594b40cb5..bfe4225fd 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -839,68 +839,56 @@ update msg model = |> Just } in - case actionMsg of - Action.ClaimConfirmationOpen action -> + case ( model.actionToClaim, actionMsg ) of + ( Nothing, Action.ClaimConfirmationOpen action ) -> updateClaimingAction (Action.initClaimingActionModel action) |> UR.init - Action.ActionClaimed -> - case model.actionToClaim |> Debug.log "Action.ActionClaimed from LoggedIn" of - Just ({ action } as actionModel) -> - let - ( proofPhoto, proofCode, proofTime ) = - case actionModel.proofData of - Just pd -> - ( pd.proofPhoto, pd.proofCode, pd.proofTime ) - - Nothing -> - ( "", "", 0 ) - - claimedAction = - { actionId = action.id - , maker = model.accountName - , proofPhoto = proofPhoto - , proofCode = proofCode - , proofTime = proofTime - } - - claimPort : JavascriptOutModel Msg - claimPort = - Action.claimActionPort - (GotActionMsg Action.ActionClaimed) - shared.contracts.community - claimedAction - in - if isAuth model then - updateClaimingAction actionModel - |> UR.init - |> UR.addPort claimPort - - else - updateClaimingAction actionModel - |> Debug.log "we stuck here" - -- TODO: Make it work! - --|> UR.addExt (RequiredAuthentication (Just (GotActionMsg Action.ActionClaimed))) - |> UR.init - - Nothing -> - model - |> UR.init - - Action.ActionWithPhotoLinkClicked route -> - case model.actionToClaim of - Just ca -> - updateClaimingAction ca - |> update SearchClosed - |> UR.addCmd (Route.replaceUrl model.shared.navKey route) - - Nothing -> - model - |> UR.init + ( Just ({ action } as actionModel), Action.ActionClaimed ) -> + let + ( proofPhoto, proofCode, proofTime ) = + case actionModel.proofData of + Just pd -> + ( pd.proofPhoto, pd.proofCode, pd.proofTime ) + + Nothing -> + ( "", "", 0 ) + + claimedAction = + { actionId = action.id + , maker = model.accountName + , proofPhoto = proofPhoto + , proofCode = proofCode + , proofTime = proofTime + } - Action.GotActionClaimedResponse resp -> - case ( model.actionToClaim, resp ) of - ( Just ca, Ok _ ) -> + claimPort : JavascriptOutModel Msg + claimPort = + Action.claimActionPort + (GotActionMsg Action.ActionClaimed) + shared.contracts.community + claimedAction + in + if isAuth model then + updateClaimingAction actionModel + |> UR.init + |> UR.addPort claimPort + + else + updateClaimingAction actionModel + |> Debug.log "we stuck here" + -- TODO: Make it work! + --|> UR.addExt (RequiredAuthentication (Just (GotActionMsg Action.ActionClaimed))) + |> UR.init + + ( Just ca, Action.ActionWithPhotoLinkClicked route ) -> + updateClaimingAction ca + |> update SearchClosed + |> UR.addCmd (Route.replaceUrl model.shared.navKey route) + + ( Just ca, Action.GotActionClaimedResponse resp ) -> + case resp of + Ok _ -> let message = tr "dashboard.check_claim.success" @@ -910,24 +898,18 @@ update msg model = |> (\m -> { m | feedback = Show Success message }) |> UR.init - ( Just ca, Err _ ) -> + Err _ -> updateClaimingAction ca |> (\m -> { m | feedback = Show Failure (t "dashboard.check_claim.failure") }) |> UR.init - ( Nothing, _ ) -> - model - |> UR.init + ( Just ca, _ ) -> + updateClaimingAction ca + |> UR.init _ -> - case model.actionToClaim of - Just ca -> - updateClaimingAction ca - |> UR.init - - Nothing -> - model - |> UR.init + model + |> UR.init SearchClosed -> { model From 91084c5731c8caae7dc7abcdefed9db59549f4e0 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Sat, 6 Feb 2021 09:25:26 +0300 Subject: [PATCH 48/91] Make request for PIN work --- src/elm/Action.elm | 32 ++++++----- src/elm/Page/Community.elm | 10 ++++ src/elm/Session/LoggedIn.elm | 107 ++++++++++++++++++++++------------- 3 files changed, 96 insertions(+), 53 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index c2b1ef720..b2c3a272e 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -93,7 +93,7 @@ type Msg = NoOp | ClaimConfirmationOpen Action | ClaimConfirmationClosed - | ActionClaimed + | ActionClaimed Bool | GotActionClaimedResponse (Result Value String) | ActionWithPhotoLinkClicked Route.Route @@ -106,8 +106,8 @@ getClaimWithPhotoRoute community objectiveId actionId = actionId -viewClaimConfirmation : Eos.Symbol -> Translators -> ClaimConfirmationModalStatus -> Html Msg -viewClaimConfirmation symbol { t } claimConfirmationModalStatus = +viewClaimConfirmation : Bool -> Eos.Symbol -> Translators -> ClaimConfirmationModalStatus -> Html Msg +viewClaimConfirmation isAuth symbol { t } claimConfirmationModalStatus = let text_ s = text (t s) @@ -161,7 +161,7 @@ viewClaimConfirmation symbol { t } claimConfirmationModalStatus = -- ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objective.id action.id) -- --else - ActionClaimed + ActionClaimed isAuth in modalContent acceptMsg False @@ -186,7 +186,7 @@ update { t } msg model = ClaimConfirmationOpen action -> { model | claimConfirmationModalStatus = Open action } - ActionClaimed -> + ActionClaimed _ -> { model | claimConfirmationModalStatus = InProgress } ClaimConfirmationClosed -> @@ -230,7 +230,7 @@ claimActionPort msg contractsCommunity { actionId, maker, proofPhoto, proofCode, jsAddressToMsg : List String -> Value -> Maybe Msg jsAddressToMsg addr val = case addr of - "ClaimAction" :: [] -> + "ActionClaimed" :: [] -> Decode.decodeValue (Decode.oneOf [ Decode.field "transactionId" Decode.string |> Decode.map Ok @@ -252,19 +252,19 @@ msgToString msg = [ "NoOp" ] ClaimConfirmationOpen _ -> - [ "OpenClaimConfirmation" ] + [ "ClaimConfirmationOpen" ] ClaimConfirmationClosed -> - [ "CloseClaimConfirmation" ] + [ "ClaimConfirmationClosed" ] - ActionClaimed -> - [ "ClaimAction" ] + ActionClaimed _ -> + [ "ActionClaimed" ] ActionWithPhotoLinkClicked _ -> [ "ActionWithPhotoLinkClicked" ] GotActionClaimedResponse r -> - [ "GotClaimActionResponse", UR.resultToString r ] + [ "GotActionClaimedResponse", UR.resultToString r ] encodeClaimAction : ClaimedAction -> Encode.Value @@ -313,12 +313,14 @@ selectionSet = |> with ActionObject.position -viewClaimButton : Action -> Html Msg -viewClaimButton action = +viewClaimButton : Symbol -> Action -> Html Msg +viewClaimButton symbol action = -- TODO: Handle action.deadline and action.isCompleted. if action.hasProofPhoto then button - [ onClick (ClaimConfirmationOpen action) + [ --onClick (ClaimConfirmationOpen action) + -- TODO: Just use href + onClick <| ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objective.id action.id) , class "self-end button button-primary" ] [ span [ class "inline-block w-4 align-middle mr-2" ] [ Icons.camera "" ] @@ -350,7 +352,7 @@ viewSearchActions symbol actions = , text " " , text <| Eos.symbolToSymbolCodeString symbol ] - , viewClaimButton action + , viewClaimButton symbol action ] ] ] diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index 547484469..1ae223dc8 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -325,6 +325,9 @@ update msg model ({ shared } as loggedIn) = GotCommunityActionMsg actionMsg -> let + _ = + Debug.log "actionMsg from Community" ( actionMsg, loggedIn.actionToClaim ) + loggedInWithUpdatedClaimingAction = case loggedIn.actionToClaim of Just a -> @@ -346,6 +349,7 @@ update msg model ({ shared } as loggedIn) = in model |> UR.init + -- TODO: This doesn't handle all `LoggedIn.GotActionMsg` cases (e.g. Claim with Photo) |> UR.addExt (UpdatedLoggedIn loggedInWithUpdatedClaimingAction) CompletedLoadCommunity (Ok community) -> @@ -530,6 +534,12 @@ viewAction translators canEdit symbol maybeDate action = (if isClosed then NoOp + else if action.hasProofPhoto then + GotCommunityActionMsg + (Action.ActionWithPhotoLinkClicked + (Action.getClaimWithPhotoRoute symbol action.objective.id action.id) + ) + else (GotCommunityActionMsg << Action.ClaimConfirmationOpen) action ) diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index bfe4225fd..c400971a0 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -281,12 +281,12 @@ viewFeedback status message = " bg-red" in div - [ class <| "w-full sticky z-10 top-0 transition duration-500 ease-in-out bg-blue-500 hover:bg-red-500 transform hover:-translate-y-1 hover:scale-110" ++ color + [ class <| "w-full sticky z-10 top-0 bg-blue-500 hover:bg-red-500" ++ color , style "display" "grid" , style "grid-template" "\". text x\" 100% / 10% 80% 10%" ] [ span - [ class "flex justify-center items-center text-sm h-10 leading-snug text-white font-bold" + [ class "flex justify-center items-center transition duration-500 ease-in-out text-sm h-10 leading-snug text-white font-bold transform hover:-translate-y-1 hover:scale-110" , style "grid-area" "text" ] [ text message ] @@ -314,6 +314,7 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = , case model.actionToClaim of Just a -> Action.viewClaimConfirmation + (isAuth model) model.selectedCommunity shared.translators a.claimConfirmationModalStatus @@ -331,6 +332,12 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = else viewMainMenu page model |> Html.map thisMsg ] + , case model.feedback of + Show status message -> + viewFeedback status message |> Html.map thisMsg + + Hidden -> + text "" ] ] ++ (if Search.isActive model.searchModel then @@ -438,13 +445,7 @@ viewPageBody t model profile_ page content thisMsg = [] ] in - [ case model.feedback of - Show status message -> - viewFeedback status message |> Html.map thisMsg - - Hidden -> - text "" - , div [ class "flex-grow" ] + [ div [ class "flex-grow" ] [ case model.hasKyc of FeatureLoading -> div [ class "full-spinner-container h-full" ] @@ -838,14 +839,22 @@ update msg model = Action.update shared.translators actionMsg actionModel |> Just } + + _ = + Debug.log "Action msg" actionMsg in case ( model.actionToClaim, actionMsg ) of ( Nothing, Action.ClaimConfirmationOpen action ) -> updateClaimingAction (Action.initClaimingActionModel action) |> UR.init - ( Just ({ action } as actionModel), Action.ActionClaimed ) -> + ( Just ({ action } as actionModel), Action.ActionClaimed True ) -> + -- Do the actual claiming via EOS. + -- This case is fired from `GotAuthMsg` after user logs in if `actionToClaim` is presented in `model`. let + _ = + Debug.log "Claiming action when isAuth is True" (isAuth model) + ( proofPhoto, proofCode, proofTime ) = case actionModel.proofData of Just pd -> @@ -865,43 +874,44 @@ update msg model = claimPort : JavascriptOutModel Msg claimPort = Action.claimActionPort - (GotActionMsg Action.ActionClaimed) + (GotActionMsg actionMsg) shared.contracts.community claimedAction in - if isAuth model then - updateClaimingAction actionModel - |> UR.init - |> UR.addPort claimPort - - else - updateClaimingAction actionModel - |> Debug.log "we stuck here" - -- TODO: Make it work! - --|> UR.addExt (RequiredAuthentication (Just (GotActionMsg Action.ActionClaimed))) - |> UR.init - - ( Just ca, Action.ActionWithPhotoLinkClicked route ) -> - updateClaimingAction ca + updateClaimingAction actionModel + |> UR.init + |> UR.addPort claimPort + + ( Just ({ action } as actionModel), Action.ActionClaimed False ) -> + updateClaimingAction actionModel + |> askedAuthentication + |> UR.init + + ( _, Action.ActionWithPhotoLinkClicked route ) -> + let + _ = + Debug.log "photo" route + in + model |> update SearchClosed |> UR.addCmd (Route.replaceUrl model.shared.navKey route) - ( Just ca, Action.GotActionClaimedResponse resp ) -> - case resp of - Ok _ -> - let - message = + ( Just _, Action.GotActionClaimedResponse resp ) -> + let + showMessage = + case resp of + Ok _ -> tr "dashboard.check_claim.success" [ ( "symbolCode", Eos.symbolToSymbolCodeString model.selectedCommunity ) ] - in - updateClaimingAction ca - |> (\m -> { m | feedback = Show Success message }) - |> UR.init + |> Show Success - Err _ -> - updateClaimingAction ca - |> (\m -> { m | feedback = Show Failure (t "dashboard.check_claim.failure") }) - |> UR.init + Err _ -> + Show Failure (t "dashboard.check_claim.failure") + in + -- Flush claimed action. + { model | actionToClaim = Nothing } + |> (\m -> { m | feedback = showMessage }) + |> UR.init ( Just ca, _ ) -> updateClaimingAction ca @@ -1051,6 +1061,10 @@ update msg model = UR.init closeAllModals GotAuthMsg authMsg -> + let + _ = + Debug.log "authMsg" authMsg + in Auth.update authMsg shared model.auth |> UR.map (\a -> { model | auth = a }) @@ -1062,8 +1076,25 @@ update msg model = |> UR.addExt AuthenticationFailed Auth.CompletedAuth _ -> + let + cmd = + -- Check if there's action claim in progress (stored in model.actionToClaim). + -- If we found one, we claim it after loggin in. Otherwise, just login. + case model.actionToClaim of + Just ac -> + let + _ = + Debug.log "from CompletedAuth" ac + in + Task.succeed (GotActionMsg (Action.ActionClaimed True)) + |> Task.perform identity + + Nothing -> + Cmd.none + in closeModal uResult |> UR.addExt AuthenticationSucceed + |> UR.addCmd cmd Auth.UpdatedShared newShared -> UR.mapModel From 1a719bd74dd9c2d1daff8269a2ad1beb63062da4 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Sat, 6 Feb 2021 10:37:58 +0300 Subject: [PATCH 49/91] Go to Claim with Photo page after claim confirmation --- src/elm/Action.elm | 18 ++++++++++-------- src/elm/Page/Community.elm | 6 ------ src/elm/Session/LoggedIn.elm | 7 ++++--- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index b2c3a272e..782e61a6b 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -157,11 +157,11 @@ viewClaimConfirmation isAuth symbol { t } claimConfirmationModalStatus = let acceptMsg = -- TODO: Decide what to do the modal when click "claim" button for claim with proof - --if action.hasProofPhoto then - -- ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objective.id action.id) - -- - --else - ActionClaimed isAuth + if action.hasProofPhoto then + ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objective.id action.id) + + else + ActionClaimed isAuth in modalContent acceptMsg False @@ -199,6 +199,10 @@ update { t } msg model = { model | claimConfirmationModalStatus = Closed } ActionWithPhotoLinkClicked _ -> + let + _ = + Debug.log "ActionWithPhotoLinkClicked" model + in { model | claimConfirmationModalStatus = Closed } @@ -318,9 +322,7 @@ viewClaimButton symbol action = -- TODO: Handle action.deadline and action.isCompleted. if action.hasProofPhoto then button - [ --onClick (ClaimConfirmationOpen action) - -- TODO: Just use href - onClick <| ActionWithPhotoLinkClicked (getClaimWithPhotoRoute symbol action.objective.id action.id) + [ onClick (ClaimConfirmationOpen action) , class "self-end button button-primary" ] [ span [ class "inline-block w-4 align-middle mr-2" ] [ Icons.camera "" ] diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index 1ae223dc8..41fc2c10b 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -534,12 +534,6 @@ viewAction translators canEdit symbol maybeDate action = (if isClosed then NoOp - else if action.hasProofPhoto then - GotCommunityActionMsg - (Action.ActionWithPhotoLinkClicked - (Action.getClaimWithPhotoRoute symbol action.objective.id action.id) - ) - else (GotCommunityActionMsg << Action.ClaimConfirmationOpen) action ) diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index c400971a0..5e1147a43 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -887,12 +887,13 @@ update msg model = |> askedAuthentication |> UR.init - ( _, Action.ActionWithPhotoLinkClicked route ) -> + ( Just ({ action } as actionModel), Action.ActionWithPhotoLinkClicked route ) -> + -- TODO: pass the route to go back to `ActionWithPhotoLinkClicked` when Cancel clicked? let _ = - Debug.log "photo" route + Debug.log "model, photo" ( model.actionToClaim, route ) in - model + updateClaimingAction actionModel |> update SearchClosed |> UR.addCmd (Route.replaceUrl model.shared.navKey route) From 3bbba97d3b4c8e163baff42a0fedf68b24d2d232 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 8 Feb 2021 10:00:37 +0300 Subject: [PATCH 50/91] Improve code readability Move view search body into separate funciton. --- src/elm/Session/LoggedIn.elm | 106 ++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 5e1147a43..9dd610db3 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -300,8 +300,8 @@ viewFeedback status message = ] -viewHelper : (Msg -> msg) -> Page -> Profile.Model -> Model -> Html msg -> Html msg -viewHelper thisMsg page profile_ ({ shared } as model) content = +viewHelper : (Msg -> pageMsg) -> Page -> Profile.Model -> Model -> Html pageMsg -> Html pageMsg +viewHelper pageMsg page profile_ ({ shared } as model) content = let { t } = shared.translators @@ -310,7 +310,7 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = [ class "min-h-screen flex flex-col" ] ([ div [ class "bg-white" ] [ div [ class "container mx-auto" ] - [ viewHeader model profile_ |> Html.map thisMsg + [ viewHeader model profile_ |> Html.map pageMsg , case model.actionToClaim of Just a -> Action.viewClaimConfirmation @@ -318,73 +318,33 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = model.selectedCommunity shared.translators a.claimConfirmationModalStatus - |> Html.map (thisMsg << GotActionMsg) + |> Html.map (pageMsg << GotActionMsg) Nothing -> text "" , -- Search form is separated from search results because it needs to -- be between community selector and user dropdown on Desktops. Search.viewForm model.searchModel - |> Html.map (GotSearchMsg >> thisMsg) + |> Html.map (GotSearchMsg >> pageMsg) , if Search.isActive model.searchModel then text "" else - viewMainMenu page model |> Html.map thisMsg + viewMainMenu page model |> Html.map pageMsg ] , case model.feedback of Show status message -> - viewFeedback status message |> Html.map thisMsg + viewFeedback status message |> Html.map pageMsg Hidden -> text "" ] ] ++ (if Search.isActive model.searchModel then - [ div [ class "container mx-auto flex flex-grow" ] - [ case model.searchModel.found of - Just ({ actions, offers } as results) -> - case ( List.length actions, List.length offers ) of - ( 0, 0 ) -> - Search.viewEmptyResults model.searchModel.queryText - |> Html.map (GotSearchMsg >> thisMsg) - - _ -> - let - wrapWithClass c inner = - div [ class ("flex-grow " ++ c) ] - [ inner ] - in - case model.searchModel.state of - ResultsShowed Offers -> - div [] - [ Search.viewTabs results Offers - , Search.viewOffers model.selectedCommunity results.offers - |> wrapWithClass "bg-gray-100" - ] - |> Html.map (GotSearchMsg >> thisMsg) - - ResultsShowed Actions -> - div [] - [ Search.viewTabs results Actions - |> Html.map (GotSearchMsg >> thisMsg) - , Action.viewSearchActions model.selectedCommunity results.actions - |> wrapWithClass "bg-gray-100" - |> Html.map (GotActionMsg >> thisMsg) - ] - - _ -> - Search.viewResultsOverview results - |> wrapWithClass "bg-white p-4" - |> Html.map (GotSearchMsg >> thisMsg) - - Nothing -> - Search.viewRecentQueries model.searchModel |> Html.map (GotSearchMsg >> thisMsg) - ] - ] + [ viewSearchBody model.selectedCommunity pageMsg model.searchModel ] else - viewPageBody t model profile_ page content thisMsg + viewPageBody t model profile_ page content pageMsg ) ++ [ viewFooter shared , Modal.initWith @@ -396,13 +356,57 @@ viewHelper thisMsg page profile_ ({ shared } as model) content = |> List.map (Html.map GotAuthMsg) ) |> Modal.toHtml - |> Html.map thisMsg + |> Html.map pageMsg , communitySelectorModal model - |> Html.map thisMsg + |> Html.map pageMsg ] ) +viewSearchBody : Symbol -> (Msg -> pageMsg) -> Search.Model -> Html pageMsg +viewSearchBody selectedCommunity toPageMsg searchModel = + div [ class "container mx-auto flex flex-grow" ] + [ case searchModel.found of + Just ({ actions, offers } as results) -> + case ( List.length actions, List.length offers ) of + ( 0, 0 ) -> + Search.viewEmptyResults searchModel.queryText + |> Html.map (GotSearchMsg >> toPageMsg) + + _ -> + let + wrapWithClass c inner = + div [ class ("flex-grow " ++ c) ] + [ inner ] + in + case searchModel.state of + ResultsShowed Offers -> + div [] + [ Search.viewTabs results Offers + , Search.viewOffers selectedCommunity results.offers + |> wrapWithClass "bg-gray-100" + ] + |> Html.map (GotSearchMsg >> toPageMsg) + + ResultsShowed Actions -> + div [] + [ Search.viewTabs results Actions + |> Html.map (GotSearchMsg >> toPageMsg) + , Action.viewSearchActions selectedCommunity results.actions + |> wrapWithClass "bg-gray-100" + |> Html.map (GotActionMsg >> toPageMsg) + ] + + _ -> + Search.viewResultsOverview results + |> wrapWithClass "bg-white p-4" + |> Html.map (GotSearchMsg >> toPageMsg) + + Nothing -> + Search.viewRecentQueries searchModel |> Html.map (GotSearchMsg >> toPageMsg) + ] + + viewPageBody t model profile_ page content thisMsg = let hasUserKycFilled = From e8a50a9d018aa02b92ad10e868e15c924e3506b1 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 8 Feb 2021 14:42:43 +0300 Subject: [PATCH 51/91] Add claiming for aciton with proof --- src/elm/Page/Community/ClaimWithPhoto.elm | 13 ++++++++----- src/elm/Session/LoggedIn.elm | 4 ++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/elm/Page/Community/ClaimWithPhoto.elm b/src/elm/Page/Community/ClaimWithPhoto.elm index 91bf04ac5..e36f75d6b 100644 --- a/src/elm/Page/Community/ClaimWithPhoto.elm +++ b/src/elm/Page/Community/ClaimWithPhoto.elm @@ -175,7 +175,10 @@ view ({ shared } as loggedIn) model = [ Page.viewHeader loggedIn (t "community.actions.title") (Route.Community loggedIn.selectedCommunity) , case loggedIn.actionToClaim of Just actionModel -> - viewClaimWithProofs model.proof loggedIn.shared.translators actionModel.action + viewClaimWithProofs model.proof + loggedIn.shared.translators + (LoggedIn.isAuth loggedIn) + actionModel.action Nothing -> text "this is claim with photo page" @@ -192,8 +195,8 @@ view ({ shared } as loggedIn) model = } -viewClaimWithProofs : Proof -> Translators -> Action -> Html Msg -viewClaimWithProofs (Proof photoStatus proofCode) translators action = +viewClaimWithProofs : Proof -> Translators -> Bool -> Action -> Html Msg +viewClaimWithProofs (Proof photoStatus proofCode) translators isAuth action = let { t } = translators @@ -255,8 +258,8 @@ viewClaimWithProofs (Proof photoStatus proofCode) translators action = NoOp else - --GotActionWithPhotoMsg Action.ActionClaimed - (GotActionWithPhotoMsg << Action.ClaimConfirmationOpen) action + GotActionWithPhotoMsg (Action.ActionClaimed isAuth) + --(GotActionWithPhotoMsg << Action.ClaimConfirmationOpen) action ) , disabled isUploadingInProgress ] diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 9dd610db3..698593617 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -887,6 +887,10 @@ update msg model = |> UR.addPort claimPort ( Just ({ action } as actionModel), Action.ActionClaimed False ) -> + let + _ = + Debug.log "Auth is " False + in updateClaimingAction actionModel |> askedAuthentication |> UR.init From dceb9b7ce73504f3871a65387500acc674bb9476 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Mon, 8 Feb 2021 20:27:05 +0300 Subject: [PATCH 52/91] Improve claiming with proof --- src/elm/Action.elm | 2 - src/elm/Page/Community/ClaimWithPhoto.elm | 269 ++++++++++------------ src/elm/Session/LoggedIn.elm | 26 +-- 3 files changed, 130 insertions(+), 167 deletions(-) diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 782e61a6b..5d56dc5dc 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -77,7 +77,6 @@ type alias ClaimedAction = type alias Model = { claimConfirmationModalStatus : ClaimConfirmationModalStatus , action : Action - , proofData : Maybe { proofPhoto : String, proofCode : String, proofTime : Int } } @@ -85,7 +84,6 @@ initClaimingActionModel : Action -> Model initClaimingActionModel action = { claimConfirmationModalStatus = Open action , action = action - , proofData = Nothing } diff --git a/src/elm/Page/Community/ClaimWithPhoto.elm b/src/elm/Page/Community/ClaimWithPhoto.elm index e36f75d6b..b5a66205e 100644 --- a/src/elm/Page/Community/ClaimWithPhoto.elm +++ b/src/elm/Page/Community/ClaimWithPhoto.elm @@ -55,7 +55,7 @@ init loggedIn symbol objectiveId actionId = case loggedIn.actionToClaim of Just actionModel -> if actionModel.action.hasProofPhoto then - ( Loaded actionModel, Task.succeed (OpenProofSection actionModel.action) |> Task.perform identity ) + ( Loaded actionModel, Task.succeed (PageShowed actionModel.action) |> Task.perform identity ) else ( NotFound, Cmd.none ) @@ -75,18 +75,16 @@ init loggedIn symbol objectiveId actionId = type Msg = NoOp - | CommunityLoaded (Result (Graphql.Http.Error (Maybe Community.Model)) (Maybe Community.Model)) - | OpenProofSection Action - | CloseProofSection ReasonToCloseProofSection + | ActionLoaded (Result (Graphql.Http.Error (Maybe Action.Model)) (Maybe Action.Model)) + | PageShowed Action + | ClaimingCancelled ReasonToCloseProofSection | GotProofTime Posix - | GetUint64Name String + | AskedForUint64Name String | GotUint64Name (Result Value String) | Tick Time.Posix - | EnteredPhoto (List File) - | CompletedPhotoUpload (Result Http.Error String) - --| ClaimAction Action - | GotClaimActionResponse (Result Value String) - | GotActionWithPhotoMsg Action.Msg + | PhotoAdded (List File) + | PhotoUploaded (Result Http.Error String) + | GotActionMsg Action.Msg type Proof @@ -94,7 +92,7 @@ type Proof type alias ProofCode = - { code : Maybe String + { code_ : Maybe String , claimTimestamp : Int , secondsAfterClaim : Int , availabilityPeriod : Int @@ -110,7 +108,7 @@ type ProofPhotoStatus type ReasonToCloseProofSection = CancelClicked - | TimerExpired + | TimerEnded viewPhotoUploader : Translators -> ProofPhotoStatus -> Html Msg @@ -134,7 +132,7 @@ viewPhotoUploader { t } proofPhotoStatus = [ class "hidden-img-input" , type_ "file" , accept "image/*" - , Page.onFileChange EnteredPhoto + , Page.onFileChange PhotoAdded , multiple False ] [] @@ -167,13 +165,12 @@ view ({ shared } as loggedIn) model = Loading -> Page.fullPageLoading shared - TimeExpired -> - Page.fullPageNotFound "Time has expired" "hello subtitle" + Expired -> + Page.fullPageNotFound "Time has expired" "" Loaded _ -> div [ class "bg-white" ] - [ Page.viewHeader loggedIn (t "community.actions.title") (Route.Community loggedIn.selectedCommunity) - , case loggedIn.actionToClaim of + [ case loggedIn.actionToClaim of Just actionModel -> viewClaimWithProofs model.proof loggedIn.shared.translators @@ -181,6 +178,7 @@ view ({ shared } as loggedIn) model = actionModel.action Nothing -> + -- TODO: FIXIT: We see this after claiming text "this is claim with photo page" ] @@ -217,8 +215,8 @@ viewClaimWithProofs (Proof photoStatus proofCode) translators isAuth action = Maybe.withDefault "" action.photoProofInstructions ] , case proofCode of - Just { code, secondsAfterClaim, availabilityPeriod } -> - case code of + Just { code_, secondsAfterClaim, availabilityPeriod } -> + case code_ of Just c -> viewProofCode translators @@ -244,7 +242,7 @@ viewClaimWithProofs (Proof photoStatus proofCode) translators isAuth action = NoOp else - CloseProofSection CancelClicked + ClaimingCancelled CancelClicked ) , classList [ ( "button-disabled", isUploadingInProgress ) ] , disabled isUploadingInProgress @@ -258,8 +256,7 @@ viewClaimWithProofs (Proof photoStatus proofCode) translators isAuth action = NoOp else - GotActionWithPhotoMsg (Action.ActionClaimed isAuth) - --(GotActionWithPhotoMsg << Action.ClaimConfirmationOpen) action + GotActionMsg (Action.ActionClaimed isAuth) ) , disabled isUploadingInProgress ] @@ -311,16 +308,16 @@ subscriptions model = Time.every 1000 Tick _ -> - -- No proof code, no timer needed + -- No timer needed if there's no proof code. Sub.none type Status = Loading | Loaded Action.Model - | LoadFailed (Graphql.Http.Error (Maybe Community.Model)) + | LoadFailed (Graphql.Http.Error (Maybe Action.Model)) | NotFound - | TimeExpired + | Expired update : Msg -> Model -> LoggedIn.Model -> UR.UpdateResult Model Msg (External Msg) @@ -333,14 +330,14 @@ update msg model ({ shared } as loggedIn) = shared.translators in case msg of - CommunityLoaded (Err err) -> + ActionLoaded (Err err) -> { model | status = LoadFailed err } |> UR.init |> UR.logGraphqlError msg err - CommunityLoaded (Ok c) -> + ActionLoaded (Ok c) -> case c of - Just community -> + Just action -> { model | status = Loading } @@ -350,7 +347,7 @@ update msg model ({ shared } as loggedIn) = { model | status = NotFound } |> UR.init - OpenProofSection action -> + PageShowed action -> let runProofCodeTimer = if action.hasProofCode then @@ -362,44 +359,37 @@ update msg model ({ shared } as loggedIn) = model |> UR.init |> UR.addCmd runProofCodeTimer - -- Don't show any messages at first - |> UR.addExt HideFeedback - EnteredPhoto (file :: _) -> - let - uploadImage = - Api.uploadImage shared file CompletedPhotoUpload - in + PhotoAdded (file :: _) -> { model | proof = Proof Uploading proofCode } |> UR.init - |> UR.addCmd uploadImage - |> UR.addExt HideFeedback + |> UR.addCmd (Api.uploadImage shared file PhotoUploaded) - EnteredPhoto [] -> + PhotoAdded [] -> model |> UR.init - CompletedPhotoUpload (Ok url) -> + PhotoUploaded (Ok url) -> { model | proof = Proof (Uploaded url) proofCode } |> UR.init - CompletedPhotoUpload (Err error) -> + PhotoUploaded (Err error) -> { model | proof = Proof (UploadFailed error) proofCode } |> UR.init |> UR.logHttpError msg error - CloseProofSection reason -> + ClaimingCancelled reason -> let ( status, ext ) = case reason of - TimerExpired -> - ( TimeExpired, ShowFeedback LoggedIn.Failure (t "community.actions.proof.time_expired") ) + TimerEnded -> + ( Expired, ShowFeedback LoggedIn.Failure (t "community.actions.proof.time_expired") ) CancelClicked -> -- TODO: Redirect to the place where the user came from @@ -412,12 +402,12 @@ update msg model ({ shared } as loggedIn) = |> UR.init |> UR.addExt ext - GetUint64Name _ -> + AskedForUint64Name _ -> model |> UR.init GotUint64Name (Ok uint64name) -> - case ( proofCode, loggedIn.actionToClaim ) of - ( Just pc, Just actionModel ) -> + case ( proofCode, model.status ) of + ( Just pc, Loaded actionModel ) -> let verificationCode = generateVerificationCode actionModel.action.id uint64name pc.claimTimestamp @@ -425,7 +415,7 @@ update msg model ({ shared } as loggedIn) = newProofCode = Just { pc - | code = Just verificationCode + | code_ = Just verificationCode } in { model | proof = Proof photoStatus newProofCode } @@ -436,30 +426,32 @@ update msg model ({ shared } as loggedIn) = |> UR.init GotUint64Name (Err _) -> - model |> UR.init + model + |> UR.init + |> UR.addExt (ShowFeedback LoggedIn.Failure "Failed while creating proof code.") Tick timer -> case proofCode of - Just pc -> + Just proofCode_ -> let secondsAfterClaim = - (Time.posixToMillis timer // 1000) - pc.claimTimestamp + (Time.posixToMillis timer // 1000) - proofCode_.claimTimestamp isProofCodeActive = - (pc.availabilityPeriod - secondsAfterClaim) > 0 + (proofCode_.availabilityPeriod - secondsAfterClaim) > 0 in if isProofCodeActive then let newProofCode = Just - { pc + { proofCode_ | secondsAfterClaim = secondsAfterClaim } in { model | proof = Proof photoStatus newProofCode } |> UR.init else - update (CloseProofSection TimerExpired) model loggedIn + update (ClaimingCancelled TimerEnded) model loggedIn _ -> model |> UR.init @@ -468,7 +460,7 @@ update msg model ({ shared } as loggedIn) = let initProofCodeParts = Just - { code = Nothing + { code_ = Nothing , claimTimestamp = Time.posixToMillis posix // 1000 , secondsAfterClaim = 0 , availabilityPeriod = 30 * 60 @@ -477,7 +469,7 @@ update msg model ({ shared } as loggedIn) = { model | proof = Proof NoPhotoAdded initProofCodeParts } |> UR.init |> UR.addPort - { responseAddress = GetUint64Name (Eos.nameToString loggedIn.accountName) + { responseAddress = AskedForUint64Name (Eos.nameToString loggedIn.accountName) , responseData = Encode.null , data = Encode.object @@ -486,102 +478,86 @@ update msg model ({ shared } as loggedIn) = ] } - GotActionWithPhotoMsg actionMsg -> - let - _ = - Debug.log "photo model" model - - hasPhotoError = - case model.proof of - Proof (Uploaded _) _ -> - False - - _ -> - -- Error: photo wasn't uploaded while claiming with proof - True - - proofData = - if hasPhotoError then - Nothing - - else - let - ( proofPhotoUrl, proofCode_, proofTime ) = - case model.proof of - Proof (Uploaded url) (Just { code, claimTimestamp }) -> - ( url, Maybe.withDefault "" code, claimTimestamp ) - - Proof (Uploaded url) Nothing -> - ( url, "", 0 ) - - _ -> - ( "", "", 0 ) - in - Just - { proofPhoto = proofPhotoUrl - , proofCode = proofCode_ - , proofTime = proofTime - } - - addProofData actionModel pd = - { actionModel | proofData = pd } - - loggedInWithUpdatedClaimingAction = - case loggedIn.actionToClaim of - Just actionModel -> - { loggedIn - | actionToClaim = - Action.update shared.translators - actionMsg - (addProofData actionModel proofData) - |> Just + GotActionMsg ((Action.ActionClaimed isAuth) as actionMsg) -> + case ( model.status, model.proof ) of + ( Loaded actionModel, Proof (Uploaded url) proofCodeMatch ) -> + let + ( code, time ) = + case proofCodeMatch of + Just { code_, claimTimestamp } -> + ( Maybe.withDefault "" code_, claimTimestamp ) + + Nothing -> + ( "", 0 ) + + claimedAction = + { actionId = actionModel.action.id + , maker = loggedIn.accountName + , proofPhoto = url + , proofCode = code + , proofTime = time } - Nothing -> - case actionMsg of - Action.ClaimConfirmationOpen a -> - { loggedIn - | actionToClaim = - addProofData (Action.initClaimingActionModel a) proofData - |> Just - } - - _ -> - loggedIn - - ext = - if hasPhotoError then - ShowFeedback LoggedIn.Failure (t "community.actions.proof.no_photo_error") - - else - UpdatedLoggedIn (Debug.log "loggedInWithUpdatedClaimingAction" loggedInWithUpdatedClaimingAction) - in - model - |> UR.init - |> UR.addExt ext + claimPort : JavascriptOutModel Msg + claimPort = + Action.claimActionPort + (GotActionMsg actionMsg) + shared.contracts.community + claimedAction + + doNext = + if isAuth then + -- Process claim if user is logged in + UR.addPort claimPort + + else + -- Ask for PIN if user is not logged in + UR.addExt (RequiredAuthentication (Just <| GotActionMsg (Action.ActionClaimed True))) + in + { model + | status = Loaded (Action.update shared.translators actionMsg actionModel) + } + |> UR.init + |> doNext - NoOp -> - model |> UR.init + _ -> + -- No photo uploaded, show the error message + model + |> UR.init + |> UR.addExt (ShowFeedback LoggedIn.Failure (t "community.actions.proof.no_photo_error")) - GotClaimActionResponse (Ok _) -> - -- TODO: remove duplicates + GotActionMsg (Action.GotActionClaimedResponse (Ok _)) -> let message = shared.translators.tr "dashboard.check_claim.success" [ ( "symbolCode", Eos.symbolToSymbolCodeString loggedIn.selectedCommunity ) ] in model - --claimConfirmationModalStatus = Closed |> UR.init + -- Remove claimed action from the global state + |> UR.addExt (UpdatedLoggedIn { loggedIn | actionToClaim = Nothing }) |> UR.addExt (ShowFeedback LoggedIn.Success message) - GotClaimActionResponse (Err _) -> - -- TODO: remove duplicates + GotActionMsg (Action.GotActionClaimedResponse (Err _)) -> model - -- claimConfirmationModalStatus = Closed |> UR.init |> UR.addExt (ShowFeedback LoggedIn.Failure (t "dashboard.check_claim.failure")) + GotActionMsg actionMsg -> + -- TODO: Just update the actionModel + case model.status of + Loaded actionModel -> + { model + | status = Loaded (Action.update shared.translators actionMsg actionModel) + } + |> UR.init + + _ -> + model |> UR.init + + NoOp -> + model |> UR.init + jsAddressToMsg : List String -> Value -> Maybe Msg jsAddressToMsg addr val = @@ -597,6 +573,10 @@ jsAddressToMsg addr val = |> Result.map (Just << GotUint64Name) |> Result.withDefault Nothing + "GotActionMsg" :: remainAddress -> + Action.jsAddressToMsg remainAddress val + |> Maybe.map GotActionMsg + _ -> Nothing @@ -604,11 +584,11 @@ jsAddressToMsg addr val = msgToString : Msg -> List String msgToString msg = case msg of - CommunityLoaded res -> + ActionLoaded res -> [ "CommunityLoaded" ] - GotActionWithPhotoMsg _ -> - [ "GotActionWithPhotoMsg" ] + GotActionMsg actionMsg -> + "GotActionMsg" :: Action.msgToString actionMsg Tick _ -> [ "Tick" ] @@ -616,27 +596,24 @@ msgToString msg = GotProofTime _ -> [ "GotProofTime" ] - OpenProofSection _ -> + PageShowed _ -> [ "OpenProofSection" ] - CloseProofSection _ -> + ClaimingCancelled _ -> [ "CloseProofSection" ] - EnteredPhoto _ -> + PhotoAdded _ -> [ "EnteredPhoto" ] - CompletedPhotoUpload r -> + PhotoUploaded r -> [ "CompletedPhotoUpload", UR.resultToString r ] - GetUint64Name _ -> + AskedForUint64Name _ -> [ "GetUint64Name" ] GotUint64Name n -> [ "GotUint64Name", UR.resultToString n ] - GotClaimActionResponse r -> - [ "GotClaimActionResponse", UR.resultToString r ] - NoOp -> [ "NoOp" ] diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 698593617..52648a74c 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -843,9 +843,6 @@ update msg model = Action.update shared.translators actionMsg actionModel |> Just } - - _ = - Debug.log "Action msg" actionMsg in case ( model.actionToClaim, actionMsg ) of ( Nothing, Action.ClaimConfirmationOpen action ) -> @@ -857,22 +854,17 @@ update msg model = -- This case is fired from `GotAuthMsg` after user logs in if `actionToClaim` is presented in `model`. let _ = - Debug.log "Claiming action when isAuth is True" (isAuth model) - - ( proofPhoto, proofCode, proofTime ) = - case actionModel.proofData of - Just pd -> - ( pd.proofPhoto, pd.proofCode, pd.proofTime ) + Debug.log "Claiming action with no proof when isAuth is True" (isAuth model) - Nothing -> - ( "", "", 0 ) + ( emptyProofPhoto, emptyProofCode, emptyProofTime ) = + ( "", "", 0 ) claimedAction = { actionId = action.id , maker = model.accountName - , proofPhoto = proofPhoto - , proofCode = proofCode - , proofTime = proofTime + , proofPhoto = emptyProofPhoto + , proofCode = emptyProofCode + , proofTime = emptyProofTime } claimPort : JavascriptOutModel Msg @@ -887,10 +879,6 @@ update msg model = |> UR.addPort claimPort ( Just ({ action } as actionModel), Action.ActionClaimed False ) -> - let - _ = - Debug.log "Auth is " False - in updateClaimingAction actionModel |> askedAuthentication |> UR.init @@ -1103,8 +1091,8 @@ update msg model = in closeModal uResult |> UR.addExt AuthenticationSucceed - |> UR.addCmd cmd + --|> UR.addCmd cmd Auth.UpdatedShared newShared -> UR.mapModel (\m -> { m | shared = newShared }) From 5e2594c5e1e32945fadeaa0dfab25c4ff6675ff8 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Tue, 9 Feb 2021 08:07:41 +0300 Subject: [PATCH 53/91] Code fix/cleanup --- src/elm/Page/Community/ClaimWithPhoto.elm | 233 ++++++++++++---------- 1 file changed, 129 insertions(+), 104 deletions(-) diff --git a/src/elm/Page/Community/ClaimWithPhoto.elm b/src/elm/Page/Community/ClaimWithPhoto.elm index b5a66205e..42cbaa49e 100644 --- a/src/elm/Page/Community/ClaimWithPhoto.elm +++ b/src/elm/Page/Community/ClaimWithPhoto.elm @@ -11,7 +11,6 @@ module Page.Community.ClaimWithPhoto exposing import Action exposing (Action, ClaimConfirmationModalStatus(..)) import Api -import Community import Eos exposing (Symbol) import Eos.Account as Eos import File exposing (File) @@ -25,7 +24,6 @@ import Json.Decode as Decode import Json.Encode as Encode exposing (Value) import Page import Ports exposing (JavascriptOutModel) -import Route import Session.LoggedIn as LoggedIn exposing (External(..)) import Session.Shared exposing (Translators) import Sha256 exposing (sha256) @@ -34,20 +32,16 @@ import Time exposing (Posix) import UpdateResult as UR + +-- MODEL + + type alias Model = { status : Status , proof : Proof } -type alias ObjectiveId = - Int - - -type alias ActionId = - Int - - init : LoggedIn.Model -> Symbol -> ObjectiveId -> Maybe ActionId -> ( Model, Cmd Msg ) init loggedIn symbol objectiveId actionId = let @@ -73,18 +67,24 @@ init loggedIn symbol objectiveId actionId = ) -type Msg - = NoOp - | ActionLoaded (Result (Graphql.Http.Error (Maybe Action.Model)) (Maybe Action.Model)) - | PageShowed Action - | ClaimingCancelled ReasonToCloseProofSection - | GotProofTime Posix - | AskedForUint64Name String - | GotUint64Name (Result Value String) - | Tick Time.Posix - | PhotoAdded (List File) - | PhotoUploaded (Result Http.Error String) - | GotActionMsg Action.Msg + +-- TYPES + + +type Status + = Loading + | Loaded Action.Model + | LoadFailed (Graphql.Http.Error (Maybe Action.Model)) + | NotFound + | Expired + + +type alias ObjectiveId = + Int + + +type alias ActionId = + Int type Proof @@ -111,47 +111,8 @@ type ReasonToCloseProofSection | TimerEnded -viewPhotoUploader : Translators -> ProofPhotoStatus -> Html Msg -viewPhotoUploader { t } proofPhotoStatus = - let - uploadedAttrs = - case proofPhotoStatus of - Uploaded url -> - [ class " bg-no-repeat bg-center bg-cover" - , style "background-image" ("url(" ++ url ++ ")") - ] - - _ -> - [] - in - label - (class "relative bg-purple-500 w-full md:w-2/3 h-56 rounded-sm flex justify-center items-center cursor-pointer" - :: uploadedAttrs - ) - [ input - [ class "hidden-img-input" - , type_ "file" - , accept "image/*" - , Page.onFileChange PhotoAdded - , multiple False - ] - [] - , div [] - [ case proofPhotoStatus of - Uploading -> - div [ class "spinner spinner-light" ] [] - - Uploaded _ -> - span [ class "absolute bottom-0 right-0 mr-4 mb-4 bg-orange-300 w-8 h-8 p-2 rounded-full" ] - [ Icons.camera "" ] - _ -> - div [ class "text-white text-body font-bold text-center" ] - [ div [ class "w-10 mx-auto mb-2" ] [ Icons.camera "" ] - , div [] [ text (t "community.actions.proof.upload_photo_hint") ] - ] - ] - ] +-- VIEW view : LoggedIn.Model -> Model -> { title : String, content : Html Msg } @@ -162,12 +123,6 @@ view ({ shared } as loggedIn) model = content = case model.status of - Loading -> - Page.fullPageLoading shared - - Expired -> - Page.fullPageNotFound "Time has expired" "" - Loaded _ -> div [ class "bg-white" ] [ case loggedIn.actionToClaim of @@ -185,20 +140,23 @@ view ({ shared } as loggedIn) model = LoadFailed err -> Page.fullPageGraphQLError (t "error.invalidSymbol") err + Loading -> + Page.fullPageLoading shared + + Expired -> + Page.fullPageNotFound "Time has expired" "" + NotFound -> Page.fullPageNotFound (t "community.actions.form.not_found") "" in - { title = "Claim with photo" + { title = "Claim action with photo" , content = content } viewClaimWithProofs : Proof -> Translators -> Bool -> Action -> Html Msg -viewClaimWithProofs (Proof photoStatus proofCode) translators isAuth action = +viewClaimWithProofs (Proof photoStatus proofCode) ({ t } as translators) isAuth action = let - { t } = - translators - isUploadingInProgress = case photoStatus of Uploading -> @@ -211,9 +169,7 @@ viewClaimWithProofs (Proof photoStatus proofCode) translators isAuth action = [ div [ class "container p-4 mx-auto" ] [ div [ class "heading-bold leading-7 font-bold" ] [ text <| t "community.actions.proof.title" ] , p [ class "mb-4" ] - [ text <| - Maybe.withDefault "" action.photoProofInstructions - ] + [ text (Maybe.withDefault "" action.photoProofInstructions) ] , case proofCode of Just { code_, secondsAfterClaim, availabilityPeriod } -> case code_ of @@ -266,6 +222,49 @@ viewClaimWithProofs (Proof photoStatus proofCode) translators isAuth action = ] +viewPhotoUploader : Translators -> ProofPhotoStatus -> Html Msg +viewPhotoUploader { t } proofPhotoStatus = + let + uploadedAttrs = + case proofPhotoStatus of + Uploaded url -> + [ class "bg-no-repeat bg-center bg-cover" + , style "background-image" ("url(" ++ url ++ ")") + ] + + _ -> + [] + in + label + (class "relative bg-purple-500 w-full md:w-2/3 h-56 rounded-sm flex justify-center items-center cursor-pointer" + :: uploadedAttrs + ) + [ input + [ class "hidden-img-input" + , type_ "file" + , accept "image/*" + , Page.onFileChange PhotoAdded + , multiple False + ] + [] + , div [] + [ case proofPhotoStatus of + Uploading -> + div [ class "spinner spinner-light" ] [] + + Uploaded _ -> + span [ class "absolute bottom-0 right-0 mr-4 mb-4 bg-orange-300 w-8 h-8 p-2 rounded-full" ] + [ Icons.camera "" ] + + _ -> + div [ class "text-white text-body font-bold text-center" ] + [ div [ class "w-10 mx-auto mb-2" ] [ Icons.camera "" ] + , div [] [ text (t "community.actions.proof.upload_photo_hint") ] + ] + ] + ] + + viewProofCode : Translators -> String -> Int -> Int -> Html msg viewProofCode { t } proofCode secondsAfterClaim proofCodeValiditySeconds = let @@ -301,23 +300,22 @@ viewProofCode { t } proofCode secondsAfterClaim proofCodeValiditySeconds = ] -subscriptions : Model -> Sub Msg -subscriptions model = - case model.proof of - Proof _ (Just _) -> - Time.every 1000 Tick - _ -> - -- No timer needed if there's no proof code. - Sub.none +-- UPDATE -type Status - = Loading - | Loaded Action.Model - | LoadFailed (Graphql.Http.Error (Maybe Action.Model)) - | NotFound - | Expired +type Msg + = NoOp + | ActionLoaded (Result (Graphql.Http.Error (Maybe Action.Model)) (Maybe Action.Model)) + | PageShowed Action + | ClaimingCancelled ReasonToCloseProofSection + | GotProofTime Posix + | AskedForUint64Name String + | GotUint64Name (Result Value String) + | Tick Time.Posix + | PhotoAdded (List File) + | PhotoUploaded (Result Http.Error String) + | GotActionMsg Action.Msg update : Msg -> Model -> LoggedIn.Model -> UR.UpdateResult Model Msg (External Msg) @@ -335,8 +333,9 @@ update msg model ({ shared } as loggedIn) = |> UR.init |> UR.logGraphqlError msg err - ActionLoaded (Ok c) -> - case c of + ActionLoaded (Ok a) -> + -- TODO: This is a placeholder. Use real loaded action. + case a of Just action -> { model | status = Loading @@ -392,7 +391,8 @@ update msg model ({ shared } as loggedIn) = ( Expired, ShowFeedback LoggedIn.Failure (t "community.actions.proof.time_expired") ) CancelClicked -> - -- TODO: Redirect to the place where the user came from + -- TODO: Redirect to the place where the user came from. + -- TODO: Flush global action in loggedIn. ( NotFound, HideFeedback ) in { model @@ -425,10 +425,11 @@ update msg model ({ shared } as loggedIn) = model |> UR.init - GotUint64Name (Err _) -> + GotUint64Name (Err err) -> model |> UR.init |> UR.addExt (ShowFeedback LoggedIn.Failure "Failed while creating proof code.") + |> UR.logDebugValue msg err Tick timer -> case proofCode of @@ -448,12 +449,13 @@ update msg model ({ shared } as loggedIn) = | secondsAfterClaim = secondsAfterClaim } in - { model | proof = Proof photoStatus newProofCode } |> UR.init + { model | proof = Proof photoStatus newProofCode } + |> UR.init else update (ClaimingCancelled TimerEnded) model loggedIn - _ -> + Nothing -> model |> UR.init GotProofTime posix -> @@ -559,10 +561,14 @@ update msg model ({ shared } as loggedIn) = model |> UR.init + +-- INTEROP + + jsAddressToMsg : List String -> Value -> Maybe Msg jsAddressToMsg addr val = case addr of - "GetUint64Name" :: [] -> + "AskedForUint64Name" :: [] -> Decode.decodeValue (Decode.oneOf [ Decode.field "uint64name" Decode.string |> Decode.map Ok @@ -584,8 +590,8 @@ jsAddressToMsg addr val = msgToString : Msg -> List String msgToString msg = case msg of - ActionLoaded res -> - [ "CommunityLoaded" ] + ActionLoaded _ -> + [ "ActionLoaded" ] GotActionMsg actionMsg -> "GotActionMsg" :: Action.msgToString actionMsg @@ -597,19 +603,19 @@ msgToString msg = [ "GotProofTime" ] PageShowed _ -> - [ "OpenProofSection" ] + [ "PageShowed" ] ClaimingCancelled _ -> - [ "CloseProofSection" ] + [ "ClaimingCancelled" ] PhotoAdded _ -> - [ "EnteredPhoto" ] + [ "PhotoAdded" ] PhotoUploaded r -> - [ "CompletedPhotoUpload", UR.resultToString r ] + [ "PhotoUploaded", UR.resultToString r ] AskedForUint64Name _ -> - [ "GetUint64Name" ] + [ "AskedForUint64Name" ] GotUint64Name n -> [ "GotUint64Name", UR.resultToString n ] @@ -618,6 +624,25 @@ msgToString msg = [ "NoOp" ] + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + case model.proof of + Proof _ (Just _) -> + Time.every 1000 Tick + + _ -> + -- No timer needed if there's no proof code. + Sub.none + + + +-- HELPERS + + generateVerificationCode : Int -> String -> Int -> String generateVerificationCode actionId makerAccountUint64 proofTimeSeconds = (String.fromInt actionId From 485895d5a756c94d6d8a1ee6825e059ae3630feb Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Tue, 9 Feb 2021 08:17:56 +0300 Subject: [PATCH 54/91] Improve code organization Move handling all action messages into a separate function --- src/elm/Page/Community/ClaimWithPhoto.elm | 33 +++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/elm/Page/Community/ClaimWithPhoto.elm b/src/elm/Page/Community/ClaimWithPhoto.elm index 42cbaa49e..91e865b3e 100644 --- a/src/elm/Page/Community/ClaimWithPhoto.elm +++ b/src/elm/Page/Community/ClaimWithPhoto.elm @@ -310,7 +310,7 @@ type Msg | PageShowed Action | ClaimingCancelled ReasonToCloseProofSection | GotProofTime Posix - | AskedForUint64Name String + | AskedForUint64Name | GotUint64Name (Result Value String) | Tick Time.Posix | PhotoAdded (List File) @@ -402,7 +402,7 @@ update msg model ({ shared } as loggedIn) = |> UR.init |> UR.addExt ext - AskedForUint64Name _ -> + AskedForUint64Name -> model |> UR.init GotUint64Name (Ok uint64name) -> @@ -471,7 +471,7 @@ update msg model ({ shared } as loggedIn) = { model | proof = Proof NoPhotoAdded initProofCodeParts } |> UR.init |> UR.addPort - { responseAddress = AskedForUint64Name (Eos.nameToString loggedIn.accountName) + { responseAddress = AskedForUint64Name , responseData = Encode.null , data = Encode.object @@ -480,7 +480,21 @@ update msg model ({ shared } as loggedIn) = ] } - GotActionMsg ((Action.ActionClaimed isAuth) as actionMsg) -> + GotActionMsg actionMsg -> + handleActionMsg loggedIn actionMsg model + + NoOp -> + model |> UR.init + + +handleActionMsg : LoggedIn.Model -> Action.Msg -> Model -> UR.UpdateResult Model Msg (External Msg) +handleActionMsg ({ shared } as loggedIn) actionMsg model = + let + { t } = + shared.translators + in + case actionMsg of + Action.ActionClaimed isAuth -> case ( model.status, model.proof ) of ( Loaded actionModel, Proof (Uploaded url) proofCodeMatch ) -> let @@ -528,7 +542,7 @@ update msg model ({ shared } as loggedIn) = |> UR.init |> UR.addExt (ShowFeedback LoggedIn.Failure (t "community.actions.proof.no_photo_error")) - GotActionMsg (Action.GotActionClaimedResponse (Ok _)) -> + Action.GotActionClaimedResponse (Ok _) -> let message = shared.translators.tr "dashboard.check_claim.success" @@ -540,12 +554,12 @@ update msg model ({ shared } as loggedIn) = |> UR.addExt (UpdatedLoggedIn { loggedIn | actionToClaim = Nothing }) |> UR.addExt (ShowFeedback LoggedIn.Success message) - GotActionMsg (Action.GotActionClaimedResponse (Err _)) -> + Action.GotActionClaimedResponse (Err _) -> model |> UR.init |> UR.addExt (ShowFeedback LoggedIn.Failure (t "dashboard.check_claim.failure")) - GotActionMsg actionMsg -> + _ -> -- TODO: Just update the actionModel case model.status of Loaded actionModel -> @@ -557,9 +571,6 @@ update msg model ({ shared } as loggedIn) = _ -> model |> UR.init - NoOp -> - model |> UR.init - -- INTEROP @@ -614,7 +625,7 @@ msgToString msg = PhotoUploaded r -> [ "PhotoUploaded", UR.resultToString r ] - AskedForUint64Name _ -> + AskedForUint64Name -> [ "AskedForUint64Name" ] GotUint64Name n -> From ad8f8e82a23bca325b3b1a21efec6e851b11686c Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Tue, 9 Feb 2021 09:30:32 +0300 Subject: [PATCH 55/91] Improve code organization Move handling ActionMsg to separate funciton --- src/elm/Session/LoggedIn.elm | 174 ++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 83 deletions(-) diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index 52648a74c..2d6e3e0f6 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -835,88 +835,7 @@ update msg model = UR.init model GotActionMsg actionMsg -> - let - updateClaimingAction : Action.Model -> Model - updateClaimingAction actionModel = - { model - | actionToClaim = - Action.update shared.translators actionMsg actionModel - |> Just - } - in - case ( model.actionToClaim, actionMsg ) of - ( Nothing, Action.ClaimConfirmationOpen action ) -> - updateClaimingAction (Action.initClaimingActionModel action) - |> UR.init - - ( Just ({ action } as actionModel), Action.ActionClaimed True ) -> - -- Do the actual claiming via EOS. - -- This case is fired from `GotAuthMsg` after user logs in if `actionToClaim` is presented in `model`. - let - _ = - Debug.log "Claiming action with no proof when isAuth is True" (isAuth model) - - ( emptyProofPhoto, emptyProofCode, emptyProofTime ) = - ( "", "", 0 ) - - claimedAction = - { actionId = action.id - , maker = model.accountName - , proofPhoto = emptyProofPhoto - , proofCode = emptyProofCode - , proofTime = emptyProofTime - } - - claimPort : JavascriptOutModel Msg - claimPort = - Action.claimActionPort - (GotActionMsg actionMsg) - shared.contracts.community - claimedAction - in - updateClaimingAction actionModel - |> UR.init - |> UR.addPort claimPort - - ( Just ({ action } as actionModel), Action.ActionClaimed False ) -> - updateClaimingAction actionModel - |> askedAuthentication - |> UR.init - - ( Just ({ action } as actionModel), Action.ActionWithPhotoLinkClicked route ) -> - -- TODO: pass the route to go back to `ActionWithPhotoLinkClicked` when Cancel clicked? - let - _ = - Debug.log "model, photo" ( model.actionToClaim, route ) - in - updateClaimingAction actionModel - |> update SearchClosed - |> UR.addCmd (Route.replaceUrl model.shared.navKey route) - - ( Just _, Action.GotActionClaimedResponse resp ) -> - let - showMessage = - case resp of - Ok _ -> - tr "dashboard.check_claim.success" - [ ( "symbolCode", Eos.symbolToSymbolCodeString model.selectedCommunity ) ] - |> Show Success - - Err _ -> - Show Failure (t "dashboard.check_claim.failure") - in - -- Flush claimed action. - { model | actionToClaim = Nothing } - |> (\m -> { m | feedback = showMessage }) - |> UR.init - - ( Just ca, _ ) -> - updateClaimingAction ca - |> UR.init - - _ -> - model - |> UR.init + handleActionMsg model actionMsg SearchClosed -> { model @@ -1091,8 +1010,8 @@ update msg model = in closeModal uResult |> UR.addExt AuthenticationSucceed + |> UR.addCmd cmd - --|> UR.addCmd cmd Auth.UpdatedShared newShared -> UR.mapModel (\m -> { m | shared = newShared }) @@ -1153,6 +1072,95 @@ update msg model = |> UR.addCmd doNext +handleActionMsg : Model -> Action.Msg -> UpdateResult +handleActionMsg ({ shared } as model) actionMsg = + let + { t, tr } = + shared.translators + + updateClaimingAction : Action.Model -> Model + updateClaimingAction actionModel = + { model + | actionToClaim = + Action.update shared.translators actionMsg actionModel + |> Just + } + in + case ( model.actionToClaim, actionMsg ) of + ( Nothing, Action.ClaimConfirmationOpen action ) -> + updateClaimingAction (Action.initClaimingActionModel action) + |> UR.init + + ( Just ({ action } as actionModel), Action.ActionClaimed True ) -> + -- Do the actual claiming via EOS. + -- This case is fired from `GotAuthMsg` after user logs in if `actionToClaim` is presented in `model`. + let + _ = + Debug.log "Claiming action with no proof when isAuth is True" (isAuth model) + + ( emptyProofPhoto, emptyProofCode, emptyProofTime ) = + ( "", "", 0 ) + + claimedAction = + { actionId = action.id + , maker = model.accountName + , proofPhoto = emptyProofPhoto + , proofCode = emptyProofCode + , proofTime = emptyProofTime + } + + claimPort : JavascriptOutModel Msg + claimPort = + Action.claimActionPort + (GotActionMsg actionMsg) + shared.contracts.community + claimedAction + in + updateClaimingAction actionModel + |> UR.init + |> UR.addPort claimPort + + ( Just ({ action } as actionModel), Action.ActionClaimed False ) -> + updateClaimingAction actionModel + |> askedAuthentication + |> UR.init + + ( Just ({ action } as actionModel), Action.ActionWithPhotoLinkClicked route ) -> + -- TODO: pass the route to go back to `ActionWithPhotoLinkClicked` when Cancel clicked? + let + _ = + Debug.log "model, photo" ( model.actionToClaim, route ) + in + updateClaimingAction actionModel + |> update SearchClosed + |> UR.addCmd (Route.replaceUrl model.shared.navKey route) + + ( Just _, Action.GotActionClaimedResponse resp ) -> + let + showMessage = + case resp of + Ok _ -> + tr "dashboard.check_claim.success" + [ ( "symbolCode", Eos.symbolToSymbolCodeString model.selectedCommunity ) ] + |> Show Success + + Err _ -> + Show Failure (t "dashboard.check_claim.failure") + in + -- Flush claimed action. + { model | actionToClaim = Nothing } + |> (\m -> { m | feedback = showMessage }) + |> UR.init + + ( Just ca, _ ) -> + updateClaimingAction ca + |> UR.init + + _ -> + model + |> UR.init + + closeModal : UpdateResult -> UpdateResult closeModal ({ model } as uResult) = { uResult From c514b9cebb6d3a45a7f93dd15acca6202bc828a7 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Tue, 9 Feb 2021 10:03:41 +0300 Subject: [PATCH 56/91] Improve the code organization --- src/elm/Page/Community.elm | 43 ++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/src/elm/Page/Community.elm b/src/elm/Page/Community.elm index 41fc2c10b..6d4748b10 100755 --- a/src/elm/Page/Community.elm +++ b/src/elm/Page/Community.elm @@ -307,7 +307,7 @@ type Msg -- Objective | ClickedOpenObjective Int | ClickedCloseObjective - | GotCommunityActionMsg Action.Msg + | GotActionMsg Action.Msg update : Msg -> Model -> LoggedIn.Model -> UpdateResult @@ -323,34 +323,27 @@ update msg model ({ shared } as loggedIn) = GotTime date -> UR.init { model | date = Just date } - GotCommunityActionMsg actionMsg -> + GotActionMsg ((Action.ClaimConfirmationOpen a) as actionMsg) -> let _ = - Debug.log "actionMsg from Community" ( actionMsg, loggedIn.actionToClaim ) - - loggedInWithUpdatedClaimingAction = - case loggedIn.actionToClaim of - Just a -> - { loggedIn - | actionToClaim = - Action.update shared.translators actionMsg a - |> Just - } + Debug.log "opened" actionMsg + in + model + |> UR.init + |> UR.addExt + (UpdatedLoggedIn + { loggedIn + | actionToClaim = Just (Action.initClaimingActionModel a) + } + ) - Nothing -> - case actionMsg of - Action.ClaimConfirmationOpen a -> - { loggedIn - | actionToClaim = Just (Action.initClaimingActionModel a) - } - - _ -> - loggedIn + GotActionMsg a -> + let + _ = + Debug.log "unhandled action message from community" a in model |> UR.init - -- TODO: This doesn't handle all `LoggedIn.GotActionMsg` cases (e.g. Claim with Photo) - |> UR.addExt (UpdatedLoggedIn loggedInWithUpdatedClaimingAction) CompletedLoadCommunity (Ok community) -> case community of @@ -394,7 +387,7 @@ msgToString msg = GotTime _ -> [ "GotTime" ] - GotCommunityActionMsg _ -> + GotActionMsg _ -> [ "GotCommunityActionMsg" ] CompletedLoadCommunity r -> @@ -535,7 +528,7 @@ viewAction translators canEdit symbol maybeDate action = NoOp else - (GotCommunityActionMsg << Action.ClaimConfirmationOpen) action + (GotActionMsg << Action.ClaimConfirmationOpen) action ) ] [ if action.hasProofPhoto then From c230240bd63815039ff6d248d599b0290fe8b544 Mon Sep 17 00:00:00 2001 From: Andrey Miskov Date: Tue, 9 Feb 2021 10:16:40 +0300 Subject: [PATCH 57/91] Fix GraphQl types - Rename Profile to User - Add `contacts` field placeholder to the mutation for the Edit Profile form --- src/elm/Cambiatus/Enum/ContactType.elm | 98 +++++++++++++ src/elm/Cambiatus/InputObject.elm | 133 +++++++++++------- src/elm/Cambiatus/Mutation.elm | 26 ++-- src/elm/Cambiatus/Object.elm | 16 ++- src/elm/Cambiatus/Object/Action.elm | 4 +- src/elm/Cambiatus/Object/Check.elm | 2 +- src/elm/Cambiatus/Object/Claim.elm | 2 +- src/elm/Cambiatus/Object/Community.elm | 4 +- src/elm/Cambiatus/Object/Contact.elm | 30 ++++ src/elm/Cambiatus/Object/Invite.elm | 2 +- src/elm/Cambiatus/Object/Mint.elm | 2 +- src/elm/Cambiatus/Object/Network.elm | 2 +- .../Cambiatus/Object/NotificationHistory.elm | 2 +- src/elm/Cambiatus/Object/Objective.elm | 2 +- src/elm/Cambiatus/Object/Order.elm | 4 +- src/elm/Cambiatus/Object/Product.elm | 2 +- src/elm/Cambiatus/Object/SearchResult.elm | 59 ++++++++ src/elm/Cambiatus/Object/SignUpResponse.elm | 14 +- src/elm/Cambiatus/Object/Transfer.elm | 4 +- .../Object/{Profile.elm => User.elm} | 58 +++++--- src/elm/Cambiatus/Query.elm | 32 +++-- src/elm/Community.elm | 2 +- src/elm/Page/PaymentHistory.elm | 12 +- src/elm/Page/Profile/Claims.elm | 6 +- src/elm/Profile.elm | 13 +- src/elm/Shop.elm | 4 +- src/elm/Transfer.elm | 7 +- 27 files changed, 406 insertions(+), 136 deletions(-) create mode 100644 src/elm/Cambiatus/Enum/ContactType.elm create mode 100644 src/elm/Cambiatus/Object/Contact.elm create mode 100644 src/elm/Cambiatus/Object/SearchResult.elm rename src/elm/Cambiatus/Object/{Profile.elm => User.elm} (77%) diff --git a/src/elm/Cambiatus/Enum/ContactType.elm b/src/elm/Cambiatus/Enum/ContactType.elm new file mode 100644 index 000000000..d66bcec81 --- /dev/null +++ b/src/elm/Cambiatus/Enum/ContactType.elm @@ -0,0 +1,98 @@ +-- Do not manually edit this file, it was auto-generated by dillonkearns/elm-graphql +-- https://github.com/dillonkearns/elm-graphql + + +module Cambiatus.Enum.ContactType exposing (..) + +import Json.Decode as Decode exposing (Decoder) + + +{-| + + - Instagram - An Instagram account. Must have full URL like + - Phone - A regular phone number + - Telegram - An username or phone number for Telegram. Must be or + - Whatsapp - A phone number used in Whatsapp. Regular international phone number + +-} +type ContactType + = Instagram + | Phone + | Telegram + | Whatsapp + + +list : List ContactType +list = + [ Instagram, Phone, Telegram, Whatsapp ] + + +decoder : Decoder ContactType +decoder = + Decode.string + |> Decode.andThen + (\string -> + case string of + "INSTAGRAM" -> + Decode.succeed Instagram + + "PHONE" -> + Decode.succeed Phone + + "TELEGRAM" -> + Decode.succeed Telegram + + "WHATSAPP" -> + Decode.succeed Whatsapp + + _ -> + Decode.fail ("Invalid ContactType type, " ++ string ++ " try re-running the @dillonkearns/elm-graphql CLI ") + ) + + +{-| Convert from the union type representing the Enum to a string that the GraphQL server will recognize. +-} +toString : ContactType -> String +toString enum = + case enum of + Instagram -> + "INSTAGRAM" + + Phone -> + "PHONE" + + Telegram -> + "TELEGRAM" + + Whatsapp -> + "WHATSAPP" + + +{-| Convert from a String representation to an elm representation enum. +This is the inverse of the Enum `toString` function. So you can call `toString` and then convert back `fromString` safely. + + Swapi.Enum.Episode.NewHope + |> Swapi.Enum.Episode.toString + |> Swapi.Enum.Episode.fromString + == Just NewHope + +This can be useful for generating Strings to use for