Show item detail for a shared item

This commit is contained in:
eikek 2021-10-07 22:02:31 +02:00
parent 006791deb4
commit 7cbdf919f4
18 changed files with 458 additions and 57 deletions

View File

@ -16,12 +16,13 @@
"elm/html": "1.0.0", "elm/html": "1.0.0",
"elm/http": "2.0.0", "elm/http": "2.0.0",
"elm/json": "1.1.3", "elm/json": "1.1.3",
"elm/svg": "1.0.1",
"elm/time": "1.0.0", "elm/time": "1.0.0",
"elm/url": "1.0.0", "elm/url": "1.0.0",
"elm-explorations/markdown": "1.0.0", "elm-explorations/markdown": "1.0.0",
"justinmimbs/date": "3.1.2", "justinmimbs/date": "3.1.2",
"norpan/elm-html5-drag-drop": "3.1.4", "norpan/elm-html5-drag-drop": "3.1.4",
"pablohirafuji/elm-qrcode": "3.3.1", "pablohirafuji/elm-qrcode": "4.0.1",
"ryannhg/date-format": "2.3.0", "ryannhg/date-format": "2.3.0",
"truqu/elm-base64": "2.0.4", "truqu/elm-base64": "2.0.4",
"ursi/elm-scroll": "1.0.0", "ursi/elm-scroll": "1.0.0",
@ -33,7 +34,6 @@
"elm/bytes": "1.0.8", "elm/bytes": "1.0.8",
"elm/parser": "1.1.0", "elm/parser": "1.1.0",
"elm/regex": "1.0.0", "elm/regex": "1.0.0",
"elm/svg": "1.0.1",
"elm/virtual-dom": "1.0.2", "elm/virtual-dom": "1.0.2",
"elm-community/list-extra": "8.2.4", "elm-community/list-extra": "8.2.4",
"folkertdev/elm-flate": "2.0.4", "folkertdev/elm-flate": "2.0.4",

View File

@ -613,8 +613,19 @@ initPage model_ page =
] ]
model model
SharePage _ -> SharePage id ->
( model, Cmd.none, Sub.none ) let
cmd =
Cmd.map ShareMsg (Page.Share.Data.initCmd id model.flags)
shareModel =
model.shareModel
in
if shareModel.initialized then
( model, Cmd.none, Sub.none )
else
( { model | shareModel = { shareModel | initialized = True } }, cmd, Sub.none )
ShareDetailPage _ _ -> ShareDetailPage _ _ ->
case model_.page of case model_.page of

View File

@ -451,6 +451,8 @@ viewShareDetail texts shareId itemId model =
model.sidebarVisible model.sidebarVisible
model.flags model.flags
model.uiSettings model.uiSettings
shareId
itemId
model.shareDetailModel model.shareDetailModel
) )
, Html.map ShareDetailMsg , Html.map ShareDetailMsg

View File

@ -17,6 +17,7 @@ import Html.Attributes exposing (..)
import Html.Events exposing (onClick) import Html.Events exposing (onClick)
import QRCode import QRCode
import Styles as S import Styles as S
import Svg.Attributes as SvgA
view : Flags -> String -> Model -> UrlId -> Html Msg view : Flags -> String -> Model -> UrlId -> Html Msg
@ -111,7 +112,7 @@ type UrlId
qrCodeView : String -> Html msg qrCodeView : String -> Html msg
qrCodeView message = qrCodeView message =
QRCode.encode message QRCode.fromString message
|> Result.map QRCode.toSvg |> Result.map (QRCode.toSvg [ SvgA.class "w-64 h-64" ])
|> Result.withDefault |> Result.withDefault
(text "Error generating QR code") (text "Error generating QR code")

View File

@ -23,6 +23,7 @@ import Markdown
import Messages.Comp.OtpSetup exposing (Texts) import Messages.Comp.OtpSetup exposing (Texts)
import QRCode import QRCode
import Styles as S import Styles as S
import Svg.Attributes as SvgA
type Model type Model
@ -389,8 +390,8 @@ viewDisabled texts model =
qrCodeView : Texts -> String -> Html msg qrCodeView : Texts -> String -> Html msg
qrCodeView texts message = qrCodeView texts message =
QRCode.encode message QRCode.fromString message
|> Result.map QRCode.toSvg |> Result.map (QRCode.toSvg [ SvgA.class "w-64 h-64" ])
|> Result.withDefault |> Result.withDefault
(Html.text texts.errorGeneratingQR) (Html.text texts.errorGeneratingQR)

View File

@ -14,6 +14,7 @@ import Html.Attributes exposing (..)
import Messages.Comp.ShareView exposing (Texts) import Messages.Comp.ShareView exposing (Texts)
import QRCode import QRCode
import Styles as S import Styles as S
import Svg.Attributes as SvgA
type alias ViewSettings = type alias ViewSettings =
@ -178,7 +179,7 @@ viewDisabled cfg texts share =
qrCodeView : Texts -> String -> Html msg qrCodeView : Texts -> String -> Html msg
qrCodeView texts message = qrCodeView texts message =
QRCode.encode message QRCode.fromString message
|> Result.map QRCode.toSvg |> Result.map (QRCode.toSvg [ SvgA.class "w-64 h-64" ])
|> Result.withDefault |> Result.withDefault
(Html.text texts.qrCodeError) (Html.text texts.qrCodeError)

View File

@ -32,6 +32,7 @@ import Messages.Comp.SourceManage exposing (Texts)
import Ports import Ports
import QRCode import QRCode
import Styles as S import Styles as S
import Svg.Attributes as SvgA
type alias Model = type alias Model =
@ -226,8 +227,8 @@ update flags msg model =
qrCodeView : Texts -> String -> Html msg qrCodeView : Texts -> String -> Html msg
qrCodeView texts message = qrCodeView texts message =
QRCode.encode message QRCode.fromString message
|> Result.map QRCode.toSvg |> Result.map (QRCode.toSvg [ SvgA.class "w-64 h-64" ])
|> Result.withDefault |> Result.withDefault
(Html.text texts.errorGeneratingQR) (Html.text texts.errorGeneratingQR)

View File

@ -0,0 +1,95 @@
module Comp.UrlCopy exposing (..)
import Comp.Basic as B
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Ports
import QRCode
import Styles as S
import Svg.Attributes as SvgA
type Msg
= Print String
update : Msg -> Cmd msg
update msg =
case msg of
Print id ->
Ports.printElement id
initCopy : String -> Cmd msg
initCopy data =
Ports.initClipboard <| clipboardData data
clipboardData : String -> ( String, String )
clipboardData data =
( "share-url", "#button-share-url" )
view : String -> Html Msg
view data =
let
( elementId, buttonId ) =
clipboardData data
btnId =
String.dropLeft 1 buttonId
printId =
"print-qr-code"
in
div [ class "flex flex-col items-center" ]
[ div
[ class S.border
, class S.qrCode
, id printId
]
[ qrCodeView data
]
, div
[ class "flex w-64"
]
[ p
[ id elementId
, class "font-mono text-xs py-2 mx-auto break-all"
]
[ text data
]
]
, div [ class "flex flex-row mt-1 space-x-2 items-center w-full" ]
[ B.primaryButton
{ label = "Copy"
, icon = "fa fa-copy"
, handler = href "#"
, disabled = False
, attrs =
[ id btnId
, class "flex flex-grow items-center justify-center"
, attribute "data-clipboard-target" ("#" ++ elementId)
]
}
, B.primaryButton
{ label = "Print"
, icon = "fa fa-print"
, handler = onClick (Print printId)
, disabled = False
, attrs =
[ href "#"
, class "flex flex-grow items-center justify-center"
]
}
]
]
qrCodeView : String -> Html msg
qrCodeView message =
QRCode.fromString message
|> Result.map (QRCode.toSvg [ SvgA.class "w-64 h-64" ])
|> Result.withDefault
(text "Error generating QR code")

View File

@ -7,7 +7,9 @@
module Messages.Page.Share exposing (..) module Messages.Page.Share exposing (..)
import Http
import Messages.Basics import Messages.Basics
import Messages.Comp.HttpError
import Messages.Comp.ItemCardList import Messages.Comp.ItemCardList
import Messages.Comp.SearchMenu import Messages.Comp.SearchMenu
import Messages.Comp.SharePasswordForm import Messages.Comp.SharePasswordForm
@ -18,6 +20,8 @@ type alias Texts =
, basics : Messages.Basics.Texts , basics : Messages.Basics.Texts
, itemCardList : Messages.Comp.ItemCardList.Texts , itemCardList : Messages.Comp.ItemCardList.Texts
, passwordForm : Messages.Comp.SharePasswordForm.Texts , passwordForm : Messages.Comp.SharePasswordForm.Texts
, httpError : Http.Error -> String
, authFailed : String
} }
@ -27,6 +31,8 @@ gb =
, basics = Messages.Basics.gb , basics = Messages.Basics.gb
, itemCardList = Messages.Comp.ItemCardList.gb , itemCardList = Messages.Comp.ItemCardList.gb
, passwordForm = Messages.Comp.SharePasswordForm.gb , passwordForm = Messages.Comp.SharePasswordForm.gb
, authFailed = "This share does not exist."
, httpError = Messages.Comp.HttpError.gb
} }
@ -36,4 +42,6 @@ de =
, basics = Messages.Basics.de , basics = Messages.Basics.de
, itemCardList = Messages.Comp.ItemCardList.de , itemCardList = Messages.Comp.ItemCardList.de
, passwordForm = Messages.Comp.SharePasswordForm.de , passwordForm = Messages.Comp.SharePasswordForm.de
, authFailed = "Diese Freigabe existiert nicht."
, httpError = Messages.Comp.HttpError.de
} }

View File

@ -1,28 +1,54 @@
module Messages.Page.ShareDetail exposing (..) module Messages.Page.ShareDetail exposing (..)
import Data.Fields exposing (Field)
import Http
import Messages.Basics
import Messages.Comp.HttpError
import Messages.Comp.SharePasswordForm import Messages.Comp.SharePasswordForm
import Messages.Data.Fields
import Messages.DateFormat import Messages.DateFormat
import Messages.UiLanguage exposing (UiLanguage(..)) import Messages.UiLanguage exposing (UiLanguage(..))
type alias Texts = type alias Texts =
{ passwordForm : Messages.Comp.SharePasswordForm.Texts { passwordForm : Messages.Comp.SharePasswordForm.Texts
, basics : Messages.Basics.Texts
, field : Field -> String
, formatDateLong : Int -> String , formatDateLong : Int -> String
, formatDateShort : Int -> String , formatDateShort : Int -> String
, httpError : Http.Error -> String
, authFailed : String
, tagsAndFields : String
, noName : String
, unconfirmed : String
} }
gb : Texts gb : Texts
gb = gb =
{ passwordForm = Messages.Comp.SharePasswordForm.gb { passwordForm = Messages.Comp.SharePasswordForm.gb
, basics = Messages.Basics.gb
, field = Messages.Data.Fields.gb
, formatDateLong = Messages.DateFormat.formatDateLong English , formatDateLong = Messages.DateFormat.formatDateLong English
, formatDateShort = Messages.DateFormat.formatDateShort English , formatDateShort = Messages.DateFormat.formatDateShort English
, authFailed = "This share does not exist."
, httpError = Messages.Comp.HttpError.gb
, tagsAndFields = "Tags & Fields"
, noName = "No name"
, unconfirmed = "Unconfirmed"
} }
de : Texts de : Texts
de = de =
{ passwordForm = Messages.Comp.SharePasswordForm.de { passwordForm = Messages.Comp.SharePasswordForm.de
, basics = Messages.Basics.de
, field = Messages.Data.Fields.de
, formatDateLong = Messages.DateFormat.formatDateLong German , formatDateLong = Messages.DateFormat.formatDateLong German
, formatDateShort = Messages.DateFormat.formatDateShort German , formatDateShort = Messages.DateFormat.formatDateShort German
, authFailed = "Diese Freigabe existiert nicht."
, httpError = Messages.Comp.HttpError.de
, tagsAndFields = "Tags & Felder"
, noName = "Kein Name"
, unconfirmed = "Nicht bestätigt"
} }

View File

@ -116,6 +116,9 @@ hasSidebar page =
SharePage _ -> SharePage _ ->
True True
ShareDetailPage _ _ ->
True
_ -> _ ->
isSecured page isSecured page

View File

@ -5,7 +5,7 @@
-} -}
module Page.Share.Data exposing (Mode(..), Model, Msg(..), PageError(..), init) module Page.Share.Data exposing (Mode(..), Model, Msg(..), PageError(..), init, initCmd)
import Api import Api
import Api.Model.ItemLightList exposing (ItemLightList) import Api.Model.ItemLightList exposing (ItemLightList)
@ -41,6 +41,7 @@ type alias Model =
, powerSearchInput : Comp.PowerSearchInput.Model , powerSearchInput : Comp.PowerSearchInput.Model
, searchInProgress : Bool , searchInProgress : Bool
, itemListModel : Comp.ItemCardList.Model , itemListModel : Comp.ItemCardList.Model
, initialized : Bool
} }
@ -54,17 +55,27 @@ emptyModel flags =
, powerSearchInput = Comp.PowerSearchInput.init , powerSearchInput = Comp.PowerSearchInput.init
, searchInProgress = False , searchInProgress = False
, itemListModel = Comp.ItemCardList.init , itemListModel = Comp.ItemCardList.init
, initialized = False
} }
init : Maybe String -> Flags -> ( Model, Cmd Msg ) init : Maybe String -> Flags -> ( Model, Cmd Msg )
init shareId flags = init shareId flags =
let
em =
emptyModel flags
in
case shareId of case shareId of
Just id -> Just id ->
( emptyModel flags, Api.verifyShare flags (ShareSecret id Nothing) VerifyResp ) ( { em | initialized = True }, Api.verifyShare flags (ShareSecret id Nothing) VerifyResp )
Nothing -> Nothing ->
( emptyModel flags, Cmd.none ) ( em, Cmd.none )
initCmd : String -> Flags -> Cmd Msg
initCmd shareId flags =
Api.verifyShare flags (ShareSecret shareId Nothing) VerifyResp
type Msg type Msg

View File

@ -42,13 +42,18 @@ viewContent texts flags versionInfo uiSettings shareId model =
ModeInitial -> ModeInitial ->
div div
[ id "content" [ id "content"
, class "h-full w-full flex flex-col text-5xl" , class "h-full w-full flex flex-col"
, class S.content , class S.content
] ]
[ B.loadingDimmer [ div [ class " text-5xl" ]
{ active = model.pageError == PageErrorNone [ B.loadingDimmer
, label = "" { active = model.pageError == PageErrorNone
} , label = ""
}
]
, div [ class "my-4 text-lg" ]
[ errorMessage texts model
]
] ]
ModePassword -> ModePassword ->
@ -76,10 +81,28 @@ mainContent texts flags settings shareId model =
[ text <| Maybe.withDefault "" model.verifyResult.name [ text <| Maybe.withDefault "" model.verifyResult.name
] ]
, Menubar.view texts model , Menubar.view texts model
, errorMessage texts model
, Results.view texts settings flags shareId model , Results.view texts settings flags shareId model
] ]
errorMessage : Texts -> Model -> Html Msg
errorMessage texts model =
case model.pageError of
PageErrorNone ->
span [ class "hidden" ] []
PageErrorAuthFail ->
div [ class S.errorMessage ]
[ text texts.authFailed
]
PageErrorHttp err ->
div [ class S.errorMessage ]
[ text (texts.httpError err)
]
passwordContent : Texts -> Flags -> VersionInfo -> Model -> Html Msg passwordContent : Texts -> Flags -> VersionInfo -> Model -> Html Msg
passwordContent texts flags versionInfo model = passwordContent texts flags versionInfo model =
div div

View File

@ -5,6 +5,7 @@ import Api.Model.ItemDetail exposing (ItemDetail)
import Api.Model.ShareSecret exposing (ShareSecret) import Api.Model.ShareSecret exposing (ShareSecret)
import Api.Model.ShareVerifyResult exposing (ShareVerifyResult) import Api.Model.ShareVerifyResult exposing (ShareVerifyResult)
import Comp.SharePasswordForm import Comp.SharePasswordForm
import Comp.UrlCopy
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Http import Http
@ -27,6 +28,8 @@ type alias Model =
, passwordModel : Comp.SharePasswordForm.Model , passwordModel : Comp.SharePasswordForm.Model
, viewMode : ViewMode , viewMode : ViewMode
, pageError : PageError , pageError : PageError
, attachMenuOpen : Bool
, visibleAttach : Int
} }
@ -34,6 +37,9 @@ type Msg
= VerifyResp (Result Http.Error ShareVerifyResult) = VerifyResp (Result Http.Error ShareVerifyResult)
| GetItemResp (Result Http.Error ItemDetail) | GetItemResp (Result Http.Error ItemDetail)
| PasswordMsg Comp.SharePasswordForm.Msg | PasswordMsg Comp.SharePasswordForm.Msg
| SelectActiveAttachment Int
| ToggleSelectAttach
| UrlCopyMsg Comp.UrlCopy.Msg
emptyModel : ViewMode -> Model emptyModel : ViewMode -> Model
@ -43,14 +49,18 @@ emptyModel vm =
, passwordModel = Comp.SharePasswordForm.init , passwordModel = Comp.SharePasswordForm.init
, viewMode = vm , viewMode = vm
, pageError = PageErrorNone , pageError = PageErrorNone
, attachMenuOpen = False
, visibleAttach = 0
} }
init : Maybe ( String, String ) -> Flags -> ( Model, Cmd Msg ) init : Maybe ( String, String ) -> Flags -> ( Model, Cmd Msg )
init mids flags = init mids flags =
case mids of case mids of
Just ( shareId, _ ) -> Just ( shareId, itemId ) ->
( emptyModel ViewLoading, Api.verifyShare flags (ShareSecret shareId Nothing) VerifyResp ) ( emptyModel ViewLoading
, Api.verifyShare flags (ShareSecret shareId Nothing) VerifyResp
)
Nothing -> Nothing ->
( emptyModel ViewLoading, Cmd.none ) ( emptyModel ViewLoading, Cmd.none )

View File

@ -2,7 +2,9 @@ module Page.ShareDetail.Update exposing (update)
import Api import Api
import Comp.SharePasswordForm import Comp.SharePasswordForm
import Comp.UrlCopy
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Page exposing (Page(..))
import Page.ShareDetail.Data exposing (..) import Page.ShareDetail.Data exposing (..)
@ -36,12 +38,16 @@ update shareId itemId flags msg model =
( { model | pageError = PageErrorHttp err }, Cmd.none ) ( { model | pageError = PageErrorHttp err }, Cmd.none )
GetItemResp (Ok item) -> GetItemResp (Ok item) ->
let
url =
Page.pageToString (ShareDetailPage shareId itemId)
in
( { model ( { model
| item = item | item = item
, viewMode = ViewNormal , viewMode = ViewNormal
, pageError = PageErrorNone , pageError = PageErrorNone
} }
, Cmd.none , Comp.UrlCopy.initCopy url
) )
GetItemResp (Err err) -> GetItemResp (Err err) ->
@ -62,3 +68,21 @@ update shareId itemId flags msg model =
Nothing -> Nothing ->
( { model | passwordModel = m }, Cmd.map PasswordMsg c ) ( { model | passwordModel = m }, Cmd.map PasswordMsg c )
SelectActiveAttachment pos ->
( { model
| visibleAttach = pos
, attachMenuOpen = False
}
, Cmd.none
)
ToggleSelectAttach ->
( { model | attachMenuOpen = not model.attachMenuOpen }, Cmd.none )
UrlCopyMsg lm ->
let
cmd =
Comp.UrlCopy.update lm
in
( model, cmd )

View File

@ -1,30 +1,41 @@
module Page.ShareDetail.View exposing (viewContent, viewSidebar) module Page.ShareDetail.View exposing (viewContent, viewSidebar)
import Api import Api
import Api.Model.Attachment exposing (Attachment)
import Api.Model.VersionInfo exposing (VersionInfo) import Api.Model.VersionInfo exposing (VersionInfo)
import Comp.Basic as B import Comp.Basic as B
import Comp.SharePasswordForm import Comp.SharePasswordForm
import Comp.UrlCopy
import Data.Fields
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.Icons as Icons import Data.Icons as Icons
import Data.ItemTemplate as IT exposing (ItemTemplate) import Data.ItemTemplate as IT exposing (ItemTemplate)
import Data.UiSettings exposing (UiSettings) import Data.UiSettings exposing (UiSettings)
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Messages.Page.ShareDetail exposing (Texts) import Messages.Page.ShareDetail exposing (Texts)
import Page exposing (Page(..)) import Page exposing (Page(..))
import Page.ShareDetail.Data exposing (..) import Page.ShareDetail.Data exposing (..)
import Styles as S import Styles as S
import Util.CustomField import Util.CustomField
import Util.Item import Util.Item
import Util.List
import Util.Size
import Util.String
viewSidebar : Texts -> Bool -> Flags -> UiSettings -> Model -> Html Msg viewSidebar : Texts -> Bool -> Flags -> UiSettings -> String -> String -> Model -> Html Msg
viewSidebar texts visible flags settings model = viewSidebar texts visible flags settings shareId itemId model =
div div
[ id "sidebar" [ id "sidebar"
, class "hidden" , classList [ ( "hidden", not visible ) ]
, class S.sidebar
]
[ div [ class "pt-2" ]
[ itemData texts flags model shareId itemId
]
] ]
[]
viewContent : Texts -> Flags -> UiSettings -> VersionInfo -> String -> String -> Model -> Html Msg viewContent : Texts -> Flags -> UiSettings -> VersionInfo -> String -> String -> Model -> Html Msg
@ -36,10 +47,15 @@ viewContent texts flags uiSettings versionInfo shareId itemId model =
, class "h-full w-full flex flex-col text-5xl" , class "h-full w-full flex flex-col text-5xl"
, class S.content , class S.content
] ]
[ B.loadingDimmer [ div [ class "text-5xl" ]
{ active = model.pageError == PageErrorNone [ B.loadingDimmer
, label = "" { active = model.pageError == PageErrorNone
} , label = ""
}
]
, div [ class "my-4 text-lg" ]
[ errorMessage texts model
]
] ]
ViewPassword -> ViewPassword ->
@ -60,18 +76,18 @@ mainContent texts flags settings shareId model =
, class S.content , class S.content
] ]
[ itemHead texts shareId model [ itemHead texts shareId model
, div [ class "flex flex-col sm:flex-row sm:space-x-4 relative h-full" ] , errorMessage texts model
[ itemData texts model , div [ class "relative h-full" ]
, itemPreview texts flags settings model [ itemPreview texts flags settings model
] ]
] ]
itemData : Texts -> Model -> Html Msg itemData : Texts -> Flags -> Model -> String -> String -> Html Msg
itemData texts model = itemData texts flags model shareId itemId =
let let
boxStyle = boxStyle =
"mb-4 sm:mb-6 max-w-sm" "mb-4 sm:mb-6"
headerStyle = headerStyle =
"py-2 bg-blue-50 hover:bg-blue-100 dark:bg-bluegray-700 dark:hover:bg-opacity-100 dark:hover:bg-bluegray-600 text-lg font-medium rounded-lg" "py-2 bg-blue-50 hover:bg-blue-100 dark:bg-bluegray-700 dark:hover:bg-opacity-100 dark:hover:bg-bluegray-600 text-lg font-medium rounded-lg"
@ -92,11 +108,11 @@ itemData texts model =
] ]
Nothing Nothing
in in
div [ class "flex flex-col pr-2 sm:w-1/3" ] div [ class "flex flex-col" ]
[ div [ class boxStyle ] [ div [ class boxStyle ]
[ div [ class headerStyle ] [ div [ class headerStyle ]
[ Icons.dateIcon2 "mr-2 ml-2" [ Icons.dateIcon2 "mr-2 ml-2"
, text "Date" , text (texts.field Data.Fields.Date)
] ]
, div [ class "text-lg ml-2" ] , div [ class "text-lg ml-2" ]
[ Util.Item.toItemLight model.item [ Util.Item.toItemLight model.item
@ -104,10 +120,24 @@ itemData texts model =
|> text |> text
] ]
] ]
, div
[ class boxStyle
, classList [ ( "hidden", model.item.dueDate == Nothing ) ]
]
[ div [ class headerStyle ]
[ Icons.dueDateIcon2 "mr-2 ml-2"
, text (texts.field Data.Fields.DueDate)
]
, div [ class "text-lg ml-2" ]
[ Util.Item.toItemLight model.item
|> IT.render IT.dueDateLong (templateCtx texts)
|> text
]
]
, div [ class boxStyle ] , div [ class boxStyle ]
[ div [ class headerStyle ] [ div [ class headerStyle ]
[ Icons.tagsIcon2 "mr-2 ml-2" [ Icons.tagsIcon2 "mr-2 ml-2"
, text "Tags & Fields" , text texts.tagsAndFields
] ]
, div [ class "flex flex-row items-center flex-wrap font-medium my-1" ] , div [ class "flex flex-row items-center flex-wrap font-medium my-1" ]
(List.map showTag model.item.tags ++ List.map showField model.item.customfields) (List.map showTag model.item.tags ++ List.map showField model.item.customfields)
@ -115,7 +145,7 @@ itemData texts model =
, div [ class boxStyle ] , div [ class boxStyle ]
[ div [ class headerStyle ] [ div [ class headerStyle ]
[ Icons.correspondentIcon2 "mr-2 ml-2" [ Icons.correspondentIcon2 "mr-2 ml-2"
, text "Correspondent" , text texts.basics.correspondent
] ]
, div [ class "text-lg ml-2" ] , div [ class "text-lg ml-2" ]
[ Util.Item.toItemLight model.item [ Util.Item.toItemLight model.item
@ -126,7 +156,7 @@ itemData texts model =
, div [ class boxStyle ] , div [ class boxStyle ]
[ div [ class headerStyle ] [ div [ class headerStyle ]
[ Icons.concernedIcon2 "mr-2 ml-2" [ Icons.concernedIcon2 "mr-2 ml-2"
, text "Concerning" , text texts.basics.concerning
] ]
, div [ class "text-lg ml-2" ] , div [ class "text-lg ml-2" ]
[ Util.Item.toItemLight model.item [ Util.Item.toItemLight model.item
@ -134,40 +164,110 @@ itemData texts model =
|> text |> text
] ]
] ]
, div [ class boxStyle ]
[ div [ class headerStyle ]
[ i [ class "fa fa-copy mr-2 ml-2" ] []
, text "Copy URL"
]
, div [ class "flex flex-col items-center py-2" ]
[ Html.map UrlCopyMsg
(Comp.UrlCopy.view
(flags.config.baseUrl
++ Page.pageToString
(ShareDetailPage shareId itemId)
)
)
]
]
] ]
itemPreview : Texts -> Flags -> UiSettings -> Model -> Html Msg itemPreview : Texts -> Flags -> UiSettings -> Model -> Html Msg
itemPreview texts flags settings model = itemPreview texts flags settings model =
let let
id = attach =
List.head model.item.attachments Util.List.get model.item.attachments model.visibleAttach
|> Maybe.map .id |> Maybe.withDefault Api.Model.Attachment.empty
|> Maybe.withDefault ""
attachName =
Maybe.withDefault (texts.noName ++ ".pdf") attach.name
in in
div div
[ class "flex flex-grow" [ class "flex flex-grow flex-col h-full border-t dark:border-bluegray-600"
, style "min-height" "500px"
] ]
[ embed [ div [ class "flex flex-col sm:flex-row items-center py-1 px-1 border-l border-r dark:border-bluegray-600" ]
[ src (Data.UiSettings.pdfUrl settings flags (Api.shareFileURL id)) [ div [ class "text-base font-bold flex-grow w-full text-center sm:text-left break-all" ]
, class " h-full w-full mx-0 py-0" [ text attachName
, text " ("
, text (Util.Size.bytesReadable Util.Size.B (toFloat attach.size))
, text ")"
]
, div [ class "flex flex-row space-x-2" ]
[ B.secondaryBasicButton
{ label = ""
, icon = "fa fa-eye"
, disabled = False
, handler = href (Api.shareFileURL attach.id)
, attrs =
[ target "_new"
]
}
, B.secondaryBasicButton
{ label = ""
, icon = "fa fa-download"
, disabled = False
, handler = href (Api.shareFileURL attach.id)
, attrs =
[ download attachName
]
}
, B.secondaryBasicButton
{ label = ""
, icon = "fa fa-ellipsis-v"
, disabled = False
, handler = onClick ToggleSelectAttach
, attrs =
[ href "#"
, classList [ ( "hidden", List.length model.item.attachments <= 1 ) ]
]
}
]
]
, attachmentSelect texts model
, div
[ class "flex w-full h-full mb-4 border-b border-l border-r dark:border-bluegray-600"
, style "min-height" "500px"
]
[ embed
[ src (Data.UiSettings.pdfUrl settings flags (Api.shareFileURL attach.id))
, class " h-full w-full mx-0 py-0"
]
[]
] ]
[]
] ]
itemHead : Texts -> String -> Model -> Html Msg itemHead : Texts -> String -> Model -> Html Msg
itemHead texts shareId model = itemHead texts shareId model =
div [ class "flex flex-col sm:flex-row" ] div [ class "flex flex-col sm:flex-row mt-1" ]
[ div [ class "flex flex-grow items-center" ] [ div [ class "flex flex-grow items-center" ]
[ h1 [ class S.header1 ] [ h1
[ class S.header1
, class "items-center flex flex-row"
]
[ text model.item.name [ text model.item.name
, span
[ classList [ ( "hidden", model.item.state /= "created" ) ]
, class S.blueBasicLabel
, class "inline ml-4 text-sm"
]
[ text texts.unconfirmed
]
] ]
] ]
, div [ class "flex flex-row items-center justify-end mb-2 sm:mb-0" ] , div [ class "flex flex-row items-center justify-end mb-2 sm:mb-0" ]
[ B.secondaryBasicButton [ B.secondaryBasicButton
{ label = "Close" { label = texts.basics.back
, icon = "fa fa-times" , icon = "fa fa-times"
, disabled = False , disabled = False
, handler = Page.href (SharePage shareId) , handler = Page.href (SharePage shareId)
@ -189,6 +289,83 @@ passwordContent texts flags versionInfo model =
] ]
attachmentSelect : Texts -> Model -> Html Msg
attachmentSelect texts model =
div
[ class "flex flex-row border-l border-t border-r px-2 py-2 dark:border-bluegray-600 "
, class "overflow-x-auto overflow-y-none"
, classList
[ ( "hidden", not model.attachMenuOpen )
]
]
(List.indexedMap (menuItem texts model) model.item.attachments)
menuItem : Texts -> Model -> Int -> Attachment -> Html Msg
menuItem texts model pos attach =
let
iconClass =
"fa fa-circle ml-1"
visible =
model.visibleAttach == pos
in
a
[ classList <|
[ ( "border-blue-500 dark:border-lightblue-500", pos == 0 )
, ( "dark:border-bluegray-600", pos /= 0 )
]
, class "flex flex-col relative border rounded px-1 py-1 mr-2"
, class " hover:shadow dark:hover:border-bluegray-500"
, href "#"
, onClick (SelectActiveAttachment pos)
]
[ div
[ classList
[ ( "hidden", not visible )
]
, class "absolute right-1 top-1 text-blue-400 dark:text-lightblue-400 text-xl"
]
[ i [ class iconClass ] []
]
, div [ class "flex-grow" ]
[ img
[ src (Api.shareAttachmentPreviewURL attach.id)
, class "block w-20 mx-auto"
]
[]
]
, div [ class "mt-1 text-sm break-all w-28 text-center" ]
[ Maybe.map (Util.String.ellipsis 36) attach.name
|> Maybe.withDefault texts.noName
|> text
]
]
errorMessage : Texts -> Model -> Html Msg
errorMessage texts model =
case model.pageError of
PageErrorNone ->
span [ class "hidden" ] []
PageErrorAuthFail ->
div
[ class S.errorMessage
, class "my-4"
]
[ text texts.authFailed
]
PageErrorHttp err ->
div
[ class S.errorMessage
, class "my-4"
]
[ text (texts.httpError err)
]
templateCtx : Texts -> IT.TemplateContext templateCtx : Texts -> IT.TemplateContext
templateCtx texts = templateCtx texts =
{ dateFormatLong = texts.formatDateLong { dateFormatLong = texts.formatDateLong

View File

@ -10,7 +10,7 @@ module Styles exposing (..)
sidebar : String sidebar : String
sidebar = sidebar =
" flex flex-col flex-none md:w-80 w-full min-h-max px-2 dark:text-gray-200 shadow overflow-y-auto h-full transition-opacity transition-duration-200 scrollbar-thin scrollbar-light-sidebar dark:scrollbar-dark-sidebar" " flex flex-col flex-none md:w-80 w-full min-h-max px-2 dark:text-gray-200 overflow-y-auto h-full transition-opacity transition-duration-200 scrollbar-thin scrollbar-light-sidebar dark:scrollbar-dark-sidebar"
sidebarBg : String sidebarBg : String
@ -100,6 +100,11 @@ basicLabel =
" label border-gray-600 text-gray-600 dark:border-bluegray-300 dark:text-bluegray-300 " " label border-gray-600 text-gray-600 dark:border-bluegray-300 dark:text-bluegray-300 "
blueBasicLabel : String
blueBasicLabel =
" label border-blue-500 text-blue-500 dark:border-lightblue-200 dark:text-lightblue-200 "
--- Primary Button --- Primary Button

View File

@ -127,8 +127,10 @@ elmApp.ports.printElement.subscribe(function(id) {
w.document.write('</head>'); w.document.write('</head>');
} }
w.document.write('<body>'); w.document.write('<body>');
w.document.write('<div id="print-qr" style="width: 300px; height: 300px; padding: 5px; border: 1px solid black;">');
w.document.write(el.outerHTML); w.document.write(el.outerHTML);
w.document.write('<script type="application/javascript">window.print(); window.close();</script>'); w.document.write('</div>');
w.document.write('<script type="application/javascript">window.print();</script>');
w.document.write('</body></html>'); w.document.write('</body></html>');
} }
} }