mirror of
				https://github.com/TheAnachronism/docspell.git
				synced 2025-11-03 18:00:11 +00:00 
			
		
		
		
	Use a template for rendering title and subtitle of the item card
Introduces `ItemTemplate` to conveniently create strings given an item.
This commit is contained in:
		
							
								
								
									
										386
									
								
								modules/webapp/src/main/elm/Data/ItemTemplate.elm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								modules/webapp/src/main/elm/Data/ItemTemplate.elm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,386 @@
 | 
			
		||||
module Data.ItemTemplate exposing
 | 
			
		||||
    ( ItemTemplate
 | 
			
		||||
    , concEquip
 | 
			
		||||
    , concPerson
 | 
			
		||||
    , concat
 | 
			
		||||
    , concerning
 | 
			
		||||
    , corrOrg
 | 
			
		||||
    , corrPerson
 | 
			
		||||
    , correspondent
 | 
			
		||||
    , dateLong
 | 
			
		||||
    , dateShort
 | 
			
		||||
    , direction
 | 
			
		||||
    , dueDateLong
 | 
			
		||||
    , dueDateShort
 | 
			
		||||
    , empty
 | 
			
		||||
    , fileCount
 | 
			
		||||
    , folder
 | 
			
		||||
    , from
 | 
			
		||||
    , fromMaybe
 | 
			
		||||
    , helpMessage
 | 
			
		||||
    , isEmpty
 | 
			
		||||
    , literal
 | 
			
		||||
    , map
 | 
			
		||||
    , name
 | 
			
		||||
    , nonEmpty
 | 
			
		||||
    , readTemplate
 | 
			
		||||
    , render
 | 
			
		||||
    , source
 | 
			
		||||
    , splitTokens
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
import Api.Model.IdName exposing (IdName)
 | 
			
		||||
import Api.Model.ItemLight exposing (ItemLight)
 | 
			
		||||
import Data.Direction
 | 
			
		||||
import Set
 | 
			
		||||
import Util.List
 | 
			
		||||
import Util.String
 | 
			
		||||
import Util.Time
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type ItemTemplate
 | 
			
		||||
    = ItemTemplate (ItemLight -> String)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
readTemplate : String -> Maybe ItemTemplate
 | 
			
		||||
readTemplate str =
 | 
			
		||||
    let
 | 
			
		||||
        read tokens =
 | 
			
		||||
            List.map patternToken tokens
 | 
			
		||||
                |> concat
 | 
			
		||||
    in
 | 
			
		||||
    if str == "" then
 | 
			
		||||
        Just empty
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        Maybe.map read (splitTokens str)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
render : ItemTemplate -> ItemLight -> String
 | 
			
		||||
render pattern item =
 | 
			
		||||
    case pattern of
 | 
			
		||||
        ItemTemplate f ->
 | 
			
		||||
            f item
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
isEmpty : ItemTemplate -> ItemLight -> Bool
 | 
			
		||||
isEmpty pattern item =
 | 
			
		||||
    render pattern item |> String.isEmpty
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
nonEmpty : ItemTemplate -> ItemLight -> Bool
 | 
			
		||||
nonEmpty pattern item =
 | 
			
		||||
    isEmpty pattern item |> not
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Pattern Combinators
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
map : (String -> String) -> ItemTemplate -> ItemTemplate
 | 
			
		||||
map f pattern =
 | 
			
		||||
    case pattern of
 | 
			
		||||
        ItemTemplate p ->
 | 
			
		||||
            from (p >> f)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
map2 : (String -> String -> String) -> ItemTemplate -> ItemTemplate -> ItemTemplate
 | 
			
		||||
map2 f pattern1 pattern2 =
 | 
			
		||||
    case ( pattern1, pattern2 ) of
 | 
			
		||||
        ( ItemTemplate p1, ItemTemplate p2 ) ->
 | 
			
		||||
            from (\i -> f (p1 i) (p2 i))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
combine : String -> ItemTemplate -> ItemTemplate -> ItemTemplate
 | 
			
		||||
combine sep p1 p2 =
 | 
			
		||||
    map2
 | 
			
		||||
        (\s1 ->
 | 
			
		||||
            \s2 ->
 | 
			
		||||
                List.filter (String.isEmpty >> not) [ s1, s2 ]
 | 
			
		||||
                    |> String.join sep
 | 
			
		||||
        )
 | 
			
		||||
        p1
 | 
			
		||||
        p2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concat : List ItemTemplate -> ItemTemplate
 | 
			
		||||
concat patterns =
 | 
			
		||||
    from
 | 
			
		||||
        (\i ->
 | 
			
		||||
            List.map (\p -> render p i) patterns
 | 
			
		||||
                |> String.join ""
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
firstNonEmpty : List ItemTemplate -> ItemTemplate
 | 
			
		||||
firstNonEmpty patterns =
 | 
			
		||||
    from
 | 
			
		||||
        (\i ->
 | 
			
		||||
            List.map (\p -> render p i) patterns
 | 
			
		||||
                |> List.filter (String.isEmpty >> not)
 | 
			
		||||
                |> List.head
 | 
			
		||||
                |> Maybe.withDefault ""
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Patterns
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from : (ItemLight -> String) -> ItemTemplate
 | 
			
		||||
from f =
 | 
			
		||||
    ItemTemplate f
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fromMaybe : (ItemLight -> Maybe String) -> ItemTemplate
 | 
			
		||||
fromMaybe f =
 | 
			
		||||
    ItemTemplate (f >> Maybe.withDefault "")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
literal : String -> ItemTemplate
 | 
			
		||||
literal str =
 | 
			
		||||
    ItemTemplate (\_ -> str)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
empty : ItemTemplate
 | 
			
		||||
empty =
 | 
			
		||||
    literal ""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
name : ItemTemplate
 | 
			
		||||
name =
 | 
			
		||||
    ItemTemplate (.name >> Util.String.underscoreToSpace)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
direction : ItemTemplate
 | 
			
		||||
direction =
 | 
			
		||||
    let
 | 
			
		||||
        dirStr ms =
 | 
			
		||||
            Maybe.andThen Data.Direction.fromString ms
 | 
			
		||||
                |> Maybe.map Data.Direction.toString
 | 
			
		||||
    in
 | 
			
		||||
    fromMaybe (.direction >> dirStr)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dateLong : ItemTemplate
 | 
			
		||||
dateLong =
 | 
			
		||||
    ItemTemplate (.date >> Util.Time.formatDate)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dateShort : ItemTemplate
 | 
			
		||||
dateShort =
 | 
			
		||||
    ItemTemplate (.date >> Util.Time.formatDateShort)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dueDateLong : ItemTemplate
 | 
			
		||||
dueDateLong =
 | 
			
		||||
    fromMaybe (.dueDate >> Maybe.map Util.Time.formatDate)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
dueDateShort : ItemTemplate
 | 
			
		||||
dueDateShort =
 | 
			
		||||
    fromMaybe (.dueDate >> Maybe.map Util.Time.formatDateShort)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
source : ItemTemplate
 | 
			
		||||
source =
 | 
			
		||||
    ItemTemplate .source
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
folder : ItemTemplate
 | 
			
		||||
folder =
 | 
			
		||||
    ItemTemplate (.folder >> getName)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
corrOrg : ItemTemplate
 | 
			
		||||
corrOrg =
 | 
			
		||||
    ItemTemplate (.corrOrg >> getName)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
corrPerson : ItemTemplate
 | 
			
		||||
corrPerson =
 | 
			
		||||
    ItemTemplate (.corrPerson >> getName)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
correspondent : ItemTemplate
 | 
			
		||||
correspondent =
 | 
			
		||||
    combine ", " corrOrg corrPerson
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concPerson : ItemTemplate
 | 
			
		||||
concPerson =
 | 
			
		||||
    ItemTemplate (.concPerson >> getName)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concEquip : ItemTemplate
 | 
			
		||||
concEquip =
 | 
			
		||||
    ItemTemplate (.concEquipment >> getName)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
concerning : ItemTemplate
 | 
			
		||||
concerning =
 | 
			
		||||
    combine ", " concPerson concEquip
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fileCount : ItemTemplate
 | 
			
		||||
fileCount =
 | 
			
		||||
    ItemTemplate (.fileCount >> String.fromInt)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Helpers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
getName : Maybe IdName -> String
 | 
			
		||||
getName =
 | 
			
		||||
    Maybe.map .name >> Maybe.withDefault ""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--- Parse pattern
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
helpMessage : String
 | 
			
		||||
helpMessage =
 | 
			
		||||
    """
 | 
			
		||||
A pattern allows to customize the title and subtitle of each card.
 | 
			
		||||
Variables expressions are enclosed in `{{` and `}}`, other text is
 | 
			
		||||
used as-is. The following variables are available:
 | 
			
		||||
 | 
			
		||||
- `{{name}}` the item name
 | 
			
		||||
- `{{source}}` the source the item was created from
 | 
			
		||||
- `{{folder}}` the items folder
 | 
			
		||||
- `{{corrOrg}}` the correspondent organization
 | 
			
		||||
- `{{corrPerson}}` the correspondent person
 | 
			
		||||
- `{{correspondent}}` both organization and person separated by a comma
 | 
			
		||||
- `{{concPerson}}` the concerning person
 | 
			
		||||
- `{{concEquip}}` the concerning equipment
 | 
			
		||||
- `{{concerning}}` both person and equipment separated by a comma
 | 
			
		||||
- `{{fileCount}}` the number of attachments of this item
 | 
			
		||||
- `{{dateLong}}` the item date as full formatted date
 | 
			
		||||
- `{{dateShort}}` the item date as short formatted date (yyyy/mm/dd)
 | 
			
		||||
- `{{dueDateLong}}` the item due date as full formatted date
 | 
			
		||||
- `{{dueDateShort}}` the item due date as short formatted date (yyyy/mm/dd)
 | 
			
		||||
- `{{direction}}` the items direction values as string
 | 
			
		||||
 | 
			
		||||
If some variable is not present, an empty string is rendered. You can
 | 
			
		||||
combine multiple variables with `|` to use the first non-empty one,
 | 
			
		||||
for example `{{corrOrg|corrPerson|-}}` would render the organization
 | 
			
		||||
and if that is not present the person. If both are absent a dash `-`
 | 
			
		||||
is rendered.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
knownPattern : String -> Maybe ItemTemplate
 | 
			
		||||
knownPattern str =
 | 
			
		||||
    case str of
 | 
			
		||||
        "{{name}}" ->
 | 
			
		||||
            Just name
 | 
			
		||||
 | 
			
		||||
        "{{source}}" ->
 | 
			
		||||
            Just source
 | 
			
		||||
 | 
			
		||||
        "{{folder}}" ->
 | 
			
		||||
            Just folder
 | 
			
		||||
 | 
			
		||||
        "{{corrOrg}}" ->
 | 
			
		||||
            Just corrOrg
 | 
			
		||||
 | 
			
		||||
        "{{corrPerson}}" ->
 | 
			
		||||
            Just corrPerson
 | 
			
		||||
 | 
			
		||||
        "{{correspondent}}" ->
 | 
			
		||||
            Just correspondent
 | 
			
		||||
 | 
			
		||||
        "{{concPerson}}" ->
 | 
			
		||||
            Just concPerson
 | 
			
		||||
 | 
			
		||||
        "{{concEquip}}" ->
 | 
			
		||||
            Just concEquip
 | 
			
		||||
 | 
			
		||||
        "{{concerning}}" ->
 | 
			
		||||
            Just concerning
 | 
			
		||||
 | 
			
		||||
        "{{fileCount}}" ->
 | 
			
		||||
            Just fileCount
 | 
			
		||||
 | 
			
		||||
        "{{dateLong}}" ->
 | 
			
		||||
            Just dateLong
 | 
			
		||||
 | 
			
		||||
        "{{dateShort}}" ->
 | 
			
		||||
            Just dateShort
 | 
			
		||||
 | 
			
		||||
        "{{dueDateLong}}" ->
 | 
			
		||||
            Just dueDateLong
 | 
			
		||||
 | 
			
		||||
        "{{dueDateShort}}" ->
 | 
			
		||||
            Just dueDateShort
 | 
			
		||||
 | 
			
		||||
        "{{direction}}" ->
 | 
			
		||||
            Just direction
 | 
			
		||||
 | 
			
		||||
        _ ->
 | 
			
		||||
            Nothing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
patternToken : String -> ItemTemplate
 | 
			
		||||
patternToken str =
 | 
			
		||||
    knownPattern str
 | 
			
		||||
        |> Maybe.withDefault
 | 
			
		||||
            (alternativeToken str
 | 
			
		||||
                |> Maybe.withDefault (literal str)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
alternativeToken : String -> Maybe ItemTemplate
 | 
			
		||||
alternativeToken str =
 | 
			
		||||
    let
 | 
			
		||||
        inner =
 | 
			
		||||
            String.dropLeft 2 str
 | 
			
		||||
                |> String.dropRight 2
 | 
			
		||||
                |> String.split "|"
 | 
			
		||||
                |> List.filter (String.isEmpty >> not)
 | 
			
		||||
 | 
			
		||||
        pattern s =
 | 
			
		||||
            knownPattern ("{{" ++ s ++ "}}")
 | 
			
		||||
                |> Maybe.withDefault (literal s)
 | 
			
		||||
    in
 | 
			
		||||
    if String.startsWith "{{" str && String.endsWith "}}" str then
 | 
			
		||||
        case inner of
 | 
			
		||||
            [] ->
 | 
			
		||||
                Nothing
 | 
			
		||||
 | 
			
		||||
            _ ->
 | 
			
		||||
                List.map pattern inner
 | 
			
		||||
                    |> firstNonEmpty
 | 
			
		||||
                    |> Just
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        Nothing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
splitTokens : String -> Maybe (List String)
 | 
			
		||||
splitTokens str =
 | 
			
		||||
    let
 | 
			
		||||
        begins =
 | 
			
		||||
            String.indexes "{{" str
 | 
			
		||||
 | 
			
		||||
        ends =
 | 
			
		||||
            String.indexes "}}" str
 | 
			
		||||
                |> List.map ((+) 2)
 | 
			
		||||
 | 
			
		||||
        indexes =
 | 
			
		||||
            Set.union (Set.fromList begins) (Set.fromList ends)
 | 
			
		||||
                |> Set.insert 0
 | 
			
		||||
                |> Set.insert (String.length str)
 | 
			
		||||
                |> Set.toList
 | 
			
		||||
                |> List.sort
 | 
			
		||||
 | 
			
		||||
        mkSubstring i1 i2 =
 | 
			
		||||
            String.slice i1 i2 str
 | 
			
		||||
    in
 | 
			
		||||
    if List.length begins == List.length ends then
 | 
			
		||||
        Util.List.sliding mkSubstring indexes |> Just
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        Nothing
 | 
			
		||||
		Reference in New Issue
	
	Block a user