{- Copyright 2020 Docspell Contributors SPDX-License-Identifier: GPL-3.0-or-later -} module Comp.FolderDetail exposing ( Model , Msg , init , initEmpty , update , view2 ) import Api import Api.Model.BasicResult exposing (BasicResult) import Api.Model.FolderDetail exposing (FolderDetail) import Api.Model.IdName exposing (IdName) import Api.Model.IdResult exposing (IdResult) import Api.Model.NewFolder exposing (NewFolder) import Api.Model.User exposing (User) import Comp.Basic as B import Comp.FixedDropdown import Comp.MenuBar as MB import Comp.YesNoDimmer import Data.DropdownStyle as DS import Data.Flags exposing (Flags) import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick, onInput) import Http import Messages.Comp.FolderDetail exposing (Texts) import Styles as S import Util.Maybe type alias Model = { formState : FormState , folder : FolderDetail , name : Maybe String , members : List IdName , users : List User , memberDropdown : Comp.FixedDropdown.Model IdName , selectedMember : Maybe IdName , loading : Bool , deleteDimmer : Comp.YesNoDimmer.Model } type FormState = FormStateInitial | FormStateHttpError Http.Error | FormStateFolderCreated | FormStateGenericError String | FormStateNameChangeSuccessful | FormStateDeleteSuccessful isError : FormState -> Bool isError state = case state of FormStateInitial -> False FormStateHttpError _ -> True FormStateGenericError _ -> True FormStateFolderCreated -> False FormStateNameChangeSuccessful -> False FormStateDeleteSuccessful -> False isSuccess : FormState -> Bool isSuccess state = case state of FormStateInitial -> False FormStateHttpError _ -> False FormStateGenericError _ -> False FormStateFolderCreated -> True FormStateNameChangeSuccessful -> True FormStateDeleteSuccessful -> True type Msg = SetName String | MemberDropdownMsg (Comp.FixedDropdown.Msg IdName) | SaveName | NewFolderResp (Result Http.Error IdResult) | ChangeFolderResp (Result Http.Error BasicResult) | ChangeNameResp (Result Http.Error BasicResult) | FolderDetailResp (Result Http.Error FolderDetail) | AddMember | RemoveMember IdName | RequestDelete | DeleteMsg Comp.YesNoDimmer.Msg | DeleteResp (Result Http.Error BasicResult) | GoBack init : List User -> FolderDetail -> Model init users folder = { formState = FormStateInitial , folder = folder , name = Util.Maybe.fromString folder.name , members = folder.members , users = users , memberDropdown = Comp.FixedDropdown.init (makeOptions users folder) , selectedMember = Nothing , loading = False , deleteDimmer = Comp.YesNoDimmer.emptyModel } initEmpty : List User -> Model initEmpty users = init users Api.Model.FolderDetail.empty makeOptions : List User -> FolderDetail -> List IdName makeOptions users folder = let toIdName u = IdName u.id u.login notMember idn = List.member idn (folder.owner :: folder.members) |> not in List.map toIdName users |> List.filter notMember --- Update update : Flags -> Msg -> Model -> ( Model, Cmd Msg, Bool ) update flags msg model = case msg of GoBack -> ( model, Cmd.none, True ) SetName str -> ( { model | name = Util.Maybe.fromString str } , Cmd.none , False ) MemberDropdownMsg lmsg -> let ( mm, sel ) = Comp.FixedDropdown.update lmsg model.memberDropdown in ( { model | memberDropdown = mm , selectedMember = case sel of Just _ -> sel Nothing -> model.selectedMember } , Cmd.none , False ) SaveName -> case model.name of Just name -> let cmd = if model.folder.id == "" then Api.createNewFolder flags (NewFolder name) NewFolderResp else Api.changeFolderName flags model.folder.id (NewFolder name) ChangeNameResp in ( { model | loading = True , formState = FormStateInitial } , cmd , False ) Nothing -> ( model, Cmd.none, False ) NewFolderResp (Ok ir) -> if ir.success then ( model, Api.getFolderDetail flags ir.id FolderDetailResp, False ) else ( { model | loading = False , formState = if ir.success then FormStateFolderCreated else FormStateGenericError ir.message } , Cmd.none , False ) NewFolderResp (Err err) -> ( { model | loading = False , formState = FormStateHttpError err } , Cmd.none , False ) ChangeFolderResp (Ok r) -> if r.success then ( model , Api.getFolderDetail flags model.folder.id FolderDetailResp , False ) else ( { model | loading = False, formState = FormStateGenericError r.message } , Cmd.none , False ) ChangeFolderResp (Err err) -> ( { model | loading = False , formState = FormStateHttpError err } , Cmd.none , False ) ChangeNameResp (Ok r) -> let model_ = { model | formState = if r.success then FormStateNameChangeSuccessful else FormStateGenericError r.message , loading = False } in ( model_, Cmd.none, False ) ChangeNameResp (Err err) -> ( { model | formState = FormStateHttpError err , loading = False } , Cmd.none , False ) FolderDetailResp (Ok sd) -> ( init model.users sd, Cmd.none, False ) FolderDetailResp (Err err) -> ( { model | loading = False , formState = FormStateHttpError err } , Cmd.none , False ) AddMember -> case model.selectedMember of Just mem -> ( { model | loading = True } , Api.addMember flags model.folder.id mem.id ChangeFolderResp , False ) Nothing -> ( model, Cmd.none, False ) RemoveMember idname -> ( { model | loading = True } , Api.removeMember flags model.folder.id idname.id ChangeFolderResp , False ) RequestDelete -> let ( dm, _ ) = Comp.YesNoDimmer.update Comp.YesNoDimmer.activate model.deleteDimmer in ( { model | deleteDimmer = dm }, Cmd.none, False ) DeleteMsg lm -> let ( dm, flag ) = Comp.YesNoDimmer.update lm model.deleteDimmer cmd = if flag then Api.deleteFolder flags model.folder.id DeleteResp else Cmd.none in ( { model | deleteDimmer = dm }, cmd, False ) DeleteResp (Ok r) -> ( { model | formState = if r.success then FormStateDeleteSuccessful else FormStateGenericError r.message } , Cmd.none , r.success ) DeleteResp (Err err) -> ( { model | formState = FormStateHttpError err } , Cmd.none , False ) --- View2 view2 : Texts -> Flags -> Model -> Html Msg view2 texts flags model = let isOwner = Maybe.map .user flags.account |> Maybe.map ((==) model.folder.owner.name) |> Maybe.withDefault False dimmerSettings : Comp.YesNoDimmer.Settings dimmerSettings = Comp.YesNoDimmer.defaultSettings texts.reallyDeleteThisFolder texts.basics.yes texts.basics.no in div [ class "flex flex-col md:relative" ] (viewButtons2 texts model :: [ Html.map DeleteMsg (Comp.YesNoDimmer.viewN True dimmerSettings model.deleteDimmer ) , div [ class "py-2 text-lg opacity-75" , classList [ ( "hidden", model.folder.id /= "" ) ] ] [ text texts.autoOwnerInfo ] , div [ class "py-2 text-lg opacity-75" , classList [ ( "hidden", model.folder.id == "" ) ] ] [ text texts.modifyInfo ] , div [ class S.message , classList [ ( "hidden", model.folder.id == "" || isOwner ) ] ] [ text texts.notOwnerInfo ] , div [ class "mb-4 flex flex-col" ] [ label [ class S.inputLabel , for "folder-name" ] [ text texts.basics.name , B.inputRequired ] , div [ class "flex flex-row space-x-2" ] [ input [ type_ "text" , onInput SetName , Maybe.withDefault "" model.name |> value , classList [ ( S.inputErrorBorder, model.name == Nothing ) ] , class S.textInput , id "folder-name" ] [] , a [ class S.primaryButton , class "rounded-r -ml-1" , onClick SaveName , href "#" ] [ i [ class "fa fa-save" ] [] , span [ class "ml-2 hidden sm:inline" ] [ text texts.basics.submit ] ] ] ] , div [ classList [ ( "hidden", model.formState == FormStateInitial ) , ( S.errorMessage, isError model.formState ) , ( S.successMessage, isSuccess model.formState ) ] , class "my-4" ] [ case model.formState of FormStateInitial -> text "" FormStateHttpError err -> text (texts.httpError err) FormStateGenericError m -> text m FormStateFolderCreated -> text texts.folderCreated FormStateNameChangeSuccessful -> text texts.nameChangeSuccessful FormStateDeleteSuccessful -> text texts.deleteSuccessful ] ] ++ viewMembers2 texts model ) viewMembers2 : Texts -> Model -> List (Html Msg) viewMembers2 texts model = let folderCfg = { display = .name , icon = \_ -> Nothing , style = DS.mainStyle , selectPlaceholder = texts.basics.selectPlaceholder } in if model.folder.id == "" then [] else [ div [ class S.header3 , class "mt-4" ] [ text texts.members ] , div [ class "flex flex-col space-y-2" ] [ div [ class "flex flex-row space-x-2" ] [ div [ class "flex-grow" ] [ Html.map MemberDropdownMsg (Comp.FixedDropdown.viewStyled2 folderCfg False model.selectedMember model.memberDropdown ) ] , a [ title texts.addMember , onClick AddMember , class S.primaryButton , href "#" , class "flex-none" ] [ i [ class "fa fa-plus" ] [] , span [ class "ml-2 hidden sm:inline" ] [ text texts.add ] ] ] ] , div [ class "flex flex-col space-y-4 md:space-y-2 mt-2" , class "px-2 border-0 border-l dark:border-bluegray-600" ] (List.map (viewMember2 texts) model.members) ] viewMember2 : Texts -> IdName -> Html Msg viewMember2 texts member = div [ class "flex flex-row space-x-2 items-center" ] [ a [ class S.deleteLabel , href "#" , title texts.removeMember , onClick (RemoveMember member) ] [ i [ class "fa fa-trash " ] [] ] , span [ class "ml-2" ] [ text member.name ] ] viewButtons2 : Texts -> Model -> Html Msg viewButtons2 texts model = MB.view { start = [ MB.SecondaryButton { tagger = GoBack , label = texts.basics.cancel , icon = Just "fa fa-arrow-left" , title = texts.basics.backToList } ] , end = [ MB.CustomButton { tagger = RequestDelete , label = texts.basics.delete , icon = Just "fa fa-trash" , title = texts.deleteThisFolder , inputClass = [ ( S.deleteButton, True ) , ( "hidden", model.folder.id == "" ) ] } ] , rootClasses = "mb-4" }