mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-02-15 20:33:26 +00:00
Improve source view and add qrcode for urls
The qr-code for urls is added so that these urls are easy to copy into a phone. Then buttons for copying them into the clipboard have been added.
This commit is contained in:
parent
ed8f16fe73
commit
dbd27057d1
@ -616,9 +616,10 @@ def compileElm(
|
||||
|
||||
def createWebjarSource(wj: Seq[ModuleID], out: File): Seq[File] = {
|
||||
val target = out / "Webjars.scala"
|
||||
val badChars = "-.".toSet
|
||||
val fields = wj
|
||||
.map(m =>
|
||||
s"""val ${m.name.toLowerCase.filter(_ != '-')} = "/${m.name}/${m.revision}" """
|
||||
s"""val ${m.name.toLowerCase.filter(c => !badChars.contains(c))} = "/${m.name}/${m.revision}" """
|
||||
)
|
||||
.mkString("\n\n")
|
||||
val content = s"""package docspell.restserver.webapp
|
||||
|
@ -126,6 +126,7 @@ object TemplateRoutes {
|
||||
Seq(
|
||||
"/app/assets" + Webjars.jquery + "/jquery.min.js",
|
||||
"/app/assets" + Webjars.semanticui + "/semantic.min.js",
|
||||
"/app/assets" + Webjars.clipboardjs + "/clipboard.min.js",
|
||||
s"/app/assets/docspell-webapp/${BuildInfo.version}/docspell-app.js"
|
||||
),
|
||||
s"/app/assets/docspell-webapp/${BuildInfo.version}/favicon",
|
||||
|
@ -23,7 +23,6 @@ import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onCheck, onInput)
|
||||
import Http
|
||||
import Markdown
|
||||
import QRCode
|
||||
import Util.Folder exposing (mkFolderOption)
|
||||
|
||||
|
||||
@ -232,14 +231,6 @@ update flags msg model =
|
||||
--- View
|
||||
|
||||
|
||||
qrCodeView : String -> Html msg
|
||||
qrCodeView message =
|
||||
QRCode.encode message
|
||||
|> Result.map QRCode.toSvg
|
||||
|> Result.withDefault
|
||||
(Html.text "Error generating QR-Code")
|
||||
|
||||
|
||||
view : Flags -> UiSettings -> Model -> Html Msg
|
||||
view flags settings model =
|
||||
let
|
||||
@ -310,55 +301,6 @@ disappear then.
|
||||
"""
|
||||
]
|
||||
]
|
||||
, urlInfoMessage flags model
|
||||
]
|
||||
|
||||
|
||||
urlInfoMessage : Flags -> Model -> Html Msg
|
||||
urlInfoMessage flags model =
|
||||
let
|
||||
appUrl =
|
||||
flags.config.baseUrl ++ "/app/upload/" ++ model.source.id
|
||||
|
||||
apiUrl =
|
||||
flags.config.baseUrl ++ "/api/v1/open/upload/item/" ++ model.source.id
|
||||
in
|
||||
div
|
||||
[ classList
|
||||
[ ( "ui info icon message", True )
|
||||
, ( "hidden", not model.enabled || model.source.id == "" )
|
||||
]
|
||||
]
|
||||
[ div [ class "content" ]
|
||||
[ h3 [ class "ui dividingheader" ]
|
||||
[ i [ class "info icon" ] []
|
||||
, text "Public Uploads"
|
||||
]
|
||||
, p []
|
||||
[ text "This source defines URLs that can be used by anyone to send files to "
|
||||
, text "you. There is a web page that you can share or the API url can be used "
|
||||
, text "with other clients."
|
||||
]
|
||||
, dl [ class "ui list" ]
|
||||
[ dt [] [ text "Public Upload Page" ]
|
||||
, dd []
|
||||
[ a [ href appUrl, target "_blank" ] [ code [] [ text appUrl ] ]
|
||||
]
|
||||
]
|
||||
, dl [ class "ui list" ]
|
||||
[ dt [] [ text "Public API Upload URL" ]
|
||||
, dd []
|
||||
[ p []
|
||||
[ code []
|
||||
[ text apiUrl
|
||||
]
|
||||
]
|
||||
, p [ class "qr-code" ]
|
||||
[ qrCodeView apiUrl
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
|
@ -8,10 +8,10 @@ module Comp.SourceManage exposing
|
||||
|
||||
import Api
|
||||
import Api.Model.BasicResult exposing (BasicResult)
|
||||
import Api.Model.Source
|
||||
import Api.Model.Source exposing (Source)
|
||||
import Api.Model.SourceList exposing (SourceList)
|
||||
import Comp.SourceForm
|
||||
import Comp.SourceTable
|
||||
import Comp.SourceTable exposing (SelectMode(..))
|
||||
import Comp.YesNoDimmer
|
||||
import Data.Flags exposing (Flags)
|
||||
import Data.UiSettings exposing (UiSettings)
|
||||
@ -19,53 +19,64 @@ import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick, onSubmit)
|
||||
import Http
|
||||
import Ports
|
||||
import QRCode
|
||||
import Util.Http
|
||||
import Util.Maybe
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ tableModel : Comp.SourceTable.Model
|
||||
, formModel : Comp.SourceForm.Model
|
||||
, viewMode : ViewMode
|
||||
{ formModel : Comp.SourceForm.Model
|
||||
, viewMode : SelectMode
|
||||
, formError : Maybe String
|
||||
, loading : Bool
|
||||
, deleteConfirm : Comp.YesNoDimmer.Model
|
||||
, sources : List Source
|
||||
}
|
||||
|
||||
|
||||
type ViewMode
|
||||
= Table
|
||||
| Form
|
||||
|
||||
|
||||
init : Flags -> ( Model, Cmd Msg )
|
||||
init flags =
|
||||
let
|
||||
( fm, fc ) =
|
||||
Comp.SourceForm.init flags
|
||||
in
|
||||
( { tableModel = Comp.SourceTable.emptyModel
|
||||
, formModel = fm
|
||||
, viewMode = Table
|
||||
( { formModel = fm
|
||||
, viewMode = None
|
||||
, formError = Nothing
|
||||
, loading = False
|
||||
, deleteConfirm = Comp.YesNoDimmer.emptyModel
|
||||
, sources = []
|
||||
}
|
||||
, Cmd.map FormMsg fc
|
||||
, Cmd.batch
|
||||
[ Cmd.map FormMsg fc
|
||||
, Ports.initClipboard appClipboardData
|
||||
, Ports.initClipboard apiClipboardData
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
appClipboardData : ( String, String )
|
||||
appClipboardData =
|
||||
( "app-url", "#app-url-copy-to-clipboard-btn" )
|
||||
|
||||
|
||||
apiClipboardData : ( String, String )
|
||||
apiClipboardData =
|
||||
( "api-url", "#api-url-copy-to-clipboard-btn" )
|
||||
|
||||
|
||||
type Msg
|
||||
= TableMsg Comp.SourceTable.Msg
|
||||
| FormMsg Comp.SourceForm.Msg
|
||||
| LoadSources
|
||||
| SourceResp (Result Http.Error SourceList)
|
||||
| SetViewMode ViewMode
|
||||
| InitNewSource
|
||||
| Submit
|
||||
| SubmitResp (Result Http.Error BasicResult)
|
||||
| YesNoMsg Comp.YesNoDimmer.Msg
|
||||
| RequestDelete
|
||||
| SetTableView
|
||||
|
||||
|
||||
|
||||
@ -77,29 +88,31 @@ update flags msg model =
|
||||
case msg of
|
||||
TableMsg m ->
|
||||
let
|
||||
( tm, tc ) =
|
||||
Comp.SourceTable.update flags m model.tableModel
|
||||
( tc, sel ) =
|
||||
Comp.SourceTable.update flags m
|
||||
|
||||
( m2, c2 ) =
|
||||
( { model
|
||||
| tableModel = tm
|
||||
, viewMode = Maybe.map (\_ -> Form) tm.selected |> Maybe.withDefault Table
|
||||
| viewMode = sel
|
||||
, formError =
|
||||
if Util.Maybe.nonEmpty tm.selected then
|
||||
Nothing
|
||||
if Comp.SourceTable.isEdit sel then
|
||||
model.formError
|
||||
|
||||
else
|
||||
model.formError
|
||||
Nothing
|
||||
}
|
||||
, Cmd.map TableMsg tc
|
||||
)
|
||||
|
||||
( m3, c3 ) =
|
||||
case tm.selected of
|
||||
Just source ->
|
||||
case sel of
|
||||
Edit source ->
|
||||
update flags (FormMsg (Comp.SourceForm.SetSource source)) m2
|
||||
|
||||
Nothing ->
|
||||
Display _ ->
|
||||
( m2, Cmd.none )
|
||||
|
||||
None ->
|
||||
( m2, Cmd.none )
|
||||
in
|
||||
( m3, Cmd.batch [ c2, c3 ] )
|
||||
@ -115,34 +128,27 @@ update flags msg model =
|
||||
( { model | loading = True }, Api.getSources flags SourceResp )
|
||||
|
||||
SourceResp (Ok sources) ->
|
||||
let
|
||||
m2 =
|
||||
{ model | viewMode = Table, loading = False }
|
||||
in
|
||||
update flags (TableMsg (Comp.SourceTable.SetSources sources.items)) m2
|
||||
( { model
|
||||
| viewMode = None
|
||||
, loading = False
|
||||
, sources = sources.items
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
SourceResp (Err _) ->
|
||||
( { model | loading = False }, Cmd.none )
|
||||
|
||||
SetViewMode m ->
|
||||
let
|
||||
m2 =
|
||||
{ model | viewMode = m }
|
||||
in
|
||||
case m of
|
||||
Table ->
|
||||
update flags (TableMsg Comp.SourceTable.Deselect) m2
|
||||
|
||||
Form ->
|
||||
( m2, Cmd.none )
|
||||
SetTableView ->
|
||||
( { model | viewMode = None }, Cmd.none )
|
||||
|
||||
InitNewSource ->
|
||||
let
|
||||
nm =
|
||||
{ model | viewMode = Form, formError = Nothing }
|
||||
|
||||
source =
|
||||
Api.Model.Source.empty
|
||||
|
||||
nm =
|
||||
{ model | viewMode = Edit source, formError = Nothing }
|
||||
in
|
||||
update flags (FormMsg (Comp.SourceForm.SetSource source)) nm
|
||||
|
||||
@ -164,7 +170,7 @@ update flags msg model =
|
||||
if res.success then
|
||||
let
|
||||
( m2, c2 ) =
|
||||
update flags (SetViewMode Table) model
|
||||
update flags SetTableView model
|
||||
|
||||
( m3, c3 ) =
|
||||
update flags LoadSources m2
|
||||
@ -202,13 +208,25 @@ update flags msg model =
|
||||
--- View
|
||||
|
||||
|
||||
qrCodeView : String -> Html msg
|
||||
qrCodeView message =
|
||||
QRCode.encode message
|
||||
|> Result.map QRCode.toSvg
|
||||
|> Result.withDefault
|
||||
(Html.text "Error generating QR-Code")
|
||||
|
||||
|
||||
view : Flags -> UiSettings -> Model -> Html Msg
|
||||
view flags settings model =
|
||||
if model.viewMode == Table then
|
||||
viewTable model
|
||||
case model.viewMode of
|
||||
None ->
|
||||
viewTable model
|
||||
|
||||
else
|
||||
div [] (viewForm flags settings model)
|
||||
Edit _ ->
|
||||
div [] (viewForm flags settings model)
|
||||
|
||||
Display source ->
|
||||
viewLinks flags settings source
|
||||
|
||||
|
||||
viewTable : Model -> Html Msg
|
||||
@ -218,7 +236,7 @@ viewTable model =
|
||||
[ i [ class "plus icon" ] []
|
||||
, text "Create new"
|
||||
]
|
||||
, Html.map TableMsg (Comp.SourceTable.view model.tableModel)
|
||||
, Html.map TableMsg (Comp.SourceTable.view model.sources)
|
||||
, div
|
||||
[ classList
|
||||
[ ( "ui dimmer", True )
|
||||
@ -230,6 +248,112 @@ viewTable model =
|
||||
]
|
||||
|
||||
|
||||
viewLinks : Flags -> UiSettings -> Source -> Html Msg
|
||||
viewLinks flags _ source =
|
||||
let
|
||||
appUrl =
|
||||
flags.config.baseUrl ++ "/app/upload/" ++ source.id
|
||||
|
||||
apiUrl =
|
||||
flags.config.baseUrl ++ "/api/v1/open/upload/item/" ++ source.id
|
||||
in
|
||||
div
|
||||
[]
|
||||
[ h3 [ class "ui dividing header" ]
|
||||
[ text "Public Uploads: "
|
||||
, text source.abbrev
|
||||
, div [ class "sub header" ]
|
||||
[ text source.id
|
||||
]
|
||||
]
|
||||
, p []
|
||||
[ text "This source defines URLs that can be used by anyone to send files to "
|
||||
, text "you. There is a web page that you can share or the API url can be used "
|
||||
, text "with other clients."
|
||||
]
|
||||
, p []
|
||||
[ text "There have been "
|
||||
, String.fromInt source.counter |> text
|
||||
, text " items created through this source."
|
||||
]
|
||||
, h4 [ class "ui header" ]
|
||||
[ text "Public Upload Page"
|
||||
]
|
||||
, div [ class "ui attached message" ]
|
||||
[ div [ class "ui fluid left action input" ]
|
||||
[ a
|
||||
[ class "ui left icon button"
|
||||
, title "Copy to clipboard"
|
||||
, href "#"
|
||||
, Tuple.second appClipboardData
|
||||
|> String.dropLeft 1
|
||||
|> id
|
||||
, attribute "data-clipboard-target" "#app-url"
|
||||
]
|
||||
[ i [ class "copy icon" ] []
|
||||
]
|
||||
, a
|
||||
[ class "ui icon button"
|
||||
, href appUrl
|
||||
, target "_blank"
|
||||
, title "Open in new tab/window"
|
||||
]
|
||||
[ i [ class "link external icon" ] []
|
||||
]
|
||||
, input
|
||||
[ type_ "text"
|
||||
, id "app-url"
|
||||
, value appUrl
|
||||
, readonly True
|
||||
]
|
||||
[]
|
||||
]
|
||||
]
|
||||
, div [ class "ui attached segment" ]
|
||||
[ div [ class "qr-code" ]
|
||||
[ qrCodeView appUrl
|
||||
]
|
||||
]
|
||||
, h4 [ class "ui header" ]
|
||||
[ text "Public API Upload URL"
|
||||
]
|
||||
, div [ class "ui attached message" ]
|
||||
[ div [ class "ui fluid left action input" ]
|
||||
[ a
|
||||
[ class "ui left icon button"
|
||||
, title "Copy to clipboard"
|
||||
, href "#"
|
||||
, Tuple.second apiClipboardData
|
||||
|> String.dropLeft 1
|
||||
|> id
|
||||
, attribute "data-clipboard-target" "#api-url"
|
||||
]
|
||||
[ i [ class "copy icon" ] []
|
||||
]
|
||||
, input
|
||||
[ type_ "text"
|
||||
, value apiUrl
|
||||
, readonly True
|
||||
, id "api-url"
|
||||
]
|
||||
[]
|
||||
]
|
||||
]
|
||||
, div [ class "ui attached segment" ]
|
||||
[ div [ class "qr-code" ]
|
||||
[ qrCodeView apiUrl
|
||||
]
|
||||
]
|
||||
, div [ class "ui divider" ] []
|
||||
, button
|
||||
[ class "ui button"
|
||||
, onClick SetTableView
|
||||
]
|
||||
[ text "Back"
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
viewForm : Flags -> UiSettings -> Model -> List (Html Msg)
|
||||
viewForm flags settings model =
|
||||
let
|
||||
@ -264,7 +388,7 @@ viewForm flags settings model =
|
||||
, button [ class "ui primary button", type_ "submit" ]
|
||||
[ text "Submit"
|
||||
]
|
||||
, a [ class "ui secondary button", onClick (SetViewMode Table), href "" ]
|
||||
, a [ class "ui secondary button", onClick SetTableView, href "" ]
|
||||
[ text "Cancel"
|
||||
]
|
||||
, if not newSource then
|
||||
|
@ -1,7 +1,7 @@
|
||||
module Comp.SourceTable exposing
|
||||
( Model
|
||||
, Msg(..)
|
||||
, emptyModel
|
||||
( Msg
|
||||
, SelectMode(..)
|
||||
, isEdit
|
||||
, update
|
||||
, view
|
||||
)
|
||||
@ -14,44 +14,47 @@ import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ sources : List Source
|
||||
, selected : Maybe Source
|
||||
}
|
||||
type SelectMode
|
||||
= Edit Source
|
||||
| Display Source
|
||||
| None
|
||||
|
||||
|
||||
emptyModel : Model
|
||||
emptyModel =
|
||||
{ sources = []
|
||||
, selected = Nothing
|
||||
}
|
||||
isEdit : SelectMode -> Bool
|
||||
isEdit m =
|
||||
case m of
|
||||
Edit _ ->
|
||||
True
|
||||
|
||||
Display _ ->
|
||||
False
|
||||
|
||||
None ->
|
||||
False
|
||||
|
||||
|
||||
type Msg
|
||||
= SetSources (List Source)
|
||||
| Select Source
|
||||
| Deselect
|
||||
= Select Source
|
||||
| Show Source
|
||||
|
||||
|
||||
update : Flags -> Msg -> Model -> ( Model, Cmd Msg )
|
||||
update _ msg model =
|
||||
update : Flags -> Msg -> ( Cmd Msg, SelectMode )
|
||||
update _ msg =
|
||||
case msg of
|
||||
SetSources list ->
|
||||
( { model | sources = list, selected = Nothing }, Cmd.none )
|
||||
|
||||
Select source ->
|
||||
( { model | selected = Just source }, Cmd.none )
|
||||
( Cmd.none, Edit source )
|
||||
|
||||
Deselect ->
|
||||
( { model | selected = Nothing }, Cmd.none )
|
||||
Show source ->
|
||||
( Cmd.none, Display source )
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
table [ class "ui selectable table" ]
|
||||
view : List Source -> Html Msg
|
||||
view sources =
|
||||
table [ class "ui table" ]
|
||||
[ thead []
|
||||
[ tr []
|
||||
[ th [ class "collapsing" ] [ text "Abbrev" ]
|
||||
[ th [ class "collapsing" ] []
|
||||
, th [ class "collapsing" ] [ text "Abbrev" ]
|
||||
, th [ class "collapsing" ] [ text "Enabled" ]
|
||||
, th [ class "collapsing" ] [ text "Counter" ]
|
||||
, th [ class "collapsing" ] [ text "Priority" ]
|
||||
@ -59,17 +62,37 @@ view model =
|
||||
]
|
||||
]
|
||||
, tbody []
|
||||
(List.map (renderSourceLine model) model.sources)
|
||||
(List.map renderSourceLine sources)
|
||||
]
|
||||
|
||||
|
||||
renderSourceLine : Model -> Source -> Html Msg
|
||||
renderSourceLine model source =
|
||||
renderSourceLine : Source -> Html Msg
|
||||
renderSourceLine source =
|
||||
tr
|
||||
[ classList [ ( "active", model.selected == Just source ) ]
|
||||
, onClick (Select source)
|
||||
]
|
||||
[]
|
||||
[ td [ class "collapsing" ]
|
||||
[ a
|
||||
[ class "ui basic tiny primary button"
|
||||
, href "#"
|
||||
, onClick (Select source)
|
||||
]
|
||||
[ i [ class "edit icon" ] []
|
||||
, text "Edit"
|
||||
]
|
||||
, a
|
||||
[ classList
|
||||
[ ( "ui basic tiny primary button", True )
|
||||
, ( "disabled", not source.enabled )
|
||||
]
|
||||
, href "#"
|
||||
, disabled (not source.enabled)
|
||||
, onClick (Show source)
|
||||
]
|
||||
[ i [ class "eye icon" ] []
|
||||
, text "Show"
|
||||
]
|
||||
]
|
||||
, td [ class "collapsing" ]
|
||||
[ text source.abbrev
|
||||
]
|
||||
, td [ class "collapsing" ]
|
||||
|
@ -1,5 +1,6 @@
|
||||
port module Ports exposing
|
||||
( getUiSettings
|
||||
, initClipboard
|
||||
, loadUiSettings
|
||||
, onUiSettingsSaved
|
||||
, removeAccount
|
||||
@ -78,3 +79,6 @@ getUiSettings flags =
|
||||
|
||||
Nothing ->
|
||||
Cmd.none
|
||||
|
||||
|
||||
port initClipboard : ( String, String ) -> Cmd msg
|
||||
|
@ -105,10 +105,12 @@
|
||||
}
|
||||
|
||||
.default-layout .qr-code svg {
|
||||
border: 1px solid #ccc;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
.default-layout .qr-code.bordered svg {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.markdown-preview {
|
||||
overflow: auto;
|
||||
|
@ -83,3 +83,13 @@ elmApp.ports.requestUiSettings.subscribe(function(args) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var docspell_clipboards = {};
|
||||
|
||||
elmApp.ports.initClipboard.subscribe(function(args) {
|
||||
var page = args[0];
|
||||
if (!docspell_clipboards[page]) {
|
||||
var sel = args[1];
|
||||
docspell_clipboards[page] = new ClipboardJS(sel);
|
||||
}
|
||||
});
|
||||
|
@ -9,6 +9,7 @@ object Dependencies {
|
||||
val BitpeaceVersion = "0.5.0"
|
||||
val CalevVersion = "0.3.1"
|
||||
val CirceVersion = "0.13.0"
|
||||
val ClipboardJsVersion = "2.0.6"
|
||||
val DoobieVersion = "0.9.0"
|
||||
val EmilVersion = "0.6.2"
|
||||
val FastparseVersion = "2.1.3"
|
||||
@ -245,10 +246,11 @@ object Dependencies {
|
||||
val betterMonadicFor = "com.olegpy" %% "better-monadic-for" % BetterMonadicForVersion
|
||||
|
||||
val webjars = Seq(
|
||||
"org.webjars" % "swagger-ui" % SwaggerUIVersion,
|
||||
"org.webjars" % "Semantic-UI" % SemanticUIVersion,
|
||||
"org.webjars" % "jquery" % JQueryVersion,
|
||||
"org.webjars" % "viewerjs" % ViewerJSVersion
|
||||
"org.webjars" % "swagger-ui" % SwaggerUIVersion,
|
||||
"org.webjars" % "Semantic-UI" % SemanticUIVersion,
|
||||
"org.webjars" % "jquery" % JQueryVersion,
|
||||
"org.webjars" % "viewerjs" % ViewerJSVersion,
|
||||
"org.webjars" % "clipboard.js" % ClipboardJsVersion
|
||||
)
|
||||
|
||||
val icu4j = Seq(
|
||||
|
Loading…
Reference in New Issue
Block a user