Download multiple files as zip

This commit is contained in:
eikek
2022-04-09 14:01:36 +02:00
parent e65b8de686
commit 4488291319
55 changed files with 2328 additions and 38 deletions

View File

@ -60,6 +60,9 @@ module Api exposing
, deleteTag
, deleteUser
, disableOtp
, downloadAllLink
, downloadAllPrefetch
, downloadAllSubmit
, fileURL
, getAllDashboards
, getAttachmentMeta
@ -175,6 +178,9 @@ module Api exposing
, setTagsMultiple
, setUnconfirmed
, shareAttachmentPreviewURL
, shareDownloadAllLink
, shareDownloadAllPrefetch
, shareDownloadAllSubmit
, shareFileURL
, shareItemBasePreviewURL
, shareSendMail
@ -218,6 +224,8 @@ import Api.Model.CustomFieldList exposing (CustomFieldList)
import Api.Model.CustomFieldValue exposing (CustomFieldValue)
import Api.Model.DeleteUserData exposing (DeleteUserData)
import Api.Model.DirectionValue exposing (DirectionValue)
import Api.Model.DownloadAllRequest exposing (DownloadAllRequest)
import Api.Model.DownloadAllSummary exposing (DownloadAllSummary)
import Api.Model.EmailSettings exposing (EmailSettings)
import Api.Model.EmailSettingsList exposing (EmailSettingsList)
import Api.Model.EmptyTrashSetting exposing (EmptyTrashSetting)
@ -3084,6 +3092,70 @@ removeRelatedItem flags item1 item2 receive =
--- DownloadAll
downloadAllPrefetch : Flags -> DownloadAllRequest -> (Result Http.Error DownloadAllSummary -> msg) -> Cmd msg
downloadAllPrefetch flags req receive =
Http2.authPost
{ url = flags.config.baseUrl ++ "/api/v1/sec/downloadAll/prefetch"
, account = getAccount flags
, body = Http.jsonBody (Api.Model.DownloadAllRequest.encode req)
, expect = Http.expectJson receive Api.Model.DownloadAllSummary.decoder
}
downloadAllSubmit : Flags -> DownloadAllRequest -> (Result Http.Error DownloadAllSummary -> msg) -> Cmd msg
downloadAllSubmit flags req receive =
Http2.authPost
{ url = flags.config.baseUrl ++ "/api/v1/sec/downloadAll/submit"
, account = getAccount flags
, body = Http.jsonBody (Api.Model.DownloadAllRequest.encode req)
, expect = Http.expectJson receive Api.Model.DownloadAllSummary.decoder
}
downloadAllLink : Flags -> String -> String
downloadAllLink flags id =
flags.config.baseUrl ++ "/api/v1/sec/downloadAll/file/" ++ id
shareDownloadAllPrefetch :
Flags
-> String
-> DownloadAllRequest
-> (Result Http.Error DownloadAllSummary -> msg)
-> Cmd msg
shareDownloadAllPrefetch flags token req receive =
Http2.sharePost
{ url = flags.config.baseUrl ++ "/api/v1/share/downloadAll/prefetch"
, token = token
, body = Http.jsonBody (Api.Model.DownloadAllRequest.encode req)
, expect = Http.expectJson receive Api.Model.DownloadAllSummary.decoder
}
shareDownloadAllSubmit :
Flags
-> String
-> DownloadAllRequest
-> (Result Http.Error DownloadAllSummary -> msg)
-> Cmd msg
shareDownloadAllSubmit flags token req receive =
Http2.sharePost
{ url = flags.config.baseUrl ++ "/api/v1/share/downloadAll/submit"
, token = token
, body = Http.jsonBody (Api.Model.DownloadAllRequest.encode req)
, expect = Http.expectJson receive Api.Model.DownloadAllSummary.decoder
}
shareDownloadAllLink : Flags -> String -> String
shareDownloadAllLink flags id =
flags.config.baseUrl ++ "/api/v1/share/downloadAll/file/" ++ id
--- Helper

View File

@ -14,6 +14,7 @@ import Api
import App.Data exposing (..)
import Browser exposing (UrlRequest(..))
import Browser.Navigation as Nav
import Comp.DownloadAll
import Data.AppEvent exposing (AppEvent(..))
import Data.Environment as Env
import Data.Flags
@ -317,6 +318,9 @@ updateWithSub msg model =
isProcessItem =
task == "process-item"
isDownloadZip =
task == "download-query-zip"
newModel =
{ model
| showNewItemsArrived = isProcessItem && not (Page.isSearchPage model.page)
@ -326,6 +330,9 @@ updateWithSub msg model =
if Page.isSearchPage model.page && isProcessItem then
updateSearch texts Page.Search.Data.RefreshView newModel
else if Page.isSearchPage model.page && isDownloadZip then
updateSearch texts (Page.Search.Data.DownloadAllMsg Comp.DownloadAll.checkDownload) newModel
else if Page.isDashboardPage model.page && isProcessItem then
updateDashboard texts Page.Dashboard.Data.reloadDashboardData newModel

View File

@ -0,0 +1,349 @@
{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Comp.DownloadAll exposing (AccessMode(..), Model, Msg, UpdateResult, checkDownload, init, isPreparing, update, view)
import Api
import Api.Model.DownloadAllRequest exposing (DownloadAllRequest)
import Api.Model.DownloadAllSummary exposing (DownloadAllSummary)
import Comp.Basic as B
import Comp.FixedDropdown
import Data.DownloadAllState
import Data.DownloadFileType exposing (DownloadFileType)
import Data.DropdownStyle as DS
import Data.Flags exposing (Flags)
import Html exposing (Html, a, div, i, label, text)
import Html.Attributes exposing (class, classList, disabled, href)
import Html.Events exposing (onClick)
import Http
import Messages.Comp.DownloadAll exposing (Texts)
import Styles as S
import Util.Size
type alias Model =
{ summary : DownloadAllSummary
, query : String
, dlType : DownloadFileType
, dlTypeDropdown : Comp.FixedDropdown.Model DownloadFileType
, loading : Bool
, formError : FormError
, accessMode : AccessMode
}
type AccessMode
= AccessShare String
| AccessUser
type FormError
= FormNone
| FormHttpError Http.Error
init : AccessMode -> Flags -> String -> ( Model, Cmd Msg )
init am flags query =
let
model =
{ summary = Api.Model.DownloadAllSummary.empty
, query = query
, dlType = Data.DownloadFileType.Converted
, dlTypeDropdown = Comp.FixedDropdown.init Data.DownloadFileType.all
, formError = FormNone
, accessMode = am
, loading = False
}
in
( model
, prefetch flags model
)
type Msg
= DownloadSummaryResp (Result Http.Error DownloadAllSummary)
| DlTypeMsg (Comp.FixedDropdown.Msg DownloadFileType)
| CloseAction
| SubmitAction
| CheckAction
checkDownload : Msg
checkDownload =
CheckAction
isPreparing : Model -> Bool
isPreparing model =
Data.DownloadAllState.fromString model.summary.state == Just Data.DownloadAllState.Preparing
makeRequest : Model -> DownloadAllRequest
makeRequest model =
{ query = model.query
, fileType = Data.DownloadFileType.asString model.dlType
}
--- Update
type alias UpdateResult =
{ model : Model
, cmd : Cmd Msg
, closed : Bool
}
update : Flags -> Msg -> Model -> UpdateResult
update flags msg model =
case msg of
DownloadSummaryResp (Ok summary) ->
unit { model | summary = summary, formError = FormNone, loading = False }
DownloadSummaryResp (Err err) ->
unit { model | formError = FormHttpError err, loading = False }
DlTypeMsg lm ->
let
( dlm, sel ) =
Comp.FixedDropdown.update lm model.dlTypeDropdown
nextDlType =
Maybe.withDefault model.dlType sel
nextModel =
{ model
| dlTypeDropdown = dlm
, dlType = nextDlType
, formError = FormNone
}
in
if nextDlType /= model.dlType && sel /= Nothing then
unitCmd
( { nextModel | loading = True }
, prefetch flags nextModel
)
else
unit { model | dlTypeDropdown = dlm }
CloseAction ->
UpdateResult model Cmd.none True
SubmitAction ->
unitCmd
( model
, submit flags model
)
CheckAction ->
unitCmd
( model
, prefetch flags model
)
unit : Model -> UpdateResult
unit model =
UpdateResult model Cmd.none False
unitCmd : ( Model, Cmd Msg ) -> UpdateResult
unitCmd ( m, c ) =
UpdateResult m c False
prefetch : Flags -> Model -> Cmd Msg
prefetch flags model =
case model.accessMode of
AccessUser ->
Api.downloadAllPrefetch flags (makeRequest model) DownloadSummaryResp
AccessShare shareId ->
Api.shareDownloadAllPrefetch flags shareId (makeRequest model) DownloadSummaryResp
submit : Flags -> Model -> Cmd Msg
submit flags model =
case model.accessMode of
AccessUser ->
Api.downloadAllSubmit flags (makeRequest model) DownloadSummaryResp
AccessShare shareId ->
Api.shareDownloadAllSubmit flags shareId (makeRequest model) DownloadSummaryResp
downloadLink : Flags -> Model -> String
downloadLink flags model =
case model.accessMode of
AccessUser ->
Api.downloadAllLink flags model.summary.id
AccessShare _ ->
Api.shareDownloadAllLink flags model.summary.id
--- View
view : Flags -> Texts -> Model -> Html Msg
view flags texts model =
let
dlTypeSettings =
{ display = texts.downloadFileType
, icon = \_ -> Nothing
, selectPlaceholder = ""
, style = DS.mainStyle
}
byteStr n =
Util.Size.bytesReadable Util.Size.B (toFloat n)
in
case Data.DownloadAllState.fromString model.summary.state of
Nothing ->
div [ class "flex flex-col animate-pulse space-y-4 px-2 my-2" ]
[ div [ class "h-2 border dark:border-slate-600 bg-gray-100 dark:bg-slate-600" ]
[]
, div [ class "h-2 border dark:border-slate-600 bg-gray-100 dark:bg-slate-600" ]
[]
, div [ class "h-8 border dark:border-slate-600 bg-gray-100 dark:bg-slate-600" ]
[]
, div [ class "flex flex-row space-x-4 " ]
[ div [ class "h-10 w-32 dark:border-slate-600 bg-gray-100 dark:bg-slate-600" ]
[]
, div [ class "h-10 w-32 dark:border-slate-600 bg-gray-100 dark:bg-slate-600" ]
[]
]
]
Just Data.DownloadAllState.Empty ->
div
[ class "flex flex-col relative px-2"
]
[ div
[ class S.infoMessage
]
[ text texts.noResults
]
, div [ class "flex flex-row py-2" ]
[ a
[ class S.secondaryButton
, href "#"
, onClick CloseAction
]
[ i [ class "fa fa-times mr-2" ] []
, text texts.close
]
]
]
Just state ->
div [ class "flex flex-col relative px-2" ]
[ B.loadingDimmer
{ active = state == Data.DownloadAllState.Preparing
, label = texts.downloadPreparing
}
, div
[ classList [ ( "hidden", state == Data.DownloadAllState.Forbidden ) ]
]
[ text
(texts.summary
model.summary.fileCount
(byteStr model.summary.uncompressedSize)
)
]
, div
[ classList [ ( "hidden", state /= Data.DownloadAllState.Forbidden ) ]
, class S.errorMessage
]
[ text texts.downloadTooLarge
, text " "
, text <|
texts.downloadConfigText
flags.config.downloadAllMaxFiles
flags.config.downloadAllMaxSize
model.summary.uncompressedSize
]
, div
[ class "mt-3"
, classList [ ( "hidden", model.accessMode /= AccessUser ) ]
]
[ label [ class S.inputLabel ]
[ text texts.downloadFileTypeLabel
]
, Html.map DlTypeMsg
(Comp.FixedDropdown.viewStyled2
dlTypeSettings
False
(Just model.dlType)
model.dlTypeDropdown
)
]
, div
[ class "my-2"
, classList [ ( "hidden", state /= Data.DownloadAllState.Present ) ]
]
[ text texts.downloadReady
]
, div
[ class "my-2 "
, classList [ ( "hidden", state /= Data.DownloadAllState.NotPresent ) ]
]
[ text texts.downloadCreateText
]
, div [ class "flex flex-row py-2 items-center" ]
[ a
[ class S.primaryButton
, disabled (state /= Data.DownloadAllState.NotPresent && state /= Data.DownloadAllState.Present)
, classList [ ( "disabled", state /= Data.DownloadAllState.NotPresent && state /= Data.DownloadAllState.Present ) ]
, if state == Data.DownloadAllState.Present then
href (downloadLink flags model)
else
href "#"
, if state == Data.DownloadAllState.NotPresent then
onClick SubmitAction
else
class ""
]
[ case state of
Data.DownloadAllState.Present ->
text texts.downloadNow
Data.DownloadAllState.NotPresent ->
text texts.downloadCreate
Data.DownloadAllState.Preparing ->
text texts.downloadPreparing
Data.DownloadAllState.Forbidden ->
text "N./A."
Data.DownloadAllState.Empty ->
text "N./A."
]
, a
[ class S.secondaryButton
, class "ml-2"
, href "#"
, onClick CloseAction
]
[ i [ class "fa fa-times mr-2" ] []
, text texts.close
]
, div
[ class "h-full ml-3"
, classList [ ( "hidden", not model.loading ) ]
]
[ i [ class "fa fa-circle-notch animate-spin" ] []
]
]
]

View File

@ -0,0 +1,49 @@
{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Data.DownloadAllState exposing (DownloadAllState(..), all, asString, fromString)
type DownloadAllState
= NotPresent
| Forbidden
| Empty
| Preparing
| Present
all : List DownloadAllState
all =
[ NotPresent, Forbidden, Empty, Preparing, Present ]
asString : DownloadAllState -> String
asString st =
case st of
NotPresent ->
"notpresent"
Forbidden ->
"forbidden"
Empty ->
"empty"
Preparing ->
"preparing"
Present ->
"present"
fromString : String -> Maybe DownloadAllState
fromString str =
let
name =
String.toLower str
in
List.filter (\e -> asString e == name) all |> List.head

View File

@ -0,0 +1,41 @@
{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Data.DownloadFileType exposing (DownloadFileType(..), all, asString, fromString)
type DownloadFileType
= Converted
| Originals
all : List DownloadFileType
all =
[ Converted, Originals ]
asString : DownloadFileType -> String
asString ft =
case ft of
Converted ->
"converted"
Originals ->
"original"
fromString : String -> Maybe DownloadFileType
fromString str =
case String.toLower str of
"converted" ->
Just Converted
"originals" ->
Just Originals
_ ->
Nothing

View File

@ -35,6 +35,8 @@ type alias Config =
, maxPageSize : Int
, maxNoteLength : Int
, showClassificationSettings : Bool
, downloadAllMaxFiles : Int
, downloadAllMaxSize : Int
, openIdAuth : List OpenIdAuth
}

View File

@ -0,0 +1,113 @@
{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Messages.Comp.DownloadAll exposing (Texts, de, fr, gb)
import Messages.Data.DownloadFileType
import Util.Size
type alias Texts =
{ downloadFileType : Messages.Data.DownloadFileType.Texts
, downloadFileTypeLabel : String
, noResults : String
, summary : Int -> String -> String
, close : String
, downloadPreparing : String
, downloadTooLarge : String
, downloadConfigText : Int -> Int -> Int -> String
, downloadReady : String
, downloadCreateText : String
, downloadCreate : String
, downloadNow : String
}
byteStr : Int -> String
byteStr n =
Util.Size.bytesReadable Util.Size.B (toFloat n)
gb : Texts
gb =
{ downloadFileType = Messages.Data.DownloadFileType.gb
, downloadFileTypeLabel = "What files"
, noResults = "No results to download."
, summary = \files -> \size -> "Download consists of " ++ String.fromInt files ++ " files (" ++ size ++ ")."
, close = "Close"
, downloadPreparing = "Download is being prepared"
, downloadTooLarge = "The download is too large."
, downloadConfigText =
\maxNum ->
\maxSize ->
\curSize ->
"The maximum number of files allowed is "
++ String.fromInt maxNum
++ " and maximum size is "
++ byteStr maxSize
++ " (current size would be "
++ byteStr curSize
++ "). "
, downloadReady = "Donwload is ready!"
, downloadCreateText = "You can create the download at the server. Once it is ready, the button will download the zip file."
, downloadCreate = "Create download"
, downloadNow = "Download now!"
}
de : Texts
de =
{ downloadFileType = Messages.Data.DownloadFileType.de
, downloadFileTypeLabel = "Welche Dateien"
, noResults = "Keine Ergebnisse zum Herunterladen."
, summary = \files -> \size -> "Download besteht aus " ++ String.fromInt files ++ " Dateien (" ++ size ++ ")."
, close = "Schließen"
, downloadPreparing = "Der Download wird erstellt"
, downloadTooLarge = "Der Download ist zu groß."
, downloadConfigText =
\maxNum ->
\maxSize ->
\curSize ->
"Es können maximal "
++ String.fromInt maxNum
++ " Dateien mit einer Gesamtgröße von "
++ byteStr maxSize
++ " erstellt werden (aktuelle Größe wäre "
++ byteStr curSize
++ "). "
, downloadReady = "Donwload ist fertig!"
, downloadCreateText = "Der Download kann auf dem Server erzeugt werden. Sobald die ZIP Datei fertig ist, kann sie hier heruntergeladen werden."
, downloadCreate = "Download erstellen"
, downloadNow = "Jetzt herunterladen"
}
fr : Texts
fr =
{ downloadFileType = Messages.Data.DownloadFileType.fr
, downloadFileTypeLabel = "Quels fichiers"
, noResults = "No results to download"
, summary = \files -> \size -> "Download consists of " ++ String.fromInt files ++ " files (" ++ size ++ ")."
, close = "Fermer"
, downloadPreparing = "Le téléchargement est créé"
, downloadTooLarge = "Le téléchargement est trop important."
, downloadConfigText =
\maxNum ->
\maxSize ->
\curSize ->
"Il est possible de créer au maximum "
++ String.fromInt maxNum
++ " fichiers d'une taille totale de "
++ byteStr maxSize
++ " (la taille actuelle serait de "
++ byteStr curSize
++ "). "
, downloadReady = "Le téléchargement est achevé."
, downloadCreateText = "Vous pouvez créer le téléchargement sur le serveur. Une fois qu'il est prêt, le bouton téléchargera le fichier zip."
, downloadCreate = "Créer Télécharger"
, downloadNow = "Télécharger l'archive!"
}

View File

@ -0,0 +1,44 @@
{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Messages.Data.DownloadFileType exposing (Texts, de, fr, gb)
import Data.DownloadFileType exposing (DownloadFileType(..))
type alias Texts =
DownloadFileType -> String
gb : Texts
gb ft =
case ft of
Converted ->
"Converted PDF files"
Originals ->
"Original files"
de : Texts
de ft =
case ft of
Converted ->
"Konvertierte PDF Dateien"
Originals ->
"Original Dateien"
fr : Texts
fr ft =
case ft of
Converted ->
"Fichiers PDF convertis"
Originals ->
"Fichiers originaux"

View File

@ -15,6 +15,7 @@ module Messages.Page.Search exposing
import Data.TimeZone exposing (TimeZone)
import Messages.Basics
import Messages.Comp.BookmarkQueryManage
import Messages.Comp.DownloadAll
import Messages.Comp.ItemCardList
import Messages.Comp.ItemMerge
import Messages.Comp.PublishItems
@ -30,6 +31,7 @@ type alias Texts =
, itemMerge : Messages.Comp.ItemMerge.Texts
, publishItems : Messages.Comp.PublishItems.Texts
, bookmarkManage : Messages.Comp.BookmarkQueryManage.Texts
, downloadAllComp : Messages.Comp.DownloadAll.Texts
, contentSearch : String
, searchInNames : String
, selectModeTitle : String
@ -76,6 +78,8 @@ type alias Texts =
, linkItemsSuccessful : String
, linkItemsInProcess : String
, linkItemsHeader : String
, downloadAll : String
, downloadAllQueryNeeded : String
}
@ -88,6 +92,7 @@ gb tz =
, itemMerge = Messages.Comp.ItemMerge.gb tz
, publishItems = Messages.Comp.PublishItems.gb tz
, bookmarkManage = Messages.Comp.BookmarkQueryManage.gb
, downloadAllComp = Messages.Comp.DownloadAll.gb
, contentSearch = "Content search"
, searchInNames = "Search in names"
, selectModeTitle = "Select Mode"
@ -134,6 +139,8 @@ gb tz =
, linkItemsInProcess = "Linking items ..."
, mergeHeader = "Merge Items"
, linkItemsHeader = "Link Items"
, downloadAll = "Download all"
, downloadAllQueryNeeded = "Apply a criteria to reduce what to download."
}
@ -146,6 +153,7 @@ de tz =
, itemMerge = Messages.Comp.ItemMerge.de tz
, publishItems = Messages.Comp.PublishItems.de tz
, bookmarkManage = Messages.Comp.BookmarkQueryManage.de
, downloadAllComp = Messages.Comp.DownloadAll.de
, contentSearch = "Volltextsuche"
, searchInNames = "Suche in Namen"
, selectModeTitle = "Auswahlmodus"
@ -192,6 +200,8 @@ de tz =
, linkItemsInProcess = "Dokumente werden verknüpft ..."
, mergeHeader = "Dokumente zusammenführen"
, linkItemsHeader = "Dokument verknüpfen"
, downloadAll = "Alle herunterladen"
, downloadAllQueryNeeded = "Alles kann nicht heruntergeladen werden, es muss etwas gesucht werden."
}
@ -204,6 +214,7 @@ fr tz =
, itemMerge = Messages.Comp.ItemMerge.fr tz
, publishItems = Messages.Comp.PublishItems.fr tz
, bookmarkManage = Messages.Comp.BookmarkQueryManage.fr
, downloadAllComp = Messages.Comp.DownloadAll.fr
, contentSearch = "Recherche..."
, searchInNames = "Recherche par nom..."
, selectModeTitle = "Select Mode"
@ -250,4 +261,6 @@ fr tz =
, linkItemsInProcess = "Relier en cours ..."
, mergeHeader = "Fusionner des documents"
, linkItemsHeader = "Lier des documents"
, downloadAll = "Télécharger tout"
, downloadAllQueryNeeded = "Tout ne peut pas être téléchargé, il faut chercher quelque chose."
}

View File

@ -10,6 +10,7 @@ module Messages.Page.Share exposing (..)
import Data.TimeZone exposing (TimeZone)
import Http
import Messages.Basics
import Messages.Comp.DownloadAll
import Messages.Comp.HttpError
import Messages.Comp.ItemCardList
import Messages.Comp.SearchMenu
@ -21,6 +22,7 @@ type alias Texts =
, basics : Messages.Basics.Texts
, itemCardList : Messages.Comp.ItemCardList.Texts
, passwordForm : Messages.Comp.SharePasswordForm.Texts
, downloadAll : Messages.Comp.DownloadAll.Texts
, httpError : Http.Error -> String
, authFailed : String
, fulltextPlaceholder : String
@ -30,6 +32,7 @@ type alias Texts =
, showItemGroups : String
, listView : String
, tileView : String
, downloadAllLabel : String
}
@ -39,6 +42,7 @@ gb tz =
, basics = Messages.Basics.gb
, itemCardList = Messages.Comp.ItemCardList.gb tz
, passwordForm = Messages.Comp.SharePasswordForm.gb
, downloadAll = Messages.Comp.DownloadAll.gb
, authFailed = "This share does not exist."
, httpError = Messages.Comp.HttpError.gb
, fulltextPlaceholder = "Fulltext search"
@ -48,6 +52,7 @@ gb tz =
, showItemGroups = "Group by month"
, listView = "List view"
, tileView = "Tile view"
, downloadAllLabel = "Download all"
}
@ -57,6 +62,7 @@ de tz =
, basics = Messages.Basics.de
, itemCardList = Messages.Comp.ItemCardList.de tz
, passwordForm = Messages.Comp.SharePasswordForm.de
, downloadAll = Messages.Comp.DownloadAll.de
, authFailed = "Diese Freigabe existiert nicht."
, httpError = Messages.Comp.HttpError.de
, fulltextPlaceholder = "Volltextsuche"
@ -66,6 +72,7 @@ de tz =
, showItemGroups = "nach Monat gruppieren"
, listView = "Listenansicht"
, tileView = "Kachelansicht"
, downloadAllLabel = "Alles herunterladen"
}
@ -75,6 +82,7 @@ fr tz =
, basics = Messages.Basics.fr
, itemCardList = Messages.Comp.ItemCardList.fr tz
, passwordForm = Messages.Comp.SharePasswordForm.fr
, downloadAll = Messages.Comp.DownloadAll.fr
, authFailed = "Ce partage n'existe pas."
, httpError = Messages.Comp.HttpError.fr
, fulltextPlaceholder = "Recherche en texte entier..."
@ -84,4 +92,5 @@ fr tz =
, showItemGroups = "Grouper par mois"
, listView = "Affichage liste"
, tileView = "Affichage tuile"
, downloadAllLabel = "Télécharger tout"
}

View File

@ -17,6 +17,7 @@ module Page exposing
, isOpen
, isSearchPage
, isSecured
, isSharePage
, loginPage
, loginPageReferrer
, pageFromString
@ -144,6 +145,16 @@ loginPage p =
LoginPage { emptyLoginData | referrer = Just p }
isSharePage : Page -> Bool
isSharePage page =
case page of
SharePage _ ->
True
_ ->
False
isSearchPage : Page -> Bool
isSearchPage page =
case page of

View File

@ -33,6 +33,7 @@ import Api.Model.ItemLightList exposing (ItemLightList)
import Api.Model.SearchStats exposing (SearchStats)
import Browser.Dom as Dom
import Comp.BookmarkQueryManage
import Comp.DownloadAll
import Comp.ItemCardList
import Comp.ItemDetail.FormChange exposing (FormChange)
import Comp.ItemDetail.MultiEditMenu exposing (SaveNameState(..))
@ -76,6 +77,7 @@ type alias Model =
type TopWidgetModel
= TopWidgetHidden
| DownloadAll Comp.DownloadAll.Model
| BookmarkQuery Comp.BookmarkQueryManage.Model
@ -239,7 +241,9 @@ type Msg
| ToggleArrange ItemArrange
| ToggleExpandCollapseRows
| ToggleBookmarkCurrentQueryView
| ToggleDownloadAllView
| BookmarkQueryMsg Comp.BookmarkQueryManage.Msg
| DownloadAllMsg Comp.DownloadAll.Msg
| ItemSelectionChanged

View File

@ -13,6 +13,7 @@ module Page.Search.Update exposing
import Api
import Api.Model.ItemLightList exposing (ItemLightList)
import Comp.BookmarkQueryManage
import Comp.DownloadAll
import Comp.ItemCardList
import Comp.ItemDetail.FormChange exposing (FormChange(..))
import Comp.ItemDetail.MultiEditMenu exposing (SaveNameState(..))
@ -892,14 +893,46 @@ update texts bookmarkId lastViewedItemId env msg model =
Nothing ->
resultModelCmd env.selectedItems ( model, Cmd.none )
ToggleDownloadAllView ->
case createQuery env.selectedItems model of
Just q ->
case model.topWidgetModel of
DownloadAll _ ->
resultModelCmd env.selectedItems
( { model
| topWidgetModel = TopWidgetHidden
, viewMenuOpen = False
}
, Cmd.none
)
_ ->
let
( qm, qc ) =
Comp.DownloadAll.init Comp.DownloadAll.AccessUser env.flags (Q.render q)
in
resultModelCmd env.selectedItems
( { model | topWidgetModel = DownloadAll qm, viewMenuOpen = False }
, Cmd.map DownloadAllMsg qc
)
Nothing ->
resultModelCmd env.selectedItems ( model, Cmd.none )
ToggleBookmarkCurrentQueryView ->
case createQuery env.selectedItems model of
Just q ->
case model.topWidgetModel of
BookmarkQuery _ ->
resultModelCmd env.selectedItems ( { model | topWidgetModel = TopWidgetHidden, viewMenuOpen = False }, Cmd.none )
resultModelCmd env.selectedItems
( { model
| topWidgetModel = TopWidgetHidden
, viewMenuOpen = False
}
, Cmd.none
)
TopWidgetHidden ->
_ ->
let
( qm, qc ) =
Comp.BookmarkQueryManage.init (Q.render q)
@ -947,7 +980,30 @@ update texts bookmarkId lastViewedItemId env msg model =
, Sub.map BookmarkQueryMsg res.sub
)
TopWidgetHidden ->
_ ->
resultModelCmd env.selectedItems ( model, Cmd.none )
DownloadAllMsg lm ->
case model.topWidgetModel of
DownloadAll bm ->
let
res =
Comp.DownloadAll.update env.flags lm bm
nextModel =
if res.closed then
TopWidgetHidden
else
DownloadAll res.model
in
makeResult env.selectedItems
( { model | topWidgetModel = nextModel }
, Cmd.map DownloadAllMsg res.cmd
, Sub.none
)
_ ->
resultModelCmd env.selectedItems ( model, Cmd.none )
PublishViewMsg lmsg ->

View File

@ -11,6 +11,7 @@ import Api
import Comp.Basic as B
import Comp.BookmarkQueryManage
import Comp.ConfirmModal
import Comp.DownloadAll
import Comp.ItemCardList
import Comp.ItemMerge
import Comp.MenuBar as MB
@ -108,7 +109,7 @@ mainView texts env model =
bookmarkQueryWidget : Texts -> UiSettings -> Flags -> Model -> List (Html Msg)
bookmarkQueryWidget texts _ _ model =
bookmarkQueryWidget texts _ flags model =
case model.topWidgetModel of
BookmarkQuery m ->
[ div [ class "px-2 mb-4 border-l border-r border-b dark:border-slate-600" ]
@ -116,6 +117,12 @@ bookmarkQueryWidget texts _ _ model =
]
]
DownloadAll m ->
[ div [ class "mb-4 border-l border-r border-b dark:border-slate-600" ]
[ Html.map DownloadAllMsg (Comp.DownloadAll.view flags texts.downloadAllComp m)
]
]
TopWidgetHidden ->
[]
@ -437,6 +444,24 @@ defaultMenuBar texts env model =
onClick ToggleBookmarkCurrentQueryView
]
}
, { label = texts.downloadAll
, icon = i [ class "fa fa-download" ] []
, disabled = createQuery env.selectedItems model == Nothing
, attrs =
[ title <|
if createQuery env.selectedItems model == Nothing then
texts.downloadAllQueryNeeded
else
texts.downloadAll
, href "#"
, if createQuery env.selectedItems model == Nothing then
class ""
else
onClick ToggleDownloadAllView
]
}
, { label =
if env.settings.cardPreviewFullWidth then
texts.fullHeightPreviewTitle

View File

@ -5,13 +5,23 @@
-}
module Page.Share.Data exposing (Mode(..), Model, Msg(..), PageError(..), SearchBarMode(..), init, initCmd)
module Page.Share.Data exposing
( Mode(..)
, Model
, Msg(..)
, PageError(..)
, SearchBarMode(..)
, TopContentModel(..)
, init
, initCmd
)
import Api
import Api.Model.ItemLightList exposing (ItemLightList)
import Api.Model.SearchStats exposing (SearchStats)
import Api.Model.ShareSecret exposing (ShareSecret)
import Api.Model.ShareVerifyResult exposing (ShareVerifyResult)
import Comp.DownloadAll
import Comp.ItemCardList
import Comp.PowerSearchInput
import Comp.SearchMenu
@ -42,6 +52,11 @@ type SearchBarMode
| SearchBarContent
type TopContentModel
= TopContentHidden
| TopContentDownload Comp.DownloadAll.Model
type alias Model =
{ mode : Mode
, verifyResult : ShareVerifyResult
@ -61,6 +76,7 @@ type alias Model =
, arrange : ItemArrange
, rowsOpen : Set String
}
, topContent : TopContentModel
}
@ -84,6 +100,7 @@ emptyModel flags =
, arrange = Data.ItemArrange.Cards
, rowsOpen = Set.empty
}
, topContent = TopContentHidden
}
@ -122,3 +139,5 @@ type Msg
| ToggleViewMenu
| ToggleArrange ItemArrange
| ToggleShowGroups
| DownloadAllMsg Comp.DownloadAll.Msg
| ToggleDownloadAll

View File

@ -146,6 +146,15 @@ view texts flags model =
, onClick (ToggleArrange Data.ItemArrange.Cards)
]
}
, { label = texts.downloadAllLabel
, icon = i [ class "fa fa-download" ] []
, disabled = False
, attrs =
[ title texts.downloadAllLabel
, href "#"
, onClick ToggleDownloadAll
]
}
]
}
]

View File

@ -0,0 +1,28 @@
{-
Copyright 2020 Eike K. & Contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-}
module Page.Share.TopContent exposing (view)
import Comp.DownloadAll
import Data.Flags exposing (Flags)
import Html exposing (Html, div, span, text)
import Html.Attributes exposing (class)
import Messages.Page.Share exposing (Texts)
import Page.Share.Data exposing (Model, Msg(..), TopContentModel(..))
view : Texts -> Flags -> Model -> Html Msg
view texts flags model =
case model.topContent of
TopContentHidden ->
span [ class "hidden" ] []
TopContentDownload dm ->
div [ class "mb-4 border-l border-r border-b dark:border-slate-600" ]
[ Html.map DownloadAllMsg
(Comp.DownloadAll.view flags texts.downloadAll dm)
]

View File

@ -8,6 +8,7 @@
module Page.Share.Update exposing (UpdateResult, update)
import Api
import Comp.DownloadAll
import Comp.ItemCardList
import Comp.LinkTarget exposing (LinkTarget)
import Comp.PowerSearchInput
@ -19,7 +20,10 @@ import Data.ItemQuery as Q
import Data.SearchMode
import Data.UiSettings exposing (UiSettings)
import Page.Share.Data exposing (..)
import Process
import Set
import Task
import Time
import Util.Html
import Util.Maybe
import Util.Update
@ -252,30 +256,97 @@ update flags settings shareId msg model =
UiSettingsResp (Err _) ->
noSub ( model, Cmd.none )
DownloadAllMsg lm ->
case model.topContent of
TopContentDownload dm ->
let
res =
Comp.DownloadAll.update flags lm dm
nextModel =
if res.closed then
TopContentHidden
else
TopContentDownload res.model
-- The share page can't use websockets (not authenticated) so need to poll
-- for new download state
checkSub =
if Comp.DownloadAll.isPreparing res.model then
Process.sleep 3500
|> Task.perform (always (DownloadAllMsg Comp.DownloadAll.checkDownload))
else
Cmd.none
in
{ model = { model | topContent = nextModel }
, cmd =
Cmd.batch
[ Cmd.map DownloadAllMsg res.cmd
, checkSub
]
, sub = Sub.none
}
_ ->
noSub ( model, Cmd.none )
ToggleDownloadAll ->
let
vm =
model.viewMode
nextVm =
{ vm | menuOpen = False }
in
case model.topContent of
TopContentHidden ->
let
query =
createQuery flags model
|> Maybe.withDefault (Q.DateMs Q.Gt 0)
am =
Comp.DownloadAll.AccessShare shareId
( dm, dc ) =
Comp.DownloadAll.init am flags (Q.render query)
in
noSub ( { model | topContent = TopContentDownload dm, viewMode = nextVm }, Cmd.map DownloadAllMsg dc )
TopContentDownload _ ->
noSub ( { model | topContent = TopContentHidden, viewMode = nextVm }, Cmd.none )
noSub : ( Model, Cmd Msg ) -> UpdateResult
noSub ( m, c ) =
UpdateResult m c Sub.none
createQuery : Flags -> Model -> Maybe Q.ItemQuery
createQuery flags model =
Q.and
[ Comp.SearchMenu.getItemQuery Data.ItemIds.empty model.searchMenuModel
, Maybe.map Q.Fragment <|
case model.searchMode of
SearchBarNormal ->
Comp.PowerSearchInput.getSearchString model.powerSearchInput
SearchBarContent ->
if flags.config.fullTextSearchEnabled then
Maybe.map (Q.Contents >> Q.render) model.contentSearch
else
Maybe.map (Q.AllNames >> Q.render) model.contentSearch
]
makeSearchCmd : Flags -> Bool -> Model -> Cmd Msg
makeSearchCmd flags doInit model =
let
xq =
Q.and
[ Comp.SearchMenu.getItemQuery Data.ItemIds.empty model.searchMenuModel
, Maybe.map Q.Fragment <|
case model.searchMode of
SearchBarNormal ->
Comp.PowerSearchInput.getSearchString model.powerSearchInput
SearchBarContent ->
if flags.config.fullTextSearchEnabled then
Maybe.map (Q.Contents >> Q.render) model.contentSearch
else
Maybe.map (Q.AllNames >> Q.render) model.contentSearch
]
createQuery flags model
request mq =
{ offset = Nothing

View File

@ -19,6 +19,7 @@ import Page.Share.Data exposing (..)
import Page.Share.Menubar as Menubar
import Page.Share.Results as Results
import Page.Share.Sidebar as Sidebar
import Page.Share.TopContent as TopContent
import Styles as S
@ -80,6 +81,7 @@ mainContent texts flags shareId model =
]
, Menubar.view texts flags model
, errorMessage texts model
, TopContent.view texts flags model
, Results.view texts model.uiSettings flags shareId model
]

View File

@ -395,7 +395,7 @@ editLinkTableCellStyle =
dimmer : String
dimmer =
" absolute top-0 left-0 w-full h-full bg-black bg-opacity-90 dark:bg-slate-900 dark:bg-opacity-90 z-50 flex flex-col items-center justify-center px-4 md:px-8 py-2 "
" absolute top-0 left-0 w-full h-full bg-black bg-opacity-90 dark:bg-slate-900 dark:bg-opacity-90 z-30 flex flex-col items-center justify-center px-4 md:px-8 py-2 "
dimmerLight : String