diff --git a/public/images/girl-with-ice-cube.svg b/public/images/girl-with-ice-cube.svg new file mode 100644 index 000000000..07a9c5c25 --- /dev/null +++ b/public/images/girl-with-ice-cube.svg @@ -0,0 +1 @@ + diff --git a/public/translations/amh-ETH.json b/public/translations/amh-ETH.json index 8919b863d..4c3ff260d 100644 --- a/public/translations/amh-ETH.json +++ b/public/translations/amh-ETH.json @@ -1249,5 +1249,11 @@ "eos_plural": "Eos", "eth_plural": "ቢትኮይን", "usd_plural": "ዶላር" + }, + "permissions": { + "insufficient": "እርምጃ አይፈቀድም", + "explanation": "ይህን እርምጃ ለመፈጸም የሚያስፈልጉት ፈቃዶች የሎትም። ተጨማሪ ፈቃዶችን እንዴት ማግኘት እንደሚችሉ ለማወቅ የማህበረሰብ አስተዳዳሪዎችን ያግኙ።", + "try_again": "እባክዎ በቂ ፍቃዶች ካገኙ በኋላ እንደገና ይሞክሩ!", + "ok": "ገብቶኛል" } } diff --git a/public/translations/cat-CAT.json b/public/translations/cat-CAT.json index 28e0873b3..4e5dfe466 100755 --- a/public/translations/cat-CAT.json +++ b/public/translations/cat-CAT.json @@ -1290,5 +1290,13 @@ "eos_plural": "Eos", "eth_plural": "Ethereum", "usd_plural": "Dòlars" + }, + "permissions": { + "insufficient": { + "title": "Acció no permesa", + "explanation": "No teniu els permisos necessaris per dur a terme aquesta acció. Poseu-vos en contacte amb els administradors de la comunitat per saber com obtenir més permisos.", + "try_again": "Si us plau, torneu-ho a provar quan tingueu prou permisos!", + "ok": "Entès" + } } } diff --git a/public/translations/en-US.json b/public/translations/en-US.json index 6e3935c27..26b351740 100755 --- a/public/translations/en-US.json +++ b/public/translations/en-US.json @@ -1288,5 +1288,13 @@ "eos_plural": "Eos", "eth_plural": "Ethereum", "usd_plural": "Dollars" + }, + "permissions": { + "insufficient": { + "title": "Action not allowed", + "explanation": "You don't have the permissions needed to perform this action. Reach out to the community admins to know how to get more permissions.", + "try_again": "Please try again once you have enough permissions!", + "ok": "Got it" + } } } diff --git a/public/translations/es-ES.json b/public/translations/es-ES.json index 639d9bcd0..f5623dbfd 100755 --- a/public/translations/es-ES.json +++ b/public/translations/es-ES.json @@ -1291,5 +1291,13 @@ "eos_plural": "Eos", "eth_plural": "Ethereum", "usd_plural": "Dolares" + }, + "permissions": { + "insufficient": { + "title": "Acción no permitida", + "explanation": "No tienes los permisos necesarios para realizar esta acción. Comuníquese con los administradores de la comunidad para saber cómo obtener más permisos.", + "try_again": "¡Vuelva a intentarlo una vez que tenga suficientes permisos!", + "ok": "Entendí" + } } } diff --git a/public/translations/pt-BR.json b/public/translations/pt-BR.json index fb6a33af9..b1eb7a44d 100755 --- a/public/translations/pt-BR.json +++ b/public/translations/pt-BR.json @@ -1294,5 +1294,13 @@ "eos_plural": "Eos", "eth_plural": "Ethereum", "usd_plural": "Dólares" + }, + "permissions": { + "insufficient": { + "title": "Ação não permitida", + "explanation": "Você não tem as permissões necessárias para realizar esta ação. Contate os administradores da comunidade para saber como ganhar mais permissões.", + "try_again": "Por favor tente novamente quando tiver permissões o suficiente!", + "ok": "Entendi" + } } } diff --git a/src/elm/Action.elm b/src/elm/Action.elm index 637ef63fd..90b582bff 100644 --- a/src/elm/Action.elm +++ b/src/elm/Action.elm @@ -3,6 +3,7 @@ module Action exposing , ActionFeedback(..) , ClaimingActionStatus(..) , Community + , ExternalMsg(..) , Model , Msg(..) , Objective @@ -20,6 +21,7 @@ module Action exposing , viewSearchActions ) +import Cambiatus.Enum.Permission as Permission exposing (Permission) import Cambiatus.Enum.VerificationType as VerificationType exposing (VerificationType) import Cambiatus.Object import Cambiatus.Object.Action as ActionObject @@ -127,19 +129,29 @@ type Msg | Tick Time.Posix +type ExternalMsg + = SentFeedback Feedback.Model + | ShowInsufficientPermissions + + update : Bool + -> List Permission -> Shared -> Symbol -> Eos.Name -> Msg -> Model - -> UR.UpdateResult Model Msg Feedback.Model -update isPinConfirmed shared selectedCommunity accName msg model = + -> UR.UpdateResult Model Msg ExternalMsg +update isPinConfirmed permissions shared selectedCommunity accName msg model = let { t, tr } = shared.translators + hasPermissions necessaryPermissions = + List.all (\permission -> List.member permission permissions) + necessaryPermissions + claimOrAskForPin actionId photoUrl code time m = if isPinConfirmed then m @@ -148,7 +160,8 @@ update isPinConfirmed shared selectedCommunity accName msg model = (claimActionPort msg shared.contracts.community - { actionId = actionId + { communityId = selectedCommunity + , actionId = actionId , maker = accName , proofPhoto = photoUrl , proofCode = code @@ -168,12 +181,18 @@ update isPinConfirmed shared selectedCommunity accName msg model = |> UR.init ( ActionClaimed action Nothing, _ ) -> - { model - | status = ClaimInProgress action Nothing - , feedback = Nothing - , needsPinConfirmation = not isPinConfirmed - } - |> claimOrAskForPin action.id "" "" 0 + if hasPermissions [ Permission.Claim ] then + { model + | status = ClaimInProgress action Nothing + , feedback = Nothing + , needsPinConfirmation = not isPinConfirmed + } + |> claimOrAskForPin action.id "" "" 0 + + else + { model | status = NotAsked } + |> UR.init + |> UR.addExt ShowInsufficientPermissions ( ActionClaimed action (Just proofRecord), _ ) -> let @@ -185,21 +204,27 @@ update isPinConfirmed shared selectedCommunity accName msg model = Proof _ Nothing -> ( "", 0 ) in - case proofRecord.image of - Just image -> - { model - | status = ClaimInProgress action (Just proofRecord) - , feedback = Nothing - , needsPinConfirmation = not isPinConfirmed - } - |> claimOrAskForPin action.id image proofCode time + if hasPermissions [ Permission.Claim ] then + case proofRecord.image of + Just image -> + { model + | status = ClaimInProgress action (Just proofRecord) + , feedback = Nothing + , needsPinConfirmation = not isPinConfirmed + } + |> claimOrAskForPin action.id image proofCode time - Nothing -> - { model - | feedback = Failure (t "community.actions.proof.no_upload_error") |> Just - , needsPinConfirmation = False - } - |> UR.init + Nothing -> + { model + | feedback = Failure (t "community.actions.proof.no_upload_error") |> Just + , needsPinConfirmation = False + } + |> UR.init + + else + { model | status = NotAsked } + |> UR.init + |> UR.addExt ShowInsufficientPermissions ( AgreedToClaimWithProof action, _ ) -> { model @@ -339,7 +364,7 @@ update isPinConfirmed shared selectedCommunity accName msg model = } ) GotFormMsg - UR.addExt + (SentFeedback >> UR.addExt) model ( GotFormMsg subMsg, PhotoUploaderShowed action (Proof formModel proofCode) ) -> @@ -349,7 +374,7 @@ update isPinConfirmed shared selectedCommunity accName msg model = { model | status = PhotoUploaderShowed action (Proof newForm proofCode) } ) GotFormMsg - UR.addExt + (SentFeedback >> UR.addExt) model ( Tick timer, PhotoUploaderShowed action (Proof photoStatus (Just proofCode)) ) -> @@ -692,7 +717,8 @@ encode action = { symbol = action.objective.community.symbol, amount = amount } in Encode.object - [ ( "action_id", Encode.int action.id ) + [ ( "community_id", Eos.encodeSymbol action.objective.community.symbol ) + , ( "action_id", Encode.int action.id ) , ( "objective_id", Encode.int action.objective.id ) , ( "description", Markdown.encode action.description ) , ( "reward", Eos.encodeAsset (makeAsset action.reward) ) @@ -722,6 +748,7 @@ encode action = , ( "has_proof_photo", Eos.encodeEosBool (Eos.boolToEosBool action.hasProofPhoto) ) , ( "has_proof_code", Eos.encodeEosBool (Eos.boolToEosBool action.hasProofCode) ) , ( "photo_proof_instructions", Markdown.encode (Maybe.withDefault Markdown.empty action.photoProofInstructions) ) + , ( "image", Encode.string "" ) ] @@ -738,7 +765,7 @@ updateAction accountName shared action = claimActionPort : msg -> String -> ClaimedAction -> Ports.JavascriptOutModel msg -claimActionPort msg contractsCommunity { actionId, maker, proofPhoto, proofCode, proofTime } = +claimActionPort msg contractsCommunity action = { responseAddress = msg , responseData = Encode.null , data = @@ -746,24 +773,18 @@ claimActionPort msg contractsCommunity { actionId, maker, proofPhoto, proofCode, [ { accountName = contractsCommunity , name = "claimaction" , authorization = - { actor = maker + { actor = action.maker , permissionName = Eos.samplePermission } - , data = - { actionId = actionId - , maker = maker - , proofPhoto = proofPhoto - , proofCode = proofCode - , proofTime = proofTime - } - |> encodeClaimAction + , data = encodeClaimAction action } ] } type alias ClaimedAction = - { actionId : Int + { communityId : Symbol + , actionId : Int , maker : Eos.Name , proofPhoto : String , proofCode : String @@ -774,7 +795,8 @@ type alias ClaimedAction = encodeClaimAction : ClaimedAction -> Encode.Value encodeClaimAction c = Encode.object - [ ( "action_id", Encode.int c.actionId ) + [ ( "community_id", Eos.encodeSymbol c.communityId ) + , ( "action_id", Encode.int c.actionId ) , ( "maker", Eos.encodeName c.maker ) , ( "proof_photo", Encode.string c.proofPhoto ) , ( "proof_code", Encode.string c.proofCode ) diff --git a/src/elm/Claim.elm b/src/elm/Claim.elm index 855b9b5a7..c06faf1d1 100644 --- a/src/elm/Claim.elm +++ b/src/elm/Claim.elm @@ -145,8 +145,8 @@ pendingValidators claim = claim.action.validators -encodeVerification : ClaimId -> Eos.Name -> Bool -> Encode.Value -encodeVerification claimId validator vote = +encodeVerification : ClaimId -> Eos.Name -> Bool -> Eos.Symbol -> Encode.Value +encodeVerification claimId validator vote communityId = let encodedClaimId : Encode.Value encodedClaimId = @@ -163,7 +163,8 @@ encodeVerification claimId validator vote = |> Eos.encodeEosBool in Encode.object - [ ( "claim_id", encodedClaimId ) + [ ( "community_id", Eos.encodeSymbol communityId ) + , ( "claim_id", encodedClaimId ) , ( "verifier", encodedVerifier ) , ( "vote", encodedVote ) ] diff --git a/src/elm/Community.elm b/src/elm/Community.elm index 1babc7aa0..0110b72fe 100755 --- a/src/elm/Community.elm +++ b/src/elm/Community.elm @@ -461,7 +461,7 @@ objectiveSelectionSet = type alias CreateObjectiveAction = - { asset : Eos.Asset + { communityId : Eos.Symbol , description : Markdown , creator : Eos.Name } @@ -470,14 +470,16 @@ type alias CreateObjectiveAction = encodeCreateObjectiveAction : CreateObjectiveAction -> Value encodeCreateObjectiveAction c = Encode.object - [ ( "cmm_asset", Eos.encodeAsset c.asset ) + [ ( "community_id", Eos.encodeSymbol c.communityId ) + , ( "objective_id", Encode.int 0 ) , ( "description", Markdown.encode c.description ) - , ( "creator", Eos.encodeName c.creator ) + , ( "editor", Eos.encodeName c.creator ) ] type alias UpdateObjectiveAction = - { objectiveId : Int + { communityId : Eos.Symbol + , objectiveId : Int , description : Markdown , editor : Eos.Name } @@ -486,7 +488,8 @@ type alias UpdateObjectiveAction = encodeUpdateObjectiveAction : UpdateObjectiveAction -> Value encodeUpdateObjectiveAction c = Encode.object - [ ( "objective_id", Encode.int c.objectiveId ) + [ ( "community_id", Eos.encodeSymbol c.communityId ) + , ( "objective_id", Encode.int c.objectiveId ) , ( "description", Markdown.encode c.description ) , ( "editor", Eos.encodeName c.editor ) ] diff --git a/src/elm/Page/Community/ActionEditor.elm b/src/elm/Page/Community/ActionEditor.elm index b28dede56..eb4b25e62 100755 --- a/src/elm/Page/Community/ActionEditor.elm +++ b/src/elm/Page/Community/ActionEditor.elm @@ -285,6 +285,7 @@ update msg model ({ shared } as loggedIn) = formOutput ) |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } @@ -300,6 +301,7 @@ update msg model ({ shared } as loggedIn) = formOutput ) |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } diff --git a/src/elm/Page/Community/New.elm b/src/elm/Page/Community/New.elm index 0cf9b843a..35d4a8964 100755 --- a/src/elm/Page/Community/New.elm +++ b/src/elm/Page/Community/New.elm @@ -412,6 +412,7 @@ update msg model loggedIn = ] } |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } |> UR.addBreadcrumb diff --git a/src/elm/Page/Community/ObjectiveEditor.elm b/src/elm/Page/Community/ObjectiveEditor.elm index abbe0e7c4..d6c65e67a 100644 --- a/src/elm/Page/Community/ObjectiveEditor.elm +++ b/src/elm/Page/Community/ObjectiveEditor.elm @@ -724,13 +724,13 @@ update msg model loggedIn = , data = Eos.encodeTransaction [ { accountName = loggedIn.shared.contracts.community - , name = "newobjective" + , name = "upsertobjctv" , authorization = { actor = loggedIn.accountName , permissionName = Eos.samplePermission } , data = - { asset = Eos.Asset 0 community.symbol + { communityId = community.symbol , description = description , creator = loggedIn.accountName } @@ -739,6 +739,7 @@ update msg model loggedIn = ] } |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } @@ -756,13 +757,14 @@ update msg model loggedIn = , data = Eos.encodeTransaction [ { accountName = loggedIn.shared.contracts.community - , name = "updobjective" + , name = "upsertobjctv" , authorization = { actor = loggedIn.accountName , permissionName = Eos.samplePermission } , data = - { objectiveId = objective.id + { communityId = objective.community.symbol + , objectiveId = objective.id , description = description , editor = loggedIn.accountName } @@ -771,6 +773,7 @@ update msg model loggedIn = ] } |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } @@ -889,6 +892,7 @@ completeActionOrObjective loggedIn model msg completionStatus objective = ) ) >> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } diff --git a/src/elm/Page/Community/Settings/Currency.elm b/src/elm/Page/Community/Settings/Currency.elm index e54134497..fa690026d 100644 --- a/src/elm/Page/Community/Settings/Currency.elm +++ b/src/elm/Page/Community/Settings/Currency.elm @@ -146,6 +146,7 @@ update msg model ({ shared } as loggedIn) = ) } |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } diff --git a/src/elm/Page/Community/Settings/Features.elm b/src/elm/Page/Community/Settings/Features.elm index aaebfbdff..393960e1e 100644 --- a/src/elm/Page/Community/Settings/Features.elm +++ b/src/elm/Page/Community/Settings/Features.elm @@ -221,6 +221,7 @@ update msg model loggedIn = |> UR.init |> saveFeaturePort loggedIn Shop model.status state |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } @@ -229,6 +230,7 @@ update msg model loggedIn = |> UR.init |> saveFeaturePort loggedIn Objectives model.status state |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } diff --git a/src/elm/Page/Community/Settings/Info.elm b/src/elm/Page/Community/Settings/Info.elm index e99756558..65aad8cb7 100644 --- a/src/elm/Page/Community/Settings/Info.elm +++ b/src/elm/Page/Community/Settings/Info.elm @@ -331,6 +331,7 @@ update msg model ({ shared } as loggedIn) = } |> addCoverPhoto |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } |> UR.addBreadcrumb diff --git a/src/elm/Page/Community/Transfer.elm b/src/elm/Page/Community/Transfer.elm index e0d8a6bc1..155fbb58a 100644 --- a/src/elm/Page/Community/Transfer.elm +++ b/src/elm/Page/Community/Transfer.elm @@ -445,6 +445,7 @@ update msg model ({ shared } as loggedIn) = ] } |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } diff --git a/src/elm/Page/Dashboard.elm b/src/elm/Page/Dashboard.elm index a68132793..9469f6379 100755 --- a/src/elm/Page/Dashboard.elm +++ b/src/elm/Page/Dashboard.elm @@ -14,6 +14,7 @@ import Api.Relay import Avatar import Browser.Dom import Cambiatus.Enum.Direction +import Cambiatus.Enum.Permission as Permission import Cambiatus.Enum.TransferDirectionValue as TransferDirectionValue exposing (TransferDirectionValue) import Cambiatus.InputObject import Cambiatus.Query @@ -1173,19 +1174,36 @@ update msg model ({ shared, accountName } as loggedIn) = |> UR.init CreateInvite -> - case model.balance of - RemoteData.Success (Just b) -> - UR.init - { model | inviteModalStatus = InviteModalLoading } - |> UR.addCmd - (CompletedInviteCreation - |> Api.communityInvite loggedIn.shared b.asset.symbol loggedIn.accountName - ) + case loggedIn.profile of + RemoteData.Success profile -> + if LoggedIn.hasPermissions profile [ Permission.Invite ] then + case model.balance of + RemoteData.Success (Just b) -> + UR.init + { model | inviteModalStatus = InviteModalLoading } + |> UR.addCmd + (CompletedInviteCreation + |> Api.communityInvite loggedIn.shared b.asset.symbol loggedIn.accountName + ) + + _ -> + UR.init model + |> UR.logImpossible msg + "Created invitation, but balance wasn't loaded" + (Just loggedIn.accountName) + { moduleName = "Page.Dashboard", function = "update" } + [] + + else + model + |> UR.init + |> UR.addExt LoggedIn.ShowInsufficientPermissionsModal _ -> - UR.init model + model + |> UR.init |> UR.logImpossible msg - "Created invitation, but balance wasn't loaded" + "Tried creating invitation, but profile wasn't loaded" (Just loggedIn.accountName) { moduleName = "Page.Dashboard", function = "update" } [] diff --git a/src/elm/Page/Dashboard/Analysis.elm b/src/elm/Page/Dashboard/Analysis.elm index 1fb8c234e..8bde16a1c 100644 --- a/src/elm/Page/Dashboard/Analysis.elm +++ b/src/elm/Page/Dashboard/Analysis.elm @@ -11,6 +11,7 @@ module Page.Dashboard.Analysis exposing import Api.Relay import Cambiatus.Enum.Direction +import Cambiatus.Enum.Permission as Permission import Cambiatus.Query import Claim import Community @@ -571,8 +572,8 @@ update msg model loggedIn = |> UR.init VoteClaim claimId vote -> - case model.status of - RemoteData.Success _ -> + case ( model.status, loggedIn.selectedCommunity ) of + ( RemoteData.Success _, RemoteData.Success community ) -> let newModel = { model @@ -591,11 +592,12 @@ update msg model loggedIn = { actor = loggedIn.accountName , permissionName = Eos.samplePermission } - , data = Claim.encodeVerification claimId loggedIn.accountName vote + , data = Claim.encodeVerification claimId loggedIn.accountName vote community.symbol } ] } |> LoggedIn.withPrivateKey loggedIn + [ Permission.Verify ] model { successMsg = msg, errorMsg = ClosedAuthModal } diff --git a/src/elm/Page/Dashboard/Claim.elm b/src/elm/Page/Dashboard/Claim.elm index bea53fd08..f9b9ece62 100644 --- a/src/elm/Page/Dashboard/Claim.elm +++ b/src/elm/Page/Dashboard/Claim.elm @@ -1,6 +1,7 @@ module Page.Dashboard.Claim exposing (Model, Msg, init, jsAddressToMsg, msgToString, update, view) import Action +import Cambiatus.Enum.Permission as Permission import Cambiatus.Query import Claim import Dict @@ -527,7 +528,7 @@ update msg model loggedIn = VoteClaim claimId vote -> case model.statusClaim of - Loaded _ _ -> + Loaded claim _ -> let newModel = { model @@ -546,7 +547,11 @@ update msg model loggedIn = { actor = loggedIn.accountName , permissionName = Eos.samplePermission } - , data = Claim.encodeVerification claimId loggedIn.accountName vote + , data = + Claim.encodeVerification claimId + loggedIn.accountName + vote + claim.action.objective.community.symbol } ] } @@ -562,6 +567,7 @@ update msg model loggedIn = , level = Log.Info } |> LoggedIn.withPrivateKey loggedIn + [ Permission.Verify ] model { successMsg = msg, errorMsg = ClosedAuthModal } diff --git a/src/elm/Page/Profile.elm b/src/elm/Page/Profile.elm index 00725b2ae..040a82320 100755 --- a/src/elm/Page/Profile.elm +++ b/src/elm/Page/Profile.elm @@ -494,6 +494,7 @@ update msg model loggedIn = ] } |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = Ignored } @@ -523,6 +524,7 @@ update msg model loggedIn = } |> UR.init |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = Ignored } @@ -573,6 +575,7 @@ update msg model loggedIn = ] } |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = Ignored } diff --git a/src/elm/Page/Profile/Claims.elm b/src/elm/Page/Profile/Claims.elm index 407f22b69..251e9ae32 100644 --- a/src/elm/Page/Profile/Claims.elm +++ b/src/elm/Page/Profile/Claims.elm @@ -9,6 +9,7 @@ module Page.Profile.Claims exposing , view ) +import Cambiatus.Enum.Permission as Permission import Cambiatus.Object import Cambiatus.Object.User as Profile import Cambiatus.Query @@ -488,8 +489,8 @@ update msg model loggedIn = |> UR.init VoteClaim claimId vote -> - case model.status of - Loaded _ _ -> + case ( model.status, loggedIn.selectedCommunity ) of + ( Loaded _ _, RemoteData.Success community ) -> let newModel = { model @@ -508,11 +509,16 @@ update msg model loggedIn = { actor = loggedIn.accountName , permissionName = Eos.samplePermission } - , data = Claim.encodeVerification claimId loggedIn.accountName vote + , data = + Claim.encodeVerification claimId + loggedIn.accountName + vote + community.symbol } ] } |> LoggedIn.withPrivateKey loggedIn + [ Permission.Verify ] model { successMsg = msg, errorMsg = ClosedAuthModal } diff --git a/src/elm/Page/Shop/Editor.elm b/src/elm/Page/Shop/Editor.elm index d2768bf1b..38a7e1948 100755 --- a/src/elm/Page/Shop/Editor.elm +++ b/src/elm/Page/Shop/Editor.elm @@ -10,6 +10,7 @@ module Page.Shop.Editor exposing ) import Api +import Cambiatus.Enum.Permission as Permission import Community exposing (Balance) import Eos exposing (Symbol) import Eos.Account as Eos @@ -553,6 +554,7 @@ update msg model loggedIn = "createsale" (encodeCreateForm loggedIn formOutput) |> LoggedIn.withPrivateKey loggedIn + [ Permission.Sell ] model { successMsg = msg, errorMsg = ClosedAuthModal } @@ -566,6 +568,7 @@ update msg model loggedIn = "updatesale" (encodeUpdateForm sale formOutput community.symbol) |> LoggedIn.withPrivateKey loggedIn + [ Permission.Sell ] model { successMsg = msg, errorMsg = ClosedAuthModal } @@ -609,6 +612,7 @@ update msg model loggedIn = "deletesale" (encodeDeleteForm sale) |> LoggedIn.withPrivateKey loggedIn + [] model { successMsg = msg, errorMsg = ClosedAuthModal } diff --git a/src/elm/Page/Shop/Viewer.elm b/src/elm/Page/Shop/Viewer.elm index 52e74155a..924862b3d 100755 --- a/src/elm/Page/Shop/Viewer.elm +++ b/src/elm/Page/Shop/Viewer.elm @@ -13,6 +13,7 @@ module Page.Shop.Viewer exposing import Api import Api.Graphql import Avatar +import Cambiatus.Enum.Permission as Permission import Community exposing (Balance) import Eos import Eos.Account as Eos @@ -334,6 +335,7 @@ updateAsLoggedIn msg model loggedIn = ] } |> LoggedIn.withPrivateKey loggedIn + [ Permission.Order ] model { successMsg = msg, errorMsg = ClosedAuthModal } diff --git a/src/elm/Profile.elm b/src/elm/Profile.elm index 33f9d9716..35f8b01f3 100755 --- a/src/elm/Profile.elm +++ b/src/elm/Profile.elm @@ -24,11 +24,13 @@ module Profile exposing import Avatar exposing (Avatar) import Cambiatus.Enum.ContributionStatusType import Cambiatus.Enum.CurrencyType +import Cambiatus.Enum.Permission exposing (Permission) import Cambiatus.Mutation import Cambiatus.Object import Cambiatus.Object.Community as Community import Cambiatus.Object.Contribution import Cambiatus.Object.DeleteStatus +import Cambiatus.Object.Role import Cambiatus.Object.Subdomain as Subdomain import Cambiatus.Object.User as User import Cambiatus.Query @@ -71,6 +73,13 @@ type alias Minimal = } +type alias Role = + { color : Maybe String + , name : String + , permissions : List Permission + } + + type alias Model = { name : Maybe String , account : Eos.Name @@ -81,6 +90,7 @@ type alias Model = , contacts : List Contact.Normalized , interests : List String , communities : List CommunityInfo + , roles : List Role , analysisCount : Int , kyc : Maybe ProfileKyc , address : Maybe Address @@ -104,6 +114,14 @@ userContactSelectionSet = |> SelectionSet.map (List.filterMap identity) +roleSelectionSet : SelectionSet Role Cambiatus.Object.Role +roleSelectionSet = + SelectionSet.succeed Role + |> with Cambiatus.Object.Role.color + |> with Cambiatus.Object.Role.name + |> with Cambiatus.Object.Role.permissions + + selectionSet : SelectionSet Model Cambiatus.Object.User selectionSet = SelectionSet.succeed Model @@ -122,6 +140,7 @@ selectionSet = ) ) |> with (User.communities communityInfoSelectionSet) + |> with (User.roles roleSelectionSet) |> with User.analysisCount |> with (User.kyc Kyc.selectionSet) |> with (User.address Address.selectionSet) diff --git a/src/elm/Session/LoggedIn.elm b/src/elm/Session/LoggedIn.elm index f2d3025de..4d98aca75 100755 --- a/src/elm/Session/LoggedIn.elm +++ b/src/elm/Session/LoggedIn.elm @@ -8,6 +8,7 @@ module Session.LoggedIn exposing , Resource(..) , addFeedback , executeFeedback + , hasPermissions , init , initLogin , isAccount @@ -31,6 +32,7 @@ import Action import Api.Graphql import Auth import Avatar +import Cambiatus.Enum.Permission exposing (Permission) import Cambiatus.Object import Cambiatus.Object.UnreadNotifications import Cambiatus.Subscription as Subscription @@ -185,6 +187,7 @@ type alias Model = , routeHistory : List Route , accountName : Eos.Name , profile : RemoteData (Graphql.Http.Error (Maybe Profile.Model)) Profile.Model + , showInsufficientPermissionsModal : Bool , selectedCommunity : RemoteData (Graphql.Http.Error (Maybe Community.Model)) Community.Model , contributionCount : RemoteData (Graphql.Http.Error (Maybe Int)) Int , showUserNav : Bool @@ -218,6 +221,7 @@ initModel shared maybePrivateKey_ accountName authToken = , routeHistory = [] , accountName = accountName , profile = RemoteData.Loading + , showInsufficientPermissionsModal = False , selectedCommunity = RemoteData.Loading , contributionCount = RemoteData.NotAsked , showUserNav = False @@ -430,6 +434,8 @@ viewHelper pageMsg page profile_ ({ shared } as model) content = , viewAuthModal pageMsg model , communitySelectorModal model |> Html.map pageMsg + , insufficientPermissionsModal model + |> Html.map pageMsg ] ) @@ -830,6 +836,40 @@ communitySelectorModal model = text "" +insufficientPermissionsModal : Model -> Html (Msg externalMsg) +insufficientPermissionsModal model = + let + { t } = + model.shared.translators + in + Modal.initWith + { closeMsg = ClosedInsufficientPermissionsModal + , isVisible = model.showInsufficientPermissionsModal + } + |> Modal.withHeader (t "permissions.insufficient.title") + |> Modal.withBody + [ img + [ src "/images/girl-with-ice-cube.svg" + , alt "" + , class "mx-auto mt-4 mb-6" + ] + [] + , p [ class "md:max-w-md md:mx-auto md:text-center" ] + [ text <| t "permissions.insufficient.explanation" ] + , p [ class "my-4 md:max-w-md md:mx-auto md:text-center" ] + [ text <| t "permissions.insufficient.try_again" ] + ] + |> Modal.withFooter + [ button + [ class "button button-primary w-full mt-2" + , onClick ClosedInsufficientPermissionsModal + ] + [ text <| t "permissions.insufficient.ok" ] + ] + |> Modal.withSize Modal.Large + |> Modal.toHtml + + viewMainMenu : Page -> Model -> Html (Msg externalMsg) viewMainMenu page model = let @@ -940,6 +980,7 @@ viewFooter _ = -} type External msg = UpdatedLoggedIn Model + | ShowInsufficientPermissionsModal | AddedCommunity Profile.CommunityInfo | CreatedCommunity Eos.Symbol String | ExternalBroadcast BroadcastMsg @@ -1180,6 +1221,9 @@ mapMsg mapFn msg = ClosedAuthModal -> ClosedAuthModal + ClosedInsufficientPermissionsModal -> + ClosedInsufficientPermissionsModal + GotAuthMsg subMsg -> GotAuthMsg subMsg @@ -1276,6 +1320,9 @@ mapExternal mapFn msg = UpdatedLoggedIn model -> UpdatedLoggedIn model + ShowInsufficientPermissionsModal -> + ShowInsufficientPermissionsModal + AddedCommunity communityInfo -> AddedCommunity communityInfo @@ -1357,6 +1404,9 @@ updateExternal externalMsg ({ shared } as model) = UpdatedLoggedIn newModel -> { defaultResult | model = newModel } + ShowInsufficientPermissionsModal -> + { defaultResult | model = { model | showInsufficientPermissionsModal = True } } + AddedCommunity communityInfo -> let ( newModel, cmd ) = @@ -1578,6 +1628,7 @@ type Msg externalMsg | ToggleLanguageItems | ClickedLanguage Translation.Language | ClosedAuthModal + | ClosedInsufficientPermissionsModal | GotAuthMsg Auth.Msg | CompletedLoadUnread Value | OpenCommunitySelector @@ -1615,6 +1666,7 @@ update msg model = , showUserNav = False , showMainNav = False , showAuthModal = False + , showInsufficientPermissionsModal = False } in case msg of @@ -1951,6 +2003,10 @@ update msg model = UR.init closeAllModals |> UR.addExt AuthenticationFailed + ClosedInsufficientPermissionsModal -> + { model | showInsufficientPermissionsModal = False } + |> UR.init + GotAuthMsg authMsg -> Auth.update authMsg shared model.auth |> UR.map @@ -2168,7 +2224,7 @@ update msg model = |> UR.addPort (Api.Graphql.signPhrasePort msg privateKey phrase) |> UR.addExt (AddAfterAuthTokenCallbackInternal callback) ) - |> withPrivateKeyInternal msg model + |> withPrivateKeyInternal msg model [] GotAuthTokenPhrase _ (RemoteData.Failure err) -> { model @@ -2200,7 +2256,7 @@ update msg model = |> UR.addPort (Api.Graphql.signPhrasePort msg privateKey phrase) |> UR.addExt (AddAfterAuthTokenCallback callback) ) - |> withPrivateKeyInternal msg model + |> withPrivateKeyInternal msg model [] GotAuthTokenPhraseExternal _ (RemoteData.Failure err) -> { model @@ -2335,6 +2391,10 @@ handleActionMsg ({ shared } as model) actionMsg = ) in Action.update (hasPrivateKey model) + (model.profile + |> RemoteData.map (.roles >> List.concatMap .permissions) + |> RemoteData.withDefault [] + ) shared community.symbol model.accountName @@ -2343,7 +2403,14 @@ handleActionMsg ({ shared } as model) actionMsg = |> UR.map actionModelToLoggedIn GotActionMsg - (\feedback -> UR.mapModel (\prevModel -> { prevModel | feedback = feedback })) + (\ext -> + case ext of + Action.SentFeedback feedback -> + UR.mapModel (\prevModel -> { prevModel | feedback = feedback }) + + Action.ShowInsufficientPermissions -> + UR.mapModel (\prevModel -> { prevModel | showInsufficientPermissionsModal = True }) + ) |> UR.addCmd (case actionMsg of Action.AgreedToClaimWithProof _ -> @@ -2363,29 +2430,77 @@ again. Necessary to perform EOS transactions -} withPrivateKey : Model + -> List Permission -> subModel -> { successMsg : subMsg, errorMsg : subMsg } -> UR.UpdateResult subModel subMsg (External subMsg) -> UR.UpdateResult subModel subMsg (External subMsg) -withPrivateKey loggedIn subModel subMsg successfulUR = - if hasPrivateKey loggedIn then - successfulUR - - else - UR.init subModel - |> UR.addExt (RequiredPrivateKey subMsg) +withPrivateKey loggedIn necessaryPermissions subModel subMsg successfulUR = + case profile loggedIn of + Just validProfile -> + if hasPermissions validProfile necessaryPermissions then + if hasPrivateKey loggedIn then + successfulUR + else + UR.init subModel + |> UR.addExt (RequiredPrivateKey subMsg) -withPrivateKeyInternal : Msg msg -> Model -> (Eos.PrivateKey -> UpdateResult msg) -> UpdateResult msg -withPrivateKeyInternal msg loggedIn successfulUR = - case maybePrivateKey loggedIn of - Just privateKey -> - successfulUR privateKey + else + UR.init subModel + |> UR.addExt ShowInsufficientPermissionsModal Nothing -> - askedAuthentication loggedIn + UR.init subModel + |> UR.logImpossible subMsg.successMsg + "Tried signing eos transaction, but profile wasn't loaded" + (Just loggedIn.accountName) + { moduleName = "Session.LoggedIn" + , function = "withPrivateKey" + } + [] + + +{-| Determines if a profile has a set of permissions +-} +hasPermissions : Profile.Model -> List Permission -> Bool +hasPermissions profile_ permissions = + let + allPermissions = + List.concatMap .permissions profile_.roles + in + List.all (\permission -> List.member permission allPermissions) + permissions + + +withPrivateKeyInternal : Msg msg -> Model -> List Permission -> (Eos.PrivateKey -> UpdateResult msg) -> UpdateResult msg +withPrivateKeyInternal msg loggedIn necessaryPermissions successfulUR = + case profile loggedIn of + Just validProfile -> + if hasPermissions validProfile necessaryPermissions then + case maybePrivateKey loggedIn of + Just privateKey -> + successfulUR privateKey + + Nothing -> + askedAuthentication loggedIn + |> UR.init + |> UR.addExt (AddAfterPrivateKeyCallback msg) + + else + { loggedIn | showInsufficientPermissionsModal = True } + |> UR.init + + _ -> + loggedIn |> UR.init - |> UR.addExt (AddAfterPrivateKeyCallback msg) + |> UR.logImpossible msg + "Tried signing eos transaction internally, but profile wasn't loaded" + (Just loggedIn.accountName) + { moduleName = "Session.LoggedIn" + , function = "withPrivateKeyInternal" + } + [] isCommunityMember : Model -> Bool @@ -2531,6 +2646,7 @@ closeModal ({ model } as uResult) = , showUserNav = False , showMainNav = False , showAuthModal = False + , showInsufficientPermissionsModal = False } } @@ -2542,6 +2658,7 @@ askedAuthentication model = , showUserNav = False , showMainNav = False , showAuthModal = True + , showInsufficientPermissionsModal = False } @@ -2694,6 +2811,9 @@ msgToString msg = ClosedAuthModal -> [ "ClosedAuthModal" ] + ClosedInsufficientPermissionsModal -> + [ "ClosedInsufficientPermissionsModal" ] + GotAuthMsg subMsg -> "GotAuthMsg" :: Auth.msgToString subMsg @@ -2755,6 +2875,9 @@ externalMsgToString externalMsg = UpdatedLoggedIn _ -> [ "UpdatedLoggedIn" ] + ShowInsufficientPermissionsModal -> + [ "ShowInsufficientPermissionsModal" ] + AddedCommunity _ -> [ "AddedCommunity" ]