diff --git a/modules/webapp/src/main/elm/Api.elm b/modules/webapp/src/main/elm/Api.elm index bb6b94e7..42b16e20 100644 --- a/modules/webapp/src/main/elm/Api.elm +++ b/modules/webapp/src/main/elm/Api.elm @@ -83,6 +83,7 @@ module Api exposing , initOtp , itemBasePreviewURL , itemDetail + , itemDetailShare , itemIndexSearch , itemSearch , itemSearchStats @@ -2302,6 +2303,15 @@ searchShareStats flags token search receive = } +itemDetailShare : Flags -> String -> String -> (Result Http.Error ItemDetail -> msg) -> Cmd msg +itemDetailShare flags token itemId receive = + Http2.shareGet + { url = flags.config.baseUrl ++ "/api/v1/share/item/" ++ itemId + , token = token + , expect = Http.expectJson receive Api.Model.ItemDetail.decoder + } + + shareAttachmentPreviewURL : String -> String shareAttachmentPreviewURL id = "/api/v1/share/attachment/" ++ id ++ "/preview?withFallback=true" diff --git a/modules/webapp/src/main/elm/App/Data.elm b/modules/webapp/src/main/elm/App/Data.elm index 36713a54..c64c192a 100644 --- a/modules/webapp/src/main/elm/App/Data.elm +++ b/modules/webapp/src/main/elm/App/Data.elm @@ -33,6 +33,7 @@ import Page.NewInvite.Data import Page.Queue.Data import Page.Register.Data import Page.Share.Data +import Page.ShareDetail.Data import Page.Upload.Data import Page.UserSettings.Data import Url exposing (Url) @@ -54,6 +55,7 @@ type alias Model = , newInviteModel : Page.NewInvite.Data.Model , itemDetailModel : Page.ItemDetail.Data.Model , shareModel : Page.Share.Data.Model + , shareDetailModel : Page.ShareDetail.Data.Model , navMenuOpen : Bool , userMenuOpen : Bool , subs : Sub Msg @@ -88,7 +90,10 @@ init key url flags_ settings = Page.Login.Data.init flags (Page.loginPageReferrer page) ( shm, shc ) = - Page.Share.Data.init (Page.shareId page) flags + Page.Share.Data.init (Page.pageShareId page) flags + + ( sdm, sdc ) = + Page.ShareDetail.Data.init (Page.pageShareDetail page) flags homeViewMode = if settings.searchMenuVisible then @@ -112,6 +117,7 @@ init key url flags_ settings = , newInviteModel = Page.NewInvite.Data.emptyModel , itemDetailModel = Page.ItemDetail.Data.emptyModel , shareModel = shm + , shareDetailModel = sdm , navMenuOpen = False , userMenuOpen = False , subs = Sub.none @@ -127,6 +133,7 @@ init key url flags_ settings = , Cmd.map CollSettingsMsg csc , Cmd.map LoginMsg loginc , Cmd.map ShareMsg shc + , Cmd.map ShareDetailMsg sdc ] ) @@ -170,6 +177,7 @@ type Msg | NewInviteMsg Page.NewInvite.Data.Msg | ItemDetailMsg Page.ItemDetail.Data.Msg | ShareMsg Page.Share.Data.Msg + | ShareDetailMsg Page.ShareDetail.Data.Msg | Logout | LogoutResp (Result Http.Error ()) | SessionCheckResp (Result Http.Error AuthResult) diff --git a/modules/webapp/src/main/elm/App/Update.elm b/modules/webapp/src/main/elm/App/Update.elm index 2b051d84..19a689cb 100644 --- a/modules/webapp/src/main/elm/App/Update.elm +++ b/modules/webapp/src/main/elm/App/Update.elm @@ -36,6 +36,8 @@ import Page.Register.Data import Page.Register.Update import Page.Share.Data import Page.Share.Update +import Page.ShareDetail.Data +import Page.ShareDetail.Update import Page.Upload.Data import Page.Upload.Update import Page.UserSettings.Data @@ -119,6 +121,9 @@ updateWithSub msg model = ShareMsg lm -> updateShare lm model + ShareDetailMsg lm -> + updateShareDetail lm model + LoginMsg lm -> updateLogin lm model @@ -318,9 +323,26 @@ applyClientSettings model settings = { model | uiSettings = settings } +updateShareDetail : Page.ShareDetail.Data.Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) +updateShareDetail lmsg model = + case Page.pageShareDetail model.page of + Just ( shareId, itemId ) -> + let + ( m, c ) = + Page.ShareDetail.Update.update shareId itemId model.flags lmsg model.shareDetailModel + in + ( { model | shareDetailModel = m } + , Cmd.map ShareDetailMsg c + , Sub.none + ) + + Nothing -> + ( model, Cmd.none, Sub.none ) + + updateShare : Page.Share.Data.Msg -> Model -> ( Model, Cmd Msg, Sub Msg ) updateShare lmsg model = - case Page.shareId model.page of + case Page.pageShareId model.page of Just id -> let result = @@ -593,3 +615,15 @@ initPage model_ page = SharePage _ -> ( model, Cmd.none, Sub.none ) + + ShareDetailPage _ _ -> + case model_.page of + SharePage _ -> + let + verifyResult = + model.shareModel.verifyResult + in + updateShareDetail (Page.ShareDetail.Data.VerifyResp (Ok verifyResult)) model + + _ -> + ( model, Cmd.none, Sub.none ) diff --git a/modules/webapp/src/main/elm/App/View2.elm b/modules/webapp/src/main/elm/App/View2.elm index 06b99462..3dcaba5a 100644 --- a/modules/webapp/src/main/elm/App/View2.elm +++ b/modules/webapp/src/main/elm/App/View2.elm @@ -28,6 +28,7 @@ import Page.NewInvite.View2 as NewInvite import Page.Queue.View2 as Queue import Page.Register.View2 as Register import Page.Share.View as Share +import Page.ShareDetail.View as ShareDetail import Page.Upload.View2 as Upload import Page.UserSettings.View2 as UserSettings import Styles as S @@ -166,6 +167,9 @@ mainContent model = SharePage id -> viewShare texts id model + + ShareDetailPage shareId itemId -> + viewShareDetail texts shareId itemId model ) @@ -434,11 +438,33 @@ viewShare texts shareId model = model.flags model.version model.uiSettings + shareId model.shareModel ) ] +viewShareDetail : Messages -> String -> String -> Model -> List (Html Msg) +viewShareDetail texts shareId itemId model = + [ Html.map ShareDetailMsg + (ShareDetail.viewSidebar texts.shareDetail + model.sidebarVisible + model.flags + model.uiSettings + model.shareDetailModel + ) + , Html.map ShareDetailMsg + (ShareDetail.viewContent texts.shareDetail + model.flags + model.uiSettings + model.version + shareId + itemId + model.shareDetailModel + ) + ] + + viewHome : Messages -> Model -> List (Html Msg) viewHome texts model = [ Html.map HomeMsg diff --git a/modules/webapp/src/main/elm/Comp/ItemCard.elm b/modules/webapp/src/main/elm/Comp/ItemCard.elm index 15d66ce5..554cb1c5 100644 --- a/modules/webapp/src/main/elm/Comp/ItemCard.elm +++ b/modules/webapp/src/main/elm/Comp/ItemCard.elm @@ -59,6 +59,7 @@ type alias ViewConfig = , previewUrl : AttachmentLight -> String , previewUrlFallback : ItemLight -> String , attachUrl : AttachmentLight -> String + , detailPage : ItemLight -> Page } @@ -174,7 +175,7 @@ view2 texts cfg settings model item = cardAction = case cfg.selection of Data.ItemSelection.Inactive -> - [ Page.href (ItemDetailPage item.id) + [ Page.href (cfg.detailPage item) ] Data.ItemSelection.Active ids -> @@ -530,7 +531,7 @@ previewMenu2 texts settings cfg model item mainAttach = , a [ class S.secondaryBasicButtonPlain , class "px-2 py-1 border rounded ml-2" - , Page.href (ItemDetailPage item.id) + , Page.href (cfg.detailPage item) , title texts.gotoDetail ] [ i [ class "fa fa-edit" ] [] diff --git a/modules/webapp/src/main/elm/Comp/ItemCardList.elm b/modules/webapp/src/main/elm/Comp/ItemCardList.elm index a23894e2..1986411d 100644 --- a/modules/webapp/src/main/elm/Comp/ItemCardList.elm +++ b/modules/webapp/src/main/elm/Comp/ItemCardList.elm @@ -165,6 +165,7 @@ type alias ViewConfig = , previewUrl : AttachmentLight -> String , previewUrlFallback : ItemLight -> String , attachUrl : AttachmentLight -> String + , detailPage : ItemLight -> Page } @@ -220,7 +221,7 @@ viewItem2 texts model cfg settings item = "" vvcfg = - Comp.ItemCard.ViewConfig cfg.selection currentClass cfg.previewUrl cfg.previewUrlFallback cfg.attachUrl + Comp.ItemCard.ViewConfig cfg.selection currentClass cfg.previewUrl cfg.previewUrlFallback cfg.attachUrl cfg.detailPage cardModel = Dict.get item.id model.itemCards diff --git a/modules/webapp/src/main/elm/Comp/SharePasswordForm.elm b/modules/webapp/src/main/elm/Comp/SharePasswordForm.elm new file mode 100644 index 00000000..2c17a16a --- /dev/null +++ b/modules/webapp/src/main/elm/Comp/SharePasswordForm.elm @@ -0,0 +1,156 @@ +module Comp.SharePasswordForm exposing (Model, Msg, init, update, view) + +import Api +import Api.Model.ShareVerifyResult exposing (ShareVerifyResult) +import Api.Model.VersionInfo exposing (VersionInfo) +import Data.Flags exposing (Flags) +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (onInput, onSubmit) +import Http +import Messages.Comp.SharePasswordForm exposing (Texts) +import Styles as S + + +type CompError + = CompErrorNone + | CompErrorPasswordFailed + | CompErrorHttp Http.Error + + +type alias Model = + { password : String + , compError : CompError + } + + +init : Model +init = + { password = "" + , compError = CompErrorNone + } + + +type Msg + = SetPassword String + | SubmitPassword + | VerifyResp (Result Http.Error ShareVerifyResult) + + + +--- update + + +update : String -> Flags -> Msg -> Model -> ( Model, Cmd Msg, Maybe ShareVerifyResult ) +update shareId flags msg model = + case msg of + SetPassword pw -> + ( { model | password = pw }, Cmd.none, Nothing ) + + SubmitPassword -> + let + secret = + { shareId = shareId + , password = Just model.password + } + in + ( model, Api.verifyShare flags secret VerifyResp, Nothing ) + + VerifyResp (Ok res) -> + if res.success then + ( { model | password = "", compError = CompErrorNone }, Cmd.none, Just res ) + + else + ( { model | password = "", compError = CompErrorPasswordFailed }, Cmd.none, Nothing ) + + VerifyResp (Err err) -> + ( { model | password = "", compError = CompErrorHttp err }, Cmd.none, Nothing ) + + + +--- view + + +view : Texts -> Flags -> VersionInfo -> Model -> Html Msg +view texts flags versionInfo model = + div [ class "flex flex-col items-center" ] + [ div [ class ("flex flex-col px-4 sm:px-6 md:px-8 lg:px-10 py-8 rounded-md " ++ S.box) ] + [ div [ class "self-center" ] + [ img + [ class "w-16 py-2" + , src (flags.config.docspellAssetPath ++ "/img/logo-96.png") + ] + [] + ] + , div [ class "font-medium self-center text-xl sm:text-2xl" ] + [ text texts.passwordRequired + ] + , Html.form + [ action "#" + , onSubmit SubmitPassword + , autocomplete False + ] + [ div [ class "flex flex-col my-3" ] + [ label + [ for "password" + , class S.inputLabel + ] + [ text texts.password + ] + , div [ class "relative" ] + [ div [ class S.inputIcon ] + [ i [ class "fa fa-lock" ] [] + ] + , input + [ type_ "password" + , name "password" + , autocomplete False + , autofocus True + , tabindex 1 + , onInput SetPassword + , value model.password + , class ("pl-10 pr-4 py-2 rounded-lg" ++ S.textInput) + , placeholder texts.password + ] + [] + ] + ] + , div [ class "flex flex-col my-3" ] + [ button + [ type_ "submit" + , class S.primaryButton + ] + [ text texts.passwordSubmitButton + ] + ] + , case model.compError of + CompErrorNone -> + span [ class "hidden" ] [] + + CompErrorHttp err -> + div [ class S.errorMessage ] + [ text (texts.httpError err) + ] + + CompErrorPasswordFailed -> + div [ class S.errorMessage ] + [ text texts.passwordFailed + ] + ] + ] + , a + [ class "inline-flex items-center mt-4 text-xs opacity-50 hover:opacity-90" + , href "https://docspell.org" + , target "_new" + ] + [ img + [ src (flags.config.docspellAssetPath ++ "/img/logo-mc-96.png") + , class "w-3 h-3 mr-1" + ] + [] + , span [] + [ text "Docspell " + , text versionInfo.version + ] + ] + ] diff --git a/modules/webapp/src/main/elm/Messages.elm b/modules/webapp/src/main/elm/Messages.elm index 9809f1b4..24399b15 100644 --- a/modules/webapp/src/main/elm/Messages.elm +++ b/modules/webapp/src/main/elm/Messages.elm @@ -22,6 +22,7 @@ import Messages.Page.NewInvite import Messages.Page.Queue import Messages.Page.Register import Messages.Page.Share +import Messages.Page.ShareDetail import Messages.Page.Upload import Messages.Page.UserSettings import Messages.UiLanguage exposing (UiLanguage(..)) @@ -46,6 +47,7 @@ type alias Messages = , manageData : Messages.Page.ManageData.Texts , home : Messages.Page.Home.Texts , share : Messages.Page.Share.Texts + , shareDetail : Messages.Page.ShareDetail.Texts } @@ -112,6 +114,7 @@ gb = , manageData = Messages.Page.ManageData.gb , home = Messages.Page.Home.gb , share = Messages.Page.Share.gb + , shareDetail = Messages.Page.ShareDetail.gb } @@ -133,4 +136,5 @@ de = , manageData = Messages.Page.ManageData.de , home = Messages.Page.Home.de , share = Messages.Page.Share.de + , shareDetail = Messages.Page.ShareDetail.de } diff --git a/modules/webapp/src/main/elm/Messages/Comp/SharePasswordForm.elm b/modules/webapp/src/main/elm/Messages/Comp/SharePasswordForm.elm new file mode 100644 index 00000000..8688a4e9 --- /dev/null +++ b/modules/webapp/src/main/elm/Messages/Comp/SharePasswordForm.elm @@ -0,0 +1,33 @@ +module Messages.Comp.SharePasswordForm exposing (Texts, de, gb) + +import Http +import Messages.Comp.HttpError + + +type alias Texts = + { httpError : Http.Error -> String + , passwordRequired : String + , password : String + , passwordSubmitButton : String + , passwordFailed : String + } + + +gb : Texts +gb = + { httpError = Messages.Comp.HttpError.gb + , passwordRequired = "Password required" + , password = "Password" + , passwordSubmitButton = "Submit" + , passwordFailed = "Das Passwort ist falsch" + } + + +de : Texts +de = + { httpError = Messages.Comp.HttpError.de + , passwordRequired = "Passwort benötigt" + , password = "Passwort" + , passwordSubmitButton = "Submit" + , passwordFailed = "Password is wrong" + } diff --git a/modules/webapp/src/main/elm/Messages/Page/Share.elm b/modules/webapp/src/main/elm/Messages/Page/Share.elm index cee461cd..f6b73d31 100644 --- a/modules/webapp/src/main/elm/Messages/Page/Share.elm +++ b/modules/webapp/src/main/elm/Messages/Page/Share.elm @@ -10,16 +10,14 @@ module Messages.Page.Share exposing (..) import Messages.Basics import Messages.Comp.ItemCardList import Messages.Comp.SearchMenu +import Messages.Comp.SharePasswordForm type alias Texts = { searchMenu : Messages.Comp.SearchMenu.Texts , basics : Messages.Basics.Texts , itemCardList : Messages.Comp.ItemCardList.Texts - , passwordRequired : String - , password : String - , passwordSubmitButton : String - , passwordFailed : String + , passwordForm : Messages.Comp.SharePasswordForm.Texts } @@ -28,10 +26,7 @@ gb = { searchMenu = Messages.Comp.SearchMenu.gb , basics = Messages.Basics.gb , itemCardList = Messages.Comp.ItemCardList.gb - , passwordRequired = "Password required" - , password = "Password" - , passwordSubmitButton = "Submit" - , passwordFailed = "Das Passwort ist falsch" + , passwordForm = Messages.Comp.SharePasswordForm.gb } @@ -40,8 +35,5 @@ de = { searchMenu = Messages.Comp.SearchMenu.de , basics = Messages.Basics.de , itemCardList = Messages.Comp.ItemCardList.de - , passwordRequired = "Passwort benötigt" - , password = "Passwort" - , passwordSubmitButton = "Submit" - , passwordFailed = "Password is wrong" + , passwordForm = Messages.Comp.SharePasswordForm.de } diff --git a/modules/webapp/src/main/elm/Messages/Page/ShareDetail.elm b/modules/webapp/src/main/elm/Messages/Page/ShareDetail.elm new file mode 100644 index 00000000..71ab9536 --- /dev/null +++ b/modules/webapp/src/main/elm/Messages/Page/ShareDetail.elm @@ -0,0 +1,20 @@ +module Messages.Page.ShareDetail exposing (..) + +import Messages.Comp.SharePasswordForm + + +type alias Texts = + { passwordForm : Messages.Comp.SharePasswordForm.Texts + } + + +gb : Texts +gb = + { passwordForm = Messages.Comp.SharePasswordForm.gb + } + + +de : Texts +de = + { passwordForm = Messages.Comp.SharePasswordForm.de + } diff --git a/modules/webapp/src/main/elm/Page.elm b/modules/webapp/src/main/elm/Page.elm index 667fe7aa..2f42ee2e 100644 --- a/modules/webapp/src/main/elm/Page.elm +++ b/modules/webapp/src/main/elm/Page.elm @@ -19,9 +19,10 @@ module Page exposing , loginPageReferrer , pageFromString , pageName + , pageShareDetail + , pageShareId , pageToString , set - , shareId , uploadId ) @@ -61,6 +62,7 @@ type Page | NewInvitePage | ItemDetailPage String | SharePage String + | ShareDetailPage String String isSecured : Page -> Bool @@ -99,6 +101,9 @@ isSecured page = SharePage _ -> False + ShareDetailPage _ _ -> + False + {-| Currently, all secured pages have a sidebar, except UploadPage. -} @@ -171,6 +176,9 @@ pageName page = SharePage _ -> "Share" + ShareDetailPage _ _ -> + "Share Detail" + loginPageReferrer : Page -> LoginData loginPageReferrer page = @@ -182,8 +190,8 @@ loginPageReferrer page = emptyLoginData -shareId : Page -> Maybe String -shareId page = +pageShareId : Page -> Maybe String +pageShareId page = case page of SharePage id -> Just id @@ -192,6 +200,16 @@ shareId page = Nothing +pageShareDetail : Page -> Maybe ( String, String ) +pageShareDetail page = + case page of + ShareDetailPage shareId itemId -> + Just ( shareId, itemId ) + + _ -> + Nothing + + uploadId : Page -> Maybe String uploadId page = case page of @@ -248,6 +266,9 @@ pageToString page = SharePage id -> "/app/share/" ++ id + ShareDetailPage shareId itemId -> + "/app/share/" ++ shareId ++ "/" ++ itemId + pageFromString : String -> Maybe Page pageFromString str = @@ -304,6 +325,7 @@ parser = , Parser.map (UploadPage Nothing) (s pathPrefix s "upload") , Parser.map NewInvitePage (s pathPrefix s "newinvite") , Parser.map ItemDetailPage (s pathPrefix s "item" string) + , Parser.map ShareDetailPage (s pathPrefix s "share" string string) , Parser.map SharePage (s pathPrefix s "share" string) ] diff --git a/modules/webapp/src/main/elm/Page/Home/View2.elm b/modules/webapp/src/main/elm/Page/Home/View2.elm index 506ae853..da26662d 100644 --- a/modules/webapp/src/main/elm/Page/Home/View2.elm +++ b/modules/webapp/src/main/elm/Page/Home/View2.elm @@ -468,23 +468,22 @@ itemCardList texts _ settings model = previewUrlFallback item = Api.itemBasePreviewURL item.id + viewCfg sel = + Comp.ItemCardList.ViewConfig + model.scrollToCard + sel + previewUrl + previewUrlFallback + (.id >> Api.fileURL) + (.id >> ItemDetailPage) + itemViewCfg = case model.viewMode of SelectView svm -> - Comp.ItemCardList.ViewConfig - model.scrollToCard - (Data.ItemSelection.Active svm.ids) - previewUrl - previewUrlFallback - (.id >> Api.fileURL) + viewCfg (Data.ItemSelection.Active svm.ids) _ -> - Comp.ItemCardList.ViewConfig - model.scrollToCard - Data.ItemSelection.Inactive - previewUrl - previewUrlFallback - (.id >> Api.fileURL) + viewCfg Data.ItemSelection.Inactive in [ Html.map ItemCardListMsg (Comp.ItemCardList.view2 texts.itemCardList diff --git a/modules/webapp/src/main/elm/Page/Share/Data.elm b/modules/webapp/src/main/elm/Page/Share/Data.elm index eb47a41c..4be43360 100644 --- a/modules/webapp/src/main/elm/Page/Share/Data.elm +++ b/modules/webapp/src/main/elm/Page/Share/Data.elm @@ -15,6 +15,7 @@ import Api.Model.ShareVerifyResult exposing (ShareVerifyResult) import Comp.ItemCardList import Comp.PowerSearchInput import Comp.SearchMenu +import Comp.SharePasswordForm import Data.Flags exposing (Flags) import Http @@ -31,16 +32,10 @@ type PageError | PageErrorAuthFail -type alias PasswordModel = - { password : String - , passwordFailed : Bool - } - - type alias Model = { mode : Mode , verifyResult : ShareVerifyResult - , passwordModel : PasswordModel + , passwordModel : Comp.SharePasswordForm.Model , pageError : PageError , searchMenuModel : Comp.SearchMenu.Model , powerSearchInput : Comp.PowerSearchInput.Model @@ -53,10 +48,7 @@ emptyModel : Flags -> Model emptyModel flags = { mode = ModeInitial , verifyResult = Api.Model.ShareVerifyResult.empty - , passwordModel = - { password = "" - , passwordFailed = False - } + , passwordModel = Comp.SharePasswordForm.init , pageError = PageErrorNone , searchMenuModel = Comp.SearchMenu.init flags , powerSearchInput = Comp.PowerSearchInput.init @@ -79,8 +71,7 @@ type Msg = VerifyResp (Result Http.Error ShareVerifyResult) | SearchResp (Result Http.Error ItemLightList) | StatsResp (Result Http.Error SearchStats) - | SetPassword String - | SubmitPassword + | PasswordMsg Comp.SharePasswordForm.Msg | SearchMenuMsg Comp.SearchMenu.Msg | PowerSearchMsg Comp.PowerSearchInput.Msg | ResetSearch diff --git a/modules/webapp/src/main/elm/Page/Share/Results.elm b/modules/webapp/src/main/elm/Page/Share/Results.elm index 5bd69400..e47d8583 100644 --- a/modules/webapp/src/main/elm/Page/Share/Results.elm +++ b/modules/webapp/src/main/elm/Page/Share/Results.elm @@ -14,11 +14,12 @@ import Data.UiSettings exposing (UiSettings) import Html exposing (..) import Html.Attributes exposing (..) import Messages.Page.Share exposing (Texts) +import Page exposing (Page(..)) import Page.Share.Data exposing (Model, Msg(..)) -view : Texts -> UiSettings -> Model -> Html Msg -view texts settings model = +view : Texts -> UiSettings -> String -> Model -> Html Msg +view texts settings shareId model = let viewCfg = { current = Nothing @@ -26,6 +27,7 @@ view texts settings model = , previewUrl = \attach -> Api.shareAttachmentPreviewURL attach.id , previewUrlFallback = \item -> Api.shareItemBasePreviewURL item.id , attachUrl = .id >> Api.shareFileURL + , detailPage = \item -> ShareDetailPage shareId item.id } in div [] diff --git a/modules/webapp/src/main/elm/Page/Share/Update.elm b/modules/webapp/src/main/elm/Page/Share/Update.elm index 01b7ec73..c37a9776 100644 --- a/modules/webapp/src/main/elm/Page/Share/Update.elm +++ b/modules/webapp/src/main/elm/Page/Share/Update.elm @@ -13,6 +13,7 @@ import Comp.ItemCardList import Comp.LinkTarget exposing (LinkTarget) import Comp.PowerSearchInput import Comp.SearchMenu +import Comp.SharePasswordForm import Data.Flags exposing (Flags) import Data.ItemQuery as Q import Data.SearchMode @@ -51,26 +52,13 @@ update flags settings shareId msg model = ) else if res.passwordRequired then - if model.mode == ModePassword then - noSub - ( { model - | pageError = PageErrorNone - , passwordModel = - { password = "" - , passwordFailed = True - } - } - , Cmd.none - ) - - else - noSub - ( { model - | pageError = PageErrorNone - , mode = ModePassword - } - , Cmd.none - ) + noSub + ( { model + | pageError = PageErrorNone + , mode = ModePassword + } + , Cmd.none + ) else noSub @@ -101,21 +89,21 @@ update flags settings shareId msg model = StatsResp (Err err) -> noSub ( { model | pageError = PageErrorHttp err }, Cmd.none ) - SetPassword pw -> + PasswordMsg lmsg -> let - pm = - model.passwordModel + ( m, c, res ) = + Comp.SharePasswordForm.update shareId flags lmsg model.passwordModel in - noSub ( { model | passwordModel = { pm | password = pw } }, Cmd.none ) + case res of + Just verifyResult -> + update flags + settings + shareId + (VerifyResp (Ok verifyResult)) + model - SubmitPassword -> - let - secret = - { shareId = shareId - , password = Just model.passwordModel.password - } - in - noSub ( model, Api.verifyShare flags secret VerifyResp ) + Nothing -> + noSub ( { model | passwordModel = m }, Cmd.map PasswordMsg c ) SearchMenuMsg lm -> let diff --git a/modules/webapp/src/main/elm/Page/Share/View.elm b/modules/webapp/src/main/elm/Page/Share/View.elm index 5c0f941b..baaad70a 100644 --- a/modules/webapp/src/main/elm/Page/Share/View.elm +++ b/modules/webapp/src/main/elm/Page/Share/View.elm @@ -9,6 +9,7 @@ module Page.Share.View exposing (viewContent, viewSidebar) import Api.Model.VersionInfo exposing (VersionInfo) import Comp.Basic as B +import Comp.SharePasswordForm import Data.Flags exposing (Flags) import Data.Items import Data.UiSettings exposing (UiSettings) @@ -35,8 +36,8 @@ viewSidebar texts visible flags settings model = ] -viewContent : Texts -> Flags -> VersionInfo -> UiSettings -> Model -> Html Msg -viewContent texts flags versionInfo uiSettings model = +viewContent : Texts -> Flags -> VersionInfo -> UiSettings -> String -> Model -> Html Msg +viewContent texts flags versionInfo uiSettings shareId model = case model.mode of ModeInitial -> div @@ -54,15 +55,15 @@ viewContent texts flags versionInfo uiSettings model = passwordContent texts flags versionInfo model ModeShare -> - mainContent texts flags uiSettings model + mainContent texts flags uiSettings shareId model --- Helpers -mainContent : Texts -> Flags -> UiSettings -> Model -> Html Msg -mainContent texts _ settings model = +mainContent : Texts -> Flags -> UiSettings -> String -> Model -> Html Msg +mainContent texts _ settings shareId model = div [ id "content" , class "h-full flex flex-col" @@ -75,7 +76,7 @@ mainContent texts _ settings model = [ text <| Maybe.withDefault "" model.verifyResult.name ] , Menubar.view texts model - , Results.view texts settings model + , Results.view texts settings shareId model ] @@ -86,76 +87,6 @@ passwordContent texts flags versionInfo model = , class "h-full flex flex-col items-center justify-center w-full" , class S.content ] - [ div [ class ("flex flex-col px-4 sm:px-6 md:px-8 lg:px-10 py-8 rounded-md " ++ S.box) ] - [ div [ class "self-center" ] - [ img - [ class "w-16 py-2" - , src (flags.config.docspellAssetPath ++ "/img/logo-96.png") - ] - [] - ] - , div [ class "font-medium self-center text-xl sm:text-2xl" ] - [ text texts.passwordRequired - ] - , Html.form - [ action "#" - , onSubmit SubmitPassword - , autocomplete False - ] - [ div [ class "flex flex-col my-3" ] - [ label - [ for "password" - , class S.inputLabel - ] - [ text texts.password - ] - , div [ class "relative" ] - [ div [ class S.inputIcon ] - [ i [ class "fa fa-lock" ] [] - ] - , input - [ type_ "password" - , name "password" - , autocomplete False - , autofocus True - , tabindex 1 - , onInput SetPassword - , value model.passwordModel.password - , class ("pl-10 pr-4 py-2 rounded-lg" ++ S.textInput) - , placeholder texts.password - ] - [] - ] - ] - , div [ class "flex flex-col my-3" ] - [ button - [ type_ "submit" - , class S.primaryButton - ] - [ text texts.passwordSubmitButton - ] - ] - , div - [ class S.errorMessage - , classList [ ( "hidden", not model.passwordModel.passwordFailed ) ] - ] - [ text texts.passwordFailed - ] - ] - ] - , a - [ class "inline-flex items-center mt-4 text-xs opacity-50 hover:opacity-90" - , href "https://docspell.org" - , target "_new" - ] - [ img - [ src (flags.config.docspellAssetPath ++ "/img/logo-mc-96.png") - , class "w-3 h-3 mr-1" - ] - [] - , span [] - [ text "Docspell " - , text versionInfo.version - ] - ] + [ Html.map PasswordMsg + (Comp.SharePasswordForm.view texts.passwordForm flags versionInfo model.passwordModel) ] diff --git a/modules/webapp/src/main/elm/Page/ShareDetail/Data.elm b/modules/webapp/src/main/elm/Page/ShareDetail/Data.elm new file mode 100644 index 00000000..08c412bd --- /dev/null +++ b/modules/webapp/src/main/elm/Page/ShareDetail/Data.elm @@ -0,0 +1,56 @@ +module Page.ShareDetail.Data exposing (Model, Msg(..), PageError(..), ViewMode(..), init) + +import Api +import Api.Model.ItemDetail exposing (ItemDetail) +import Api.Model.ShareSecret exposing (ShareSecret) +import Api.Model.ShareVerifyResult exposing (ShareVerifyResult) +import Comp.SharePasswordForm +import Data.Flags exposing (Flags) +import Http + + +type ViewMode + = ViewNormal + | ViewPassword + | ViewLoading + + +type PageError + = PageErrorNone + | PageErrorHttp Http.Error + | PageErrorAuthFail + + +type alias Model = + { item : ItemDetail + , verifyResult : ShareVerifyResult + , passwordModel : Comp.SharePasswordForm.Model + , viewMode : ViewMode + , pageError : PageError + } + + +type Msg + = VerifyResp (Result Http.Error ShareVerifyResult) + | GetItemResp (Result Http.Error ItemDetail) + | PasswordMsg Comp.SharePasswordForm.Msg + + +emptyModel : ViewMode -> Model +emptyModel vm = + { item = Api.Model.ItemDetail.empty + , verifyResult = Api.Model.ShareVerifyResult.empty + , passwordModel = Comp.SharePasswordForm.init + , viewMode = vm + , pageError = PageErrorNone + } + + +init : Maybe ( String, String ) -> Flags -> ( Model, Cmd Msg ) +init mids flags = + case mids of + Just ( shareId, _ ) -> + ( emptyModel ViewLoading, Api.verifyShare flags (ShareSecret shareId Nothing) VerifyResp ) + + Nothing -> + ( emptyModel ViewLoading, Cmd.none ) diff --git a/modules/webapp/src/main/elm/Page/ShareDetail/Update.elm b/modules/webapp/src/main/elm/Page/ShareDetail/Update.elm new file mode 100644 index 00000000..40aa5757 --- /dev/null +++ b/modules/webapp/src/main/elm/Page/ShareDetail/Update.elm @@ -0,0 +1,64 @@ +module Page.ShareDetail.Update exposing (update) + +import Api +import Comp.SharePasswordForm +import Data.Flags exposing (Flags) +import Page.ShareDetail.Data exposing (..) + + +update : String -> String -> Flags -> Msg -> Model -> ( Model, Cmd Msg ) +update shareId itemId flags msg model = + case msg of + VerifyResp (Ok res) -> + if res.success then + ( { model + | pageError = PageErrorNone + , viewMode = ViewLoading + , verifyResult = res + } + , Api.itemDetailShare flags res.token itemId GetItemResp + ) + + else if res.passwordRequired then + ( { model + | pageError = PageErrorNone + , viewMode = ViewPassword + } + , Cmd.none + ) + + else + ( { model | pageError = PageErrorAuthFail } + , Cmd.none + ) + + VerifyResp (Err err) -> + ( { model | pageError = PageErrorHttp err }, Cmd.none ) + + GetItemResp (Ok item) -> + ( { model + | item = item + , viewMode = ViewNormal + , pageError = PageErrorNone + } + , Cmd.none + ) + + GetItemResp (Err err) -> + ( { model | viewMode = ViewNormal, pageError = PageErrorHttp err }, Cmd.none ) + + PasswordMsg lmsg -> + let + ( m, c, res ) = + Comp.SharePasswordForm.update shareId flags lmsg model.passwordModel + in + case res of + Just verifyResult -> + update shareId + itemId + flags + (VerifyResp (Ok verifyResult)) + model + + Nothing -> + ( { model | passwordModel = m }, Cmd.map PasswordMsg c ) diff --git a/modules/webapp/src/main/elm/Page/ShareDetail/View.elm b/modules/webapp/src/main/elm/Page/ShareDetail/View.elm new file mode 100644 index 00000000..aad6fa3f --- /dev/null +++ b/modules/webapp/src/main/elm/Page/ShareDetail/View.elm @@ -0,0 +1,108 @@ +module Page.ShareDetail.View exposing (viewContent, viewSidebar) + +import Api.Model.VersionInfo exposing (VersionInfo) +import Comp.Basic as B +import Comp.SharePasswordForm +import Data.Flags exposing (Flags) +import Data.UiSettings exposing (UiSettings) +import Html exposing (..) +import Html.Attributes exposing (..) +import Messages.Page.ShareDetail exposing (Texts) +import Page exposing (Page(..)) +import Page.ShareDetail.Data exposing (..) +import Styles as S + + +viewSidebar : Texts -> Bool -> Flags -> UiSettings -> Model -> Html Msg +viewSidebar texts visible flags settings model = + div + [ id "sidebar" + , class "hidden" + ] + [] + + +viewContent : Texts -> Flags -> UiSettings -> VersionInfo -> String -> String -> Model -> Html Msg +viewContent texts flags uiSettings versionInfo shareId itemId model = + case model.viewMode of + ViewLoading -> + div + [ id "content" + , class "h-full w-full flex flex-col text-5xl" + , class S.content + ] + [ B.loadingDimmer + { active = model.pageError == PageErrorNone + , label = "" + } + ] + + ViewPassword -> + passwordContent texts flags versionInfo model + + ViewNormal -> + mainContent texts flags uiSettings shareId model + + + +--- Helper + + +mainContent : Texts -> Flags -> UiSettings -> String -> Model -> Html Msg +mainContent texts flags settings shareId model = + div + [ class "flex flex-col" + , class S.content + ] + [ itemHead texts shareId model + , div [ class "flex flex-col sm:flex-row" ] + [ itemData texts model + , itemPreview texts flags settings model + ] + ] + + +itemData : Texts -> Model -> Html Msg +itemData texts model = + div [ class "flex" ] + [] + + +{-| Using ItemDetail Model to be able to reuse SingleAttachment component +-} +itemPreview : Texts -> Flags -> UiSettings -> Model -> Html Msg +itemPreview texts flags settings model = + div [ class "flex flex-grow" ] + [] + + +itemHead : Texts -> String -> Model -> Html Msg +itemHead texts shareId model = + div [ class "flex flex-col sm:flex-row" ] + [ div [ class "flex flex-grow items-center" ] + [ h1 [ class S.header1 ] + [ text model.item.name + ] + ] + , div [ class "flex flex-row items-center justify-end" ] + [ B.secondaryBasicButton + { label = "Close" + , icon = "fa fa-times" + , disabled = False + , handler = Page.href (SharePage shareId) + , attrs = [] + } + ] + ] + + +passwordContent : Texts -> Flags -> VersionInfo -> Model -> Html Msg +passwordContent texts flags versionInfo model = + div + [ id "content" + , class "h-full flex flex-col items-center justify-center w-full" + , class S.content + ] + [ Html.map PasswordMsg + (Comp.SharePasswordForm.view texts.passwordForm flags versionInfo model.passwordModel) + ] diff --git a/modules/webapp/src/main/elm/Util/Http.elm b/modules/webapp/src/main/elm/Util/Http.elm index dd965b23..5088f892 100644 --- a/modules/webapp/src/main/elm/Util/Http.elm +++ b/modules/webapp/src/main/elm/Util/Http.elm @@ -14,6 +14,7 @@ module Util.Http exposing , authTask , executeIn , jsonResolver + , shareGet , sharePost ) @@ -167,6 +168,24 @@ authGet req = } +shareGet : + { url : String + , token : String + , expect : Http.Expect msg + } + -> Cmd msg +shareGet req = + shareReq + { url = req.url + , token = req.token + , body = Http.emptyBody + , expect = req.expect + , method = "GET" + , headers = [] + , tracker = Nothing + } + + authDelete : { url : String , account : AuthResult