Compare commits
	
		
			5 Commits 
		
	
	
		
			f5f5c14e10
			...
			d12466e82a
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
								 | 
						d12466e82a | |
| 
							
							
								
								 | 
						4b92d9ea20 | |
| 
							
							
								
								 | 
						4777de5b67 | |
| 
							
							
								
								 | 
						865e83cdae | |
| 
							
							
								
								 | 
						2cb21dc102 | 
							
								
								
									
										3
									
								
								elm.json
								
								
								
								
							
							
						
						
									
										3
									
								
								elm.json
								
								
								
								
							| 
						 | 
					@ -19,7 +19,8 @@
 | 
				
			||||||
        "elm/http": "2.0.0 <= v < 3.0.0",
 | 
					        "elm/http": "2.0.0 <= v < 3.0.0",
 | 
				
			||||||
        "elm/json": "1.0.0 <= v < 2.0.0",
 | 
					        "elm/json": "1.0.0 <= v < 2.0.0",
 | 
				
			||||||
        "elm/time": "1.0.0 <= v < 2.0.0",
 | 
					        "elm/time": "1.0.0 <= v < 2.0.0",
 | 
				
			||||||
        "elm/url": "1.0.0 <= v < 2.0.0"
 | 
					        "elm/url": "1.0.0 <= v < 2.0.0",
 | 
				
			||||||
 | 
					        "miniBill/elm-fast-dict": "1.0.0 <= v < 2.0.0"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "test-dependencies": {}
 | 
					    "test-dependencies": {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,30 +31,52 @@ type as a message to the Vault to update certain information.
 | 
				
			||||||
-}
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Http
 | 
					import Http
 | 
				
			||||||
import Internal.Api.Helpers as Helpers
 | 
					 | 
				
			||||||
import Internal.Tools.Context as Context exposing (Context)
 | 
					import Internal.Tools.Context as Context exposing (Context)
 | 
				
			||||||
import Internal.Tools.Exceptions as X
 | 
					import Internal.Tools.Exceptions as X
 | 
				
			||||||
import Task exposing (Task)
 | 
					import Task exposing (Task)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| The TaskChain is a piece in the long chain of tasks that need to be completed.
 | 
				
			||||||
 | 
					The type defines four variables:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `err` value that may arise on an error
 | 
				
			||||||
 | 
					- `u`   the update msg that should be returned
 | 
				
			||||||
 | 
					- `a`   phantom type before executing the chain's context
 | 
				
			||||||
 | 
					- `b`   phantom type after  executing the chain's context
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
type alias TaskChain err u a b =
 | 
					type alias TaskChain err u a b =
 | 
				
			||||||
    Context a -> Task (FailedChainPiece err u) (TaskChainPiece u a b)
 | 
					    Context a -> Task (FailedChainPiece err u) (TaskChainPiece u a b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| An IdemChain is a TaskChain that does not influence the chain's context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `err` value that may arise on an error
 | 
				
			||||||
 | 
					- `u`   the update msg that should be executed
 | 
				
			||||||
 | 
					- `a`   phantom type before, during and after the chain's context
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
type alias IdemChain err u a =
 | 
					type alias IdemChain err u a =
 | 
				
			||||||
    TaskChain err u a a
 | 
					    TaskChain err u a a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| A CompleteChain is a complete snake that can be safely run and executed by
 | 
				
			||||||
 | 
					the Elm core.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
type alias CompleteChain u =
 | 
					type alias CompleteChain u =
 | 
				
			||||||
    TaskChain () u {} {}
 | 
					    TaskChain () u {} {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| A TaskChainPiece is a piece that updates the chain's context.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Once a chain is executed, the process will add the `messages` value to its list
 | 
				
			||||||
 | 
					of updates, and it will update its context according to the `contextChange`
 | 
				
			||||||
 | 
					function.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
type alias TaskChainPiece u a b =
 | 
					type alias TaskChainPiece u a b =
 | 
				
			||||||
    { contextChange : Context a -> Context b
 | 
					    { contextChange : Context a -> Context b
 | 
				
			||||||
    , messages : List u
 | 
					    , messages : List u
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| A FailedChainPiece initiates an early breakdown of a chain. Unless caught,
 | 
				
			||||||
 | 
					this halts execution of the chain. The process will add the `messages` value to
 | 
				
			||||||
 | 
					its list of updates, and it will return the given `err` value for a direct
 | 
				
			||||||
 | 
					explanation of what went wrong.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
type alias FailedChainPiece err u =
 | 
					type alias FailedChainPiece err u =
 | 
				
			||||||
    { error : err, messages : List u }
 | 
					    { error : err, messages : List u }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,46 +37,3 @@ ratelimited task =
 | 
				
			||||||
                    _ ->
 | 
					                    _ ->
 | 
				
			||||||
                        Task.fail e
 | 
					                        Task.fail e
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{-| Sometimes, you don't really care if something went wrong - you just want to try again.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This task will only return an error if it went wrong on the n'th attempt.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-}
 | 
					 | 
				
			||||||
retryTask : Int -> Task X.Error a -> Task X.Error a
 | 
					 | 
				
			||||||
retryTask n task =
 | 
					 | 
				
			||||||
    if n <= 0 then
 | 
					 | 
				
			||||||
        task
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        Task.onError
 | 
					 | 
				
			||||||
            (\err ->
 | 
					 | 
				
			||||||
                let
 | 
					 | 
				
			||||||
                    retry : Task X.Error a
 | 
					 | 
				
			||||||
                    retry =
 | 
					 | 
				
			||||||
                        retryTask (n - 1) task
 | 
					 | 
				
			||||||
                in
 | 
					 | 
				
			||||||
                case err of
 | 
					 | 
				
			||||||
                    X.InternetException (Http.BadUrl _) ->
 | 
					 | 
				
			||||||
                        Task.fail err
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    X.InternetException _ ->
 | 
					 | 
				
			||||||
                        retry
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    X.SDKException (X.ServerReturnsBadJSON _) ->
 | 
					 | 
				
			||||||
                        retry
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    X.SDKException _ ->
 | 
					 | 
				
			||||||
                        Task.fail err
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    X.ServerException _ ->
 | 
					 | 
				
			||||||
                        Task.fail err
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    X.ContextFailed _ ->
 | 
					 | 
				
			||||||
                        Task.fail err
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    X.UnsupportedSpecVersion ->
 | 
					 | 
				
			||||||
                        Task.fail err
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            task
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,9 +20,9 @@ Note that **under development** doesn't always mean that it _will be_ supported.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| **Spec version** |   | Syncing | Redaction |
 | 
					| **Spec version** |   | Syncing | Redaction |
 | 
				
			||||||
| ---------------- | - | ------- | --------- |
 | 
					| ---------------- | - | ------- | --------- |
 | 
				
			||||||
| v1.8   || ⚡ | ✔️ |
 | 
					| v1.8   || ✔️ | ✔️ |
 | 
				
			||||||
| v1.7   || ⚡ | ✔️ |
 | 
					| v1.7   || ✔️ | ✔️ |
 | 
				
			||||||
| v1.6   || ⚠️ | ✔️ |
 | 
					| v1.6   || ✔️ | ✔️ |
 | 
				
			||||||
| v1.5   || ✔️ | ✔️ |
 | 
					| v1.5   || ✔️ | ✔️ |
 | 
				
			||||||
| v1.4   || ✔️ | ✔️ |
 | 
					| v1.4   || ✔️ | ✔️ |
 | 
				
			||||||
| v1.3   || ✔️ | ✔️ |
 | 
					| v1.3   || ✔️ | ✔️ |
 | 
				
			||||||
| 
						 | 
					@ -64,8 +64,8 @@ Note that **under development** doesn't always mean that it _will be_ supported.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| **Spec version** |   | Event | Joined members | Event at timestamp |
 | 
					| **Spec version** |   | Event | Joined members | Event at timestamp |
 | 
				
			||||||
| ---------------- | - | ----- | -------------- | ------------------ |
 | 
					| ---------------- | - | ----- | -------------- | ------------------ |
 | 
				
			||||||
| v1.8   || ✔️ | ✔️ | ⚡ |
 | 
					| v1.8   || ✔️ | ✔️ | ⚠️ |
 | 
				
			||||||
| v1.7   || ✔️ | ✔️ | ⚡ |
 | 
					| v1.7   || ✔️ | ✔️ | ⚠️ |
 | 
				
			||||||
| v1.6   || ✔️ | ✔️ | ⚠️ |
 | 
					| v1.6   || ✔️ | ✔️ | ⚠️ |
 | 
				
			||||||
| v1.5   || ✔️ | ✔️ | ⛔ |
 | 
					| v1.5   || ✔️ | ✔️ | ⛔ |
 | 
				
			||||||
| v1.4   || ✔️ | ✔️ | ⛔ |
 | 
					| v1.4   || ✔️ | ✔️ | ⛔ |
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
module Internal.Api.Snackbar exposing (..)
 | 
					module Internal.Api.Snackbar exposing (..)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{-| The snackbar module helps wraps relevant credentials, access tokens, refresh tokens and more around internal types.
 | 
					{-| The snackbar module helps wraps relevant credentials, access tokens, 
 | 
				
			||||||
 | 
					refresh tokens and more around internal types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Vault, Room and Event types don't need access to API tokens,
 | 
					Vault, Room and Event types don't need access to API tokens,
 | 
				
			||||||
but a user may way to redact an event, leave a room or reject an invite.
 | 
					but a user may way to redact an event, leave a room or reject an invite.
 | 
				
			||||||
| 
						 | 
					@ -14,6 +15,7 @@ without needing to update every data type whenever any of the tokens change.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Dict exposing (Dict)
 | 
					import Dict exposing (Dict)
 | 
				
			||||||
import Internal.Api.Versions.V1.Versions as V
 | 
					import Internal.Api.Versions.V1.Versions as V
 | 
				
			||||||
 | 
					import Internal.Config.DefaultSettings as DS
 | 
				
			||||||
import Internal.Tools.LoginValues as Login exposing (AccessToken(..))
 | 
					import Internal.Tools.LoginValues as Login exposing (AccessToken(..))
 | 
				
			||||||
import Task exposing (Task)
 | 
					import Task exposing (Task)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,9 +29,14 @@ type Snackbar a vu
 | 
				
			||||||
        , homeserver : String
 | 
					        , homeserver : String
 | 
				
			||||||
        , transactionOffset : Int
 | 
					        , transactionOffset : Int
 | 
				
			||||||
        , vs : Maybe V.Versions
 | 
					        , vs : Maybe V.Versions
 | 
				
			||||||
 | 
					        , settings : Settings
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type alias Settings =
 | 
				
			||||||
 | 
					    { syncTimeout : Int }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
accessToken : Snackbar a vu -> AccessToken
 | 
					accessToken : Snackbar a vu -> AccessToken
 | 
				
			||||||
accessToken (Snackbar { access }) =
 | 
					accessToken (Snackbar { access }) =
 | 
				
			||||||
    access
 | 
					    access
 | 
				
			||||||
| 
						 | 
					@ -92,6 +99,9 @@ init data =
 | 
				
			||||||
        , failedTasks = Dict.empty
 | 
					        , failedTasks = Dict.empty
 | 
				
			||||||
        , failedTasksOffset = 0
 | 
					        , failedTasksOffset = 0
 | 
				
			||||||
        , homeserver = data.baseUrl
 | 
					        , homeserver = data.baseUrl
 | 
				
			||||||
 | 
					        , settings =
 | 
				
			||||||
 | 
					            { syncTimeout = DS.syncTimeout
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        , transactionOffset = 0
 | 
					        , transactionOffset = 0
 | 
				
			||||||
        , vs = Nothing
 | 
					        , vs = Nothing
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -105,6 +115,7 @@ map f (Snackbar data) =
 | 
				
			||||||
        , failedTasks = data.failedTasks
 | 
					        , failedTasks = data.failedTasks
 | 
				
			||||||
        , failedTasksOffset = 0
 | 
					        , failedTasksOffset = 0
 | 
				
			||||||
        , homeserver = data.homeserver
 | 
					        , homeserver = data.homeserver
 | 
				
			||||||
 | 
					        , settings = data.settings
 | 
				
			||||||
        , transactionOffset = data.transactionOffset
 | 
					        , transactionOffset = data.transactionOffset
 | 
				
			||||||
        , vs = data.vs
 | 
					        , vs = data.vs
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -135,6 +146,11 @@ setTransactionOffset i (Snackbar data) =
 | 
				
			||||||
    Snackbar { data | transactionOffset = max (data.transactionOffset + 1) (i + 1) }
 | 
					    Snackbar { data | transactionOffset = max (data.transactionOffset + 1) (i + 1) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					updateSettings : (Settings -> Settings) -> Snackbar a vu -> Snackbar a vu
 | 
				
			||||||
 | 
					updateSettings f (Snackbar ({ settings } as data)) =
 | 
				
			||||||
 | 
					    Snackbar { data | settings = f settings }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
userId : Snackbar a vu -> Maybe String
 | 
					userId : Snackbar a vu -> Maybe String
 | 
				
			||||||
userId (Snackbar { access }) =
 | 
					userId (Snackbar { access }) =
 | 
				
			||||||
    Login.getUserId access
 | 
					    Login.getUserId access
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,3 +33,10 @@ supportedVersions =
 | 
				
			||||||
defaultDeviceName : String
 | 
					defaultDeviceName : String
 | 
				
			||||||
defaultDeviceName =
 | 
					defaultDeviceName =
 | 
				
			||||||
    "Elm Matrix SDK (v" ++ currentVersion ++ ")"
 | 
					    "Elm Matrix SDK (v" ++ currentVersion ++ ")"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| The amount of seconds that the Matrix Vault should wait for a response from the Matrix homeserver.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					syncTimeout : Int
 | 
				
			||||||
 | 
					syncTimeout =
 | 
				
			||||||
 | 
					    10
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,7 @@ import Internal.Values.Room as Internal
 | 
				
			||||||
import Internal.Values.StateManager as StateManager
 | 
					import Internal.Values.StateManager as StateManager
 | 
				
			||||||
import Internal.Values.Timeline as Timeline
 | 
					import Internal.Values.Timeline as Timeline
 | 
				
			||||||
import Json.Encode as E
 | 
					import Json.Encode as E
 | 
				
			||||||
import Task exposing (Task)
 | 
					import Task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{-| The `Room` type represents a Matrix Room. It contains context information
 | 
					{-| The `Room` type represents a Matrix Room. It contains context information
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,56 @@
 | 
				
			||||||
 | 
					module Internal.Tools.DefaultDict exposing (..)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import FastDict as Dict exposing (Dict)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| A dictionary of keys and values that includes a default when a key doesn't exist.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type DefaultDict k v
 | 
				
			||||||
 | 
					    = DefaultDict
 | 
				
			||||||
 | 
					        { content : Dict k v
 | 
				
			||||||
 | 
					        , default : v
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Create an empty dictionary that has a default value.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					empty : v -> DefaultDict k v
 | 
				
			||||||
 | 
					empty v =
 | 
				
			||||||
 | 
					    DefaultDict
 | 
				
			||||||
 | 
					        { content = Dict.empty
 | 
				
			||||||
 | 
					        , default = v
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Get the value associated with the key. Uses the default if not found. -}
 | 
				
			||||||
 | 
					get : comparable -> DefaultDict comparable v -> v
 | 
				
			||||||
 | 
					get k (DefaultDict data) =
 | 
				
			||||||
 | 
					    Dict.get k data.content |> Maybe.withDefault data.default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Insert a key-value pair into a dictionary with a default.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					insert : comparable -> v -> DefaultDict comparable v -> DefaultDict comparable v
 | 
				
			||||||
 | 
					insert k v (DefaultDict data) =
 | 
				
			||||||
 | 
					    DefaultDict { data | content = Dict.insert k v data.content }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| "Remove" a value by making its value synchronize with the default value.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					remove : comparable -> DefaultDict comparable v -> DefaultDict comparable v
 | 
				
			||||||
 | 
					remove k (DefaultDict data) =
 | 
				
			||||||
 | 
					    DefaultDict { data | content = Dict.remove k data.content }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Update the default value of all unset keys.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					setDefault : v -> DefaultDict k v -> DefaultDict k v
 | 
				
			||||||
 | 
					setDefault v (DefaultDict data) =
 | 
				
			||||||
 | 
					    DefaultDict { data | default = v }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Update the value of a dictionary. The returned (or received) value is `Nothing`,
 | 
				
			||||||
 | 
					it means the key synchronizes with the default value.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					update : comparable -> (Maybe v -> Maybe v) -> DefaultDict comparable v -> DefaultDict comparable v
 | 
				
			||||||
 | 
					update k fv (DefaultDict data) =
 | 
				
			||||||
 | 
					    DefaultDict { data | content = Dict.update k fv data.content }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Update the default value.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					updateDefault : (v -> v) -> DefaultDict k v -> DefaultDict k v
 | 
				
			||||||
 | 
					updateDefault f (DefaultDict data) =
 | 
				
			||||||
 | 
					    DefaultDict { data | default = f data.default }
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,384 @@
 | 
				
			||||||
 | 
					module Internal.Tools.Filters.Filter exposing (..)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Internal.Tools.Filters.SpecObjects as SO
 | 
				
			||||||
 | 
					import Internal.Tools.Filters.SimpleFilter as SF exposing (SimpleFilter)
 | 
				
			||||||
 | 
					import Internal.Tools.SpecEnums as Enums
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Event filters tell the API what events to look for, 
 | 
				
			||||||
 | 
					but specifically for events that are unrelated to any room.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type EventFilter
 | 
				
			||||||
 | 
					    = EventFilter
 | 
				
			||||||
 | 
					        { limit : Maybe Int
 | 
				
			||||||
 | 
					        , senders : SimpleFilter String
 | 
				
			||||||
 | 
					        , types : SimpleFilter String
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| The final type dictates how everything else behaves.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type Filter =
 | 
				
			||||||
 | 
					    Filter
 | 
				
			||||||
 | 
					        { accountData : EventFilter
 | 
				
			||||||
 | 
					        , presence : EventFilter
 | 
				
			||||||
 | 
					        , room : RoomFilter
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| RoomFilter types tell the API what is considered relevant in a room, 
 | 
				
			||||||
 | 
					and which rooms to include.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type RoomFilter
 | 
				
			||||||
 | 
					    = RoomFilter
 | 
				
			||||||
 | 
					        { accountData : RoomEventFilter
 | 
				
			||||||
 | 
					        , ephemeral : RoomEventFilter
 | 
				
			||||||
 | 
					        , rooms : SimpleFilter String
 | 
				
			||||||
 | 
					        , timeline : RoomEventFilter
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| RoomEventFilter types tell the API what events to look for, 
 | 
				
			||||||
 | 
					and what ones to ignore.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type RoomEventFilter
 | 
				
			||||||
 | 
					    = RoomEventFilter
 | 
				
			||||||
 | 
					        { lazyLoadMembers : Bool
 | 
				
			||||||
 | 
					        , limit : Maybe Int
 | 
				
			||||||
 | 
					        , rooms : SimpleFilter String
 | 
				
			||||||
 | 
					        , senders : SimpleFilter String
 | 
				
			||||||
 | 
					        , types : SimpleFilter String
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					allEvents : EventFilter
 | 
				
			||||||
 | 
					allEvents =
 | 
				
			||||||
 | 
					    EventFilter
 | 
				
			||||||
 | 
					        { limit = Nothing
 | 
				
			||||||
 | 
					        , senders = SF.all
 | 
				
			||||||
 | 
					        , types = SF.all
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					allFilters : Filter
 | 
				
			||||||
 | 
					allFilters =
 | 
				
			||||||
 | 
					    Filter
 | 
				
			||||||
 | 
					        { accountData = allEvents
 | 
				
			||||||
 | 
					        , presence = allEvents
 | 
				
			||||||
 | 
					        , room = allRooms
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					allRooms : RoomFilter
 | 
				
			||||||
 | 
					allRooms =
 | 
				
			||||||
 | 
					    RoomFilter
 | 
				
			||||||
 | 
					        { accountData = allRoomEvents
 | 
				
			||||||
 | 
					        , ephemeral = allRoomEvents
 | 
				
			||||||
 | 
					        , rooms = SF.all
 | 
				
			||||||
 | 
					        , timeline = allRoomEvents
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					allRoomEvents : RoomEventFilter
 | 
				
			||||||
 | 
					allRoomEvents =
 | 
				
			||||||
 | 
					    RoomEventFilter
 | 
				
			||||||
 | 
					        { lazyLoadMembers = False
 | 
				
			||||||
 | 
					        , limit = Nothing
 | 
				
			||||||
 | 
					        , rooms = SF.all
 | 
				
			||||||
 | 
					        , senders = SF.all
 | 
				
			||||||
 | 
					        , types = SF.all
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					decodeEventFilter : SO.EventFilter -> EventFilter
 | 
				
			||||||
 | 
					decodeEventFilter data =
 | 
				
			||||||
 | 
					    EventFilter
 | 
				
			||||||
 | 
					        { limit = data.limit
 | 
				
			||||||
 | 
					        , senders = SF.toSimpleFilter data.senders data.notSenders
 | 
				
			||||||
 | 
					        , types = SF.toSimpleFilter data.types data.notTypes
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					decodeFilter : SO.Filter -> Filter
 | 
				
			||||||
 | 
					decodeFilter data =
 | 
				
			||||||
 | 
					    Filter
 | 
				
			||||||
 | 
					        { accountData =
 | 
				
			||||||
 | 
					            data.accountData
 | 
				
			||||||
 | 
					                |> Maybe.map decodeEventFilter
 | 
				
			||||||
 | 
					                |> Maybe.withDefault allEvents
 | 
				
			||||||
 | 
					        , presence =
 | 
				
			||||||
 | 
					            data.presence
 | 
				
			||||||
 | 
					                |> Maybe.map decodeEventFilter
 | 
				
			||||||
 | 
					                |> Maybe.withDefault allEvents
 | 
				
			||||||
 | 
					        , room =
 | 
				
			||||||
 | 
					            data.room
 | 
				
			||||||
 | 
					                |> Maybe.map decodeRoomFilter
 | 
				
			||||||
 | 
					                |> Maybe.withDefault allRooms
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Decode a RoomFilter from a spec-compliant format.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					decodeRoomFilter : SO.RoomFilter -> RoomFilter
 | 
				
			||||||
 | 
					decodeRoomFilter data =
 | 
				
			||||||
 | 
					    let
 | 
				
			||||||
 | 
					        decodeREF : Maybe SO.RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					        decodeREF =
 | 
				
			||||||
 | 
					            Maybe.map decodeRoomEventFilter >> Maybe.withDefault allRoomEvents
 | 
				
			||||||
 | 
					    in
 | 
				
			||||||
 | 
					        RoomFilter
 | 
				
			||||||
 | 
					            { accountData = decodeREF data.accountData
 | 
				
			||||||
 | 
					            , ephemeral = decodeREF data.ephemeral
 | 
				
			||||||
 | 
					            , rooms = SF.toSimpleFilter data.rooms data.notRooms
 | 
				
			||||||
 | 
					            , timeline = decodeREF data.timeline
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Decode a RoomEventFilter from a spec-compliant format.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					decodeRoomEventFilter : SO.RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					decodeRoomEventFilter data =
 | 
				
			||||||
 | 
					    RoomEventFilter
 | 
				
			||||||
 | 
					        { lazyLoadMembers = data.lazyLoadMembers
 | 
				
			||||||
 | 
					        , limit = data.limit
 | 
				
			||||||
 | 
					        , rooms = SF.toSimpleFilter data.rooms data.notRooms
 | 
				
			||||||
 | 
					        , senders = SF.toSimpleFilter data.senders data.notSenders
 | 
				
			||||||
 | 
					        , types = SF.toSimpleFilter data.types data.notTypes
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Encode an EventFilter into a spec-compliant format.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					encodeEventFilter : EventFilter -> SO.EventFilter
 | 
				
			||||||
 | 
					encodeEventFilter (EventFilter data) =
 | 
				
			||||||
 | 
					    { limit = data.limit
 | 
				
			||||||
 | 
					    , notSenders = SF.toExclude data.senders
 | 
				
			||||||
 | 
					    , notTypes = SF.toExclude data.types
 | 
				
			||||||
 | 
					    , senders = SF.toInclude data.senders
 | 
				
			||||||
 | 
					    , types = SF.toInclude data.types
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Encode a Filter into a spec-compliant format.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					encodeFilter : Filter -> SO.Filter
 | 
				
			||||||
 | 
					encodeFilter (Filter data) =
 | 
				
			||||||
 | 
					    { accountData = Just <| encodeEventFilter data.accountData
 | 
				
			||||||
 | 
					    , eventFields = Nothing
 | 
				
			||||||
 | 
					    , eventFormat = Enums.Client
 | 
				
			||||||
 | 
					    , presence = Just <| encodeEventFilter data.presence
 | 
				
			||||||
 | 
					    , room = Just <| encodeRoomFilter data.room
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Encode a RoomFilter into a spec-compliant format.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					encodeRoomFilter : RoomFilter -> SO.RoomFilter
 | 
				
			||||||
 | 
					encodeRoomFilter (RoomFilter data) =
 | 
				
			||||||
 | 
					    { accountData = Just <| encodeRoomEventFilter data.accountData
 | 
				
			||||||
 | 
					    , ephemeral = Just <| encodeRoomEventFilter data.ephemeral
 | 
				
			||||||
 | 
					    , includeLeave = False
 | 
				
			||||||
 | 
					    , notRooms = SF.toExclude data.rooms
 | 
				
			||||||
 | 
					    , rooms = SF.toInclude data.rooms
 | 
				
			||||||
 | 
					    , state = Just <| encodeRoomEventFilter data.timeline
 | 
				
			||||||
 | 
					    , timeline = Just <| encodeRoomEventFilter data.timeline
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Encode a RoomEventFilter into a spec-compliant format.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					encodeRoomEventFilter : RoomEventFilter -> SO.RoomEventFilter
 | 
				
			||||||
 | 
					encodeRoomEventFilter (RoomEventFilter data) =
 | 
				
			||||||
 | 
					    { containsUrl = Nothing
 | 
				
			||||||
 | 
					    , includeRedundantMembers = False
 | 
				
			||||||
 | 
					    , lazyLoadMembers = data.lazyLoadMembers
 | 
				
			||||||
 | 
					    , limit = data.limit
 | 
				
			||||||
 | 
					    , notRooms = SF.toExclude data.rooms
 | 
				
			||||||
 | 
					    , notSenders = SF.toExclude data.senders
 | 
				
			||||||
 | 
					    , notTypes = SF.toExclude data.types
 | 
				
			||||||
 | 
					    , rooms = SF.toInclude data.rooms
 | 
				
			||||||
 | 
					    , senders = SF.toInclude data.senders
 | 
				
			||||||
 | 
					    , types = SF.toInclude data.types
 | 
				
			||||||
 | 
					    , unreadThreadNotifications = True
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Flatten a filter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					flattenFilter : Filter -> List (SimpleFilter String)
 | 
				
			||||||
 | 
					flattenFilter (Filter f) =
 | 
				
			||||||
 | 
					    List.concat
 | 
				
			||||||
 | 
					        [ flattenEventFilter f.accountData
 | 
				
			||||||
 | 
					        , flattenEventFilter f.presence
 | 
				
			||||||
 | 
					        , flattenRoomFilter f.room
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Flatten a EventFilter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					flattenEventFilter : EventFilter -> List (SimpleFilter String)
 | 
				
			||||||
 | 
					flattenEventFilter (EventFilter f) = [ f.senders, f.types ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Flatten a RoomFilter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					flattenRoomFilter : RoomFilter -> List (SimpleFilter String)
 | 
				
			||||||
 | 
					flattenRoomFilter (RoomFilter f) =
 | 
				
			||||||
 | 
					    [ f.accountData, f.ephemeral, f.timeline ]
 | 
				
			||||||
 | 
					        |> List.map flattenRoomEventFilter
 | 
				
			||||||
 | 
					        |> List.concat
 | 
				
			||||||
 | 
					        |> (::) f.rooms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Flatten a RoomEventFilter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					flattenRoomEventFilter : RoomEventFilter -> List (SimpleFilter String)
 | 
				
			||||||
 | 
					flattenRoomEventFilter (RoomEventFilter f) = [ f.rooms, f.senders, f.types ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Get an intersection of a Filter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					intersectFilter : Filter -> Filter -> Filter
 | 
				
			||||||
 | 
					intersectFilter (Filter f1) (Filter f2) =
 | 
				
			||||||
 | 
					    Filter
 | 
				
			||||||
 | 
					        { accountData = intersectEventFilter f1.accountData f2.accountData
 | 
				
			||||||
 | 
					        , presence = intersectEventFilter f1.presence f2.presence
 | 
				
			||||||
 | 
					        , room = intersectRoomFilter f1.room f2.room
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Get an intersection of a EventFilter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					intersectEventFilter : EventFilter -> EventFilter -> EventFilter
 | 
				
			||||||
 | 
					intersectEventFilter (EventFilter f1) (EventFilter f2) =
 | 
				
			||||||
 | 
					    EventFilter
 | 
				
			||||||
 | 
					        { limit =
 | 
				
			||||||
 | 
					            case (f1.limit, f2.limit) of
 | 
				
			||||||
 | 
					                (Just l1, Just l2) ->
 | 
				
			||||||
 | 
					                    Just (max l1 l2)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                (Just _, Nothing) ->
 | 
				
			||||||
 | 
					                    f1.limit
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                (Nothing, Just _) ->
 | 
				
			||||||
 | 
					                    f2.limit
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                (Nothing, Nothing) ->
 | 
				
			||||||
 | 
					                    Nothing
 | 
				
			||||||
 | 
					        , senders = SF.intersect f1.senders f2.senders
 | 
				
			||||||
 | 
					        , types = SF.intersect f1.types f2.types
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Get an intersection of a RoomFilter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					intersectRoomFilter : RoomFilter -> RoomFilter -> RoomFilter
 | 
				
			||||||
 | 
					intersectRoomFilter (RoomFilter f1) (RoomFilter f2) =
 | 
				
			||||||
 | 
					    RoomFilter
 | 
				
			||||||
 | 
					        { accountData = intersectRoomEventFilter f1.accountData f2.accountData
 | 
				
			||||||
 | 
					        , ephemeral = intersectRoomEventFilter f1.ephemeral f2.ephemeral
 | 
				
			||||||
 | 
					        , rooms = SF.intersect f1.rooms f2.rooms
 | 
				
			||||||
 | 
					        , timeline = intersectRoomEventFilter f1.timeline f2.timeline
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Get an intersection of a RoomEventFilter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					intersectRoomEventFilter : RoomEventFilter -> RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					intersectRoomEventFilter (RoomEventFilter f1) (RoomEventFilter f2) =
 | 
				
			||||||
 | 
					    RoomEventFilter
 | 
				
			||||||
 | 
					        { lazyLoadMembers = f1.lazyLoadMembers && f2.lazyLoadMembers
 | 
				
			||||||
 | 
					        , limit =
 | 
				
			||||||
 | 
					            case (f1.limit, f2.limit) of
 | 
				
			||||||
 | 
					                (Just l1, Just l2) ->
 | 
				
			||||||
 | 
					                    Just (max l1 l2)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                (Just _, Nothing) ->
 | 
				
			||||||
 | 
					                    f1.limit
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                (Nothing, Just _) ->
 | 
				
			||||||
 | 
					                    f2.limit
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                (Nothing, Nothing) ->
 | 
				
			||||||
 | 
					                    Nothing
 | 
				
			||||||
 | 
					        , rooms = SF.intersect f1.rooms f2.rooms
 | 
				
			||||||
 | 
					        , senders = SF.intersect f1.senders f2.senders
 | 
				
			||||||
 | 
					        , types = SF.intersect f1.types f2.types
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Check whether a filter is a subset of another filter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					isSubSet : Filter -> Filter -> Bool
 | 
				
			||||||
 | 
					isSubSet f1 f2 =
 | 
				
			||||||
 | 
					    let
 | 
				
			||||||
 | 
					        isSame : List (SimpleFilter String) -> List (SimpleFilter String) -> Bool
 | 
				
			||||||
 | 
					        isSame l1 l2 =
 | 
				
			||||||
 | 
					            case (l1, l2) of
 | 
				
			||||||
 | 
					                (h1 :: t1, h2 :: t2) ->
 | 
				
			||||||
 | 
					                    SF.subset h1 h2 && isSame t1 t2
 | 
				
			||||||
 | 
					                ([], []) ->
 | 
				
			||||||
 | 
					                    True
 | 
				
			||||||
 | 
					                _ ->
 | 
				
			||||||
 | 
					                    False
 | 
				
			||||||
 | 
					    in
 | 
				
			||||||
 | 
					        isSame (flattenFilter f1) (flattenFilter f2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lazyLoadMembers : Bool -> RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					lazyLoadMembers b (RoomEventFilter data) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | lazyLoadMembers = b }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Determine a limit for the amount of events. If no limit is given, the homeserver decides this limit for itself.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					setEventLimit : Maybe Int -> RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					setEventLimit i (RoomEventFilter data) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | limit = i }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Include a specific event type.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withEventType : String -> RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withEventType x (RoomEventFilter ({ types } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | types = SF.with x types }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Include all event types that haven't been explicitly mentioned.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withOtherEventTypes : RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withOtherEventTypes (RoomEventFilter ({ types } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | types = SF.withOthers types }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Include all rooms that haven't been explicitly mentioned.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withOtherRooms : RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withOtherRooms (RoomEventFilter ({ rooms } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | rooms = SF.withOthers rooms }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Include all senders that haven't been explicitly mentioned.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withOtherSenders : RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withOtherSenders (RoomEventFilter ({ senders } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | senders = SF.withOthers senders }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Include a specific room.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withRoom : String -> RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withRoom x (RoomEventFilter ({ rooms } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | rooms = SF.with x rooms }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Include a specific sender.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withSender : String -> RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withSender x (RoomEventFilter ({ senders } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | senders = SF.with x senders }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Ignore a specific event type.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withoutEventType : String -> RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withoutEventType x (RoomEventFilter ({ types } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | types = SF.without x types }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Ignore all rooms that haven't been explicitly mentioned.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withoutOtherEventTypes : RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withoutOtherEventTypes (RoomEventFilter ({ types } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | types = SF.withoutOthers types }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Ignore all rooms that haven't been explicitly mentioned.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withoutOtherRooms : RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withoutOtherRooms (RoomEventFilter ({ rooms } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | rooms = SF.withoutOthers rooms }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Ignore all senders that haven't been explicitly mentioned.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withoutOtherSenders : RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withoutOtherSenders (RoomEventFilter ({ senders } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | senders = SF.withoutOthers senders }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Ignore a specific room.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withoutRoom : String -> RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withoutRoom x (RoomEventFilter ({ rooms } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | rooms = SF.without x rooms }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Ignore a specific sender.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withoutSender : String -> RoomEventFilter -> RoomEventFilter
 | 
				
			||||||
 | 
					withoutSender x (RoomEventFilter ({ senders } as data)) =
 | 
				
			||||||
 | 
					    RoomEventFilter { data | senders = SF.without x senders }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,85 @@
 | 
				
			||||||
 | 
					module Internal.Tools.Filters.Main exposing (..)
 | 
				
			||||||
 | 
					{-| This module contains the main functions used to get, manipulate and change
 | 
				
			||||||
 | 
					filters according to their needs.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Internal.Tools.Filters.Filter as F
 | 
				
			||||||
 | 
					import Internal.Tools.Filters.SimpleFilter as SF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type alias Filter = F.Filter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type alias SimpleFilter = SF.SimpleFilter String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Filter that adds all occurrences by default, but leaves a few ones out.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When provided with an empty list, the filter allows all types.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					allExcept : List String -> SimpleFilter
 | 
				
			||||||
 | 
					allExcept =
 | 
				
			||||||
 | 
					    List.foldl SF.without SF.all
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Filter that removes everything by default, but leaves a few ones in.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When provided with an empty list, the filter allows nothing.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					only : List String -> SimpleFilter
 | 
				
			||||||
 | 
					only =
 | 
				
			||||||
 | 
					    List.foldl SF.with SF.none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fromSimpleFilter :
 | 
				
			||||||
 | 
					    { accountDataTypes : SimpleFilter
 | 
				
			||||||
 | 
					    , presence : { limit : Maybe Int, senders : SimpleFilter, types : SimpleFilter }
 | 
				
			||||||
 | 
					    , ephemeral : { limit : Maybe Int, senders : SimpleFilter, types : SimpleFilter }
 | 
				
			||||||
 | 
					    , roomIds : SimpleFilter
 | 
				
			||||||
 | 
					    , lazyLoadMembers : Bool
 | 
				
			||||||
 | 
					    , roomEvents : { limit : Maybe Int, senders : SimpleFilter, types : SimpleFilter }
 | 
				
			||||||
 | 
					    } -> Filter
 | 
				
			||||||
 | 
					fromSimpleFilter data =
 | 
				
			||||||
 | 
					    F.Filter
 | 
				
			||||||
 | 
					        { accountData =
 | 
				
			||||||
 | 
					            F.EventFilter
 | 
				
			||||||
 | 
					                { limit = Nothing
 | 
				
			||||||
 | 
					                , senders = SF.all
 | 
				
			||||||
 | 
					                , types = data.accountDataTypes
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        , presence =
 | 
				
			||||||
 | 
					            F.EventFilter
 | 
				
			||||||
 | 
					                { limit = data.presence.limit
 | 
				
			||||||
 | 
					                , senders = data.presence.senders
 | 
				
			||||||
 | 
					                , types = data.presence.types
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        , room =
 | 
				
			||||||
 | 
					            F.RoomFilter
 | 
				
			||||||
 | 
					                { accountData =
 | 
				
			||||||
 | 
					                    F.RoomEventFilter
 | 
				
			||||||
 | 
					                        { lazyLoadMembers = data.lazyLoadMembers
 | 
				
			||||||
 | 
					                        , limit = Nothing
 | 
				
			||||||
 | 
					                        , rooms = data.roomIds
 | 
				
			||||||
 | 
					                        , senders = SF.all
 | 
				
			||||||
 | 
					                        , types = data.accountDataTypes
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                , ephemeral =
 | 
				
			||||||
 | 
					                    F.RoomEventFilter
 | 
				
			||||||
 | 
					                        { lazyLoadMembers = data.lazyLoadMembers
 | 
				
			||||||
 | 
					                        , limit = data.ephemeral.limit
 | 
				
			||||||
 | 
					                        , rooms = data.roomIds
 | 
				
			||||||
 | 
					                        , senders = data.ephemeral.senders
 | 
				
			||||||
 | 
					                        , types = data.ephemeral.types
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                , rooms = data.roomIds
 | 
				
			||||||
 | 
					                , timeline = 
 | 
				
			||||||
 | 
					                    F.RoomEventFilter
 | 
				
			||||||
 | 
					                        { lazyLoadMembers = data.lazyLoadMembers
 | 
				
			||||||
 | 
					                        , limit = data.roomEvents.limit
 | 
				
			||||||
 | 
					                        , rooms = data.roomIds
 | 
				
			||||||
 | 
					                        , senders = data.roomEvents.senders
 | 
				
			||||||
 | 
					                        , types = data.roomEvents.types
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Get the intersection of two filters.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					intersect : Filter -> Filter -> Filter
 | 
				
			||||||
 | 
					intersect =
 | 
				
			||||||
 | 
					    F.intersectFilter
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,172 @@
 | 
				
			||||||
 | 
					module Internal.Tools.Filters.SimpleFilter exposing (..)
 | 
				
			||||||
 | 
					{-| The SimpleFilter tracks values that should or should not be monitored.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Dict exposing (Dict)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| SimpleFilter type that tracks items to include or exclude.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type alias SimpleFilter a =
 | 
				
			||||||
 | 
					    { specificOnes : Dict a Bool
 | 
				
			||||||
 | 
					    , includeOthers : Bool
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					all : SimpleFilter a
 | 
				
			||||||
 | 
					all =
 | 
				
			||||||
 | 
					    { specificOnes = Dict.empty
 | 
				
			||||||
 | 
					    , includeOthers = True
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Use filter ones that are only available in the first filter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					diff : SimpleFilter comparable -> SimpleFilter comparable -> SimpleFilter comparable
 | 
				
			||||||
 | 
					diff f1 f2 =
 | 
				
			||||||
 | 
					    { specificOnes =
 | 
				
			||||||
 | 
					        Dict.merge
 | 
				
			||||||
 | 
					            (\k v1 -> Dict.insert k (v1 && not f2.includeOthers))
 | 
				
			||||||
 | 
					            (\k v1 v2 -> Dict.insert k (v1 && not v2))
 | 
				
			||||||
 | 
					            (\k v2 -> Dict.insert k (f1.includeOthers && not v2))
 | 
				
			||||||
 | 
					            f1.specificOnes
 | 
				
			||||||
 | 
					            f2.specificOnes
 | 
				
			||||||
 | 
					            Dict.empty
 | 
				
			||||||
 | 
					    , includeOthers = f1.includeOthers && not f2.includeOthers
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Form a filter that only shows the values that two filters have in common.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					intersect : SimpleFilter comparable -> SimpleFilter comparable -> SimpleFilter comparable
 | 
				
			||||||
 | 
					intersect f1 f2 =
 | 
				
			||||||
 | 
					    { specificOnes =
 | 
				
			||||||
 | 
					        Dict.merge
 | 
				
			||||||
 | 
					            (\key v1 -> Dict.insert key (v1 && f2.includeOthers))
 | 
				
			||||||
 | 
					            (\key v1 v2 -> Dict.insert key (v1 && v2))
 | 
				
			||||||
 | 
					            (\key v2 -> Dict.insert key (f1.includeOthers && v2))
 | 
				
			||||||
 | 
					            f1.specificOnes
 | 
				
			||||||
 | 
					            f2.specificOnes
 | 
				
			||||||
 | 
					            Dict.empty
 | 
				
			||||||
 | 
					    , includeOthers = f1.includeOthers && f2.includeOthers
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Start with a filter that includes none.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					none : SimpleFilter a
 | 
				
			||||||
 | 
					none =
 | 
				
			||||||
 | 
					    { specificOnes = Dict.empty
 | 
				
			||||||
 | 
					    , includeOthers = False
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Check whether a SimpleFilter is a subset of another filter.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					subset : SimpleFilter comparable -> SimpleFilter comparable -> Bool
 | 
				
			||||||
 | 
					subset small large =
 | 
				
			||||||
 | 
					    if small.includeOthers && not large.includeOthers then
 | 
				
			||||||
 | 
					        False
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        -- All elements of small are in large
 | 
				
			||||||
 | 
					        Dict.merge
 | 
				
			||||||
 | 
					            (\_ s ->
 | 
				
			||||||
 | 
					                if s && not large.includeOthers then
 | 
				
			||||||
 | 
					                    always False
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    identity
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            (\_ s l ->
 | 
				
			||||||
 | 
					                if s && not l then
 | 
				
			||||||
 | 
					                    always False
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    identity
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            (\_ l ->
 | 
				
			||||||
 | 
					                if small.includeOthers && not l then
 | 
				
			||||||
 | 
					                    always False
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    identity
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            small.specificOnes
 | 
				
			||||||
 | 
					            large.specificOnes
 | 
				
			||||||
 | 
					            True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Encode a SimpleFilter into a list of items to exclude.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					toExclude : SimpleFilter comparable -> Maybe (List comparable)
 | 
				
			||||||
 | 
					toExclude f =
 | 
				
			||||||
 | 
					    f.specificOnes
 | 
				
			||||||
 | 
					        |> Dict.filter (always not)
 | 
				
			||||||
 | 
					        |> Dict.keys
 | 
				
			||||||
 | 
					        |> Just
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Encode a SimpleFilter into a list of items to include.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					toInclude : SimpleFilter comparable -> Maybe (List comparable)
 | 
				
			||||||
 | 
					toInclude f =
 | 
				
			||||||
 | 
					    if f.includeOthers then
 | 
				
			||||||
 | 
					        Nothing
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        f.specificOnes
 | 
				
			||||||
 | 
					            |> Dict.filter (always identity)
 | 
				
			||||||
 | 
					            |> Dict.keys
 | 
				
			||||||
 | 
					            |> Just
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Create a SimpleFilter out of two optionally present lists.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					toSimpleFilter : Maybe (List comparable) -> Maybe (List comparable) -> SimpleFilter comparable
 | 
				
			||||||
 | 
					toSimpleFilter these notThese =
 | 
				
			||||||
 | 
					    let
 | 
				
			||||||
 | 
					        no : List comparable
 | 
				
			||||||
 | 
					        no = Maybe.withDefault [] notThese
 | 
				
			||||||
 | 
					    in
 | 
				
			||||||
 | 
					    case these of
 | 
				
			||||||
 | 
					        Just yes ->
 | 
				
			||||||
 | 
					            { specificOnes =
 | 
				
			||||||
 | 
					                Dict.union
 | 
				
			||||||
 | 
					                    (Dict.fromList ( List.map (\x -> Tuple.pair x False) no ))
 | 
				
			||||||
 | 
					                    (Dict.fromList ( List.map (\x -> Tuple.pair x True) yes ))
 | 
				
			||||||
 | 
					            , includeOthers = False
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Nothing ->
 | 
				
			||||||
 | 
					            { specificOnes =
 | 
				
			||||||
 | 
					                no
 | 
				
			||||||
 | 
					                    |> List.map (\x -> Tuple.pair x False)
 | 
				
			||||||
 | 
					                    |> Dict.fromList
 | 
				
			||||||
 | 
					            , includeOthers = True
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Form a filter that includes values if it is included in either filters.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					union : SimpleFilter comparable -> SimpleFilter comparable -> SimpleFilter comparable
 | 
				
			||||||
 | 
					union f1 f2 =
 | 
				
			||||||
 | 
					    { specificOnes =
 | 
				
			||||||
 | 
					        Dict.merge
 | 
				
			||||||
 | 
					            (\key v1 -> Dict.insert key (v1 || f2.includeOthers))
 | 
				
			||||||
 | 
					            (\key v1 v2 -> Dict.insert key (v1 || v2))
 | 
				
			||||||
 | 
					            (\key v2 -> Dict.insert key (f1.includeOthers || v2))
 | 
				
			||||||
 | 
					            f1.specificOnes
 | 
				
			||||||
 | 
					            f2.specificOnes
 | 
				
			||||||
 | 
					            Dict.empty
 | 
				
			||||||
 | 
					    , includeOthers = f1.includeOthers && f2.includeOthers
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Add a value that should be included.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					with : comparable -> SimpleFilter comparable -> SimpleFilter comparable
 | 
				
			||||||
 | 
					with x f =
 | 
				
			||||||
 | 
					    { f | specificOnes = Dict.insert x True f.specificOnes }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Include all values that haven't been mentioned.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withOthers : SimpleFilter comparable -> SimpleFilter comparable
 | 
				
			||||||
 | 
					withOthers f =
 | 
				
			||||||
 | 
					    { f | includeOthers = True }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Add a value that should be ignored.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					without : comparable -> SimpleFilter comparable -> SimpleFilter comparable
 | 
				
			||||||
 | 
					without x f =
 | 
				
			||||||
 | 
					    { f | specificOnes = Dict.insert x False f.specificOnes }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Ignore all values that haven't been mentioned.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					withoutOthers : SimpleFilter comparable -> SimpleFilter comparable
 | 
				
			||||||
 | 
					withoutOthers f =
 | 
				
			||||||
 | 
					    { f | includeOthers = False }
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,246 @@
 | 
				
			||||||
 | 
					module Internal.Tools.Filters.SpecObjects exposing
 | 
				
			||||||
 | 
					    ( EventFilter
 | 
				
			||||||
 | 
					    , Filter
 | 
				
			||||||
 | 
					    , RoomEventFilter
 | 
				
			||||||
 | 
					    , RoomFilter
 | 
				
			||||||
 | 
					    , StateFilter
 | 
				
			||||||
 | 
					    , encodeEventFilter
 | 
				
			||||||
 | 
					    , encodeFilter
 | 
				
			||||||
 | 
					    , encodeRoomEventFilter
 | 
				
			||||||
 | 
					    , encodeRoomFilter
 | 
				
			||||||
 | 
					    , encodeStateFilter
 | 
				
			||||||
 | 
					    , eventFilterDecoder
 | 
				
			||||||
 | 
					    , filterDecoder
 | 
				
			||||||
 | 
					    , roomEventFilterDecoder
 | 
				
			||||||
 | 
					    , roomFilterDecoder
 | 
				
			||||||
 | 
					    , stateFilterDecoder
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Automatically generated 'SpecObjects'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Last generated at Unix time 1681915222
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Internal.Tools.DecodeExtra as D exposing (opField, opFieldWithDefault)
 | 
				
			||||||
 | 
					import Internal.Tools.EncodeExtra exposing (maybeObject)
 | 
				
			||||||
 | 
					import Internal.Tools.SpecEnums as Enums
 | 
				
			||||||
 | 
					import Json.Decode as D
 | 
				
			||||||
 | 
					import Json.Encode as E
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Filter that describes which events to include/exclude.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type alias EventFilter =
 | 
				
			||||||
 | 
					    { limit : Maybe Int
 | 
				
			||||||
 | 
					    , notSenders : Maybe (List String)
 | 
				
			||||||
 | 
					    , notTypes : Maybe (List String)
 | 
				
			||||||
 | 
					    , senders : Maybe (List String)
 | 
				
			||||||
 | 
					    , types : Maybe (List String)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					encodeEventFilter : EventFilter -> E.Value
 | 
				
			||||||
 | 
					encodeEventFilter data =
 | 
				
			||||||
 | 
					    maybeObject
 | 
				
			||||||
 | 
					        [ ( "limit", Maybe.map E.int data.limit )
 | 
				
			||||||
 | 
					        , ( "not_senders", Maybe.map (E.list E.string) data.notSenders )
 | 
				
			||||||
 | 
					        , ( "not_types", Maybe.map (E.list E.string) data.notTypes )
 | 
				
			||||||
 | 
					        , ( "senders", Maybe.map (E.list E.string) data.senders )
 | 
				
			||||||
 | 
					        , ( "types", Maybe.map (E.list E.string) data.types )
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					eventFilterDecoder : D.Decoder EventFilter
 | 
				
			||||||
 | 
					eventFilterDecoder =
 | 
				
			||||||
 | 
					    D.map5
 | 
				
			||||||
 | 
					        (\a b c d e ->
 | 
				
			||||||
 | 
					            { limit = a, notSenders = b, notTypes = c, senders = d, types = e }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        (opField "limit" D.int)
 | 
				
			||||||
 | 
					        (opField "not_senders" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "not_types" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "senders" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "types" (D.list D.string))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Main filter for filtering results
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type alias Filter =
 | 
				
			||||||
 | 
					    { accountData : Maybe EventFilter
 | 
				
			||||||
 | 
					    , eventFields : Maybe (List String)
 | 
				
			||||||
 | 
					    , eventFormat : Enums.EventFormat
 | 
				
			||||||
 | 
					    , presence : Maybe EventFilter
 | 
				
			||||||
 | 
					    , room : Maybe RoomFilter
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					encodeFilter : Filter -> E.Value
 | 
				
			||||||
 | 
					encodeFilter data =
 | 
				
			||||||
 | 
					    maybeObject
 | 
				
			||||||
 | 
					        [ ( "account_data", Maybe.map encodeEventFilter data.accountData )
 | 
				
			||||||
 | 
					        , ( "event_fields", Maybe.map (E.list E.string) data.eventFields )
 | 
				
			||||||
 | 
					        , ( "event_format", Just <| Enums.encodeEventFormat data.eventFormat )
 | 
				
			||||||
 | 
					        , ( "presence", Maybe.map encodeEventFilter data.presence )
 | 
				
			||||||
 | 
					        , ( "room", Maybe.map encodeRoomFilter data.room )
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					filterDecoder : D.Decoder Filter
 | 
				
			||||||
 | 
					filterDecoder =
 | 
				
			||||||
 | 
					    D.map5
 | 
				
			||||||
 | 
					        (\a b c d e ->
 | 
				
			||||||
 | 
					            { accountData = a, eventFields = b, eventFormat = c, presence = d, room = e }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        (opField "account_data" eventFilterDecoder)
 | 
				
			||||||
 | 
					        (opField "event_fields" (D.list D.string))
 | 
				
			||||||
 | 
					        (opFieldWithDefault "event_format" Enums.Client Enums.eventFormatDecoder)
 | 
				
			||||||
 | 
					        (opField "presence" eventFilterDecoder)
 | 
				
			||||||
 | 
					        (opField "room" roomFilterDecoder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Filter that describes which events to include/exclude in a Matrix room.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type alias RoomEventFilter =
 | 
				
			||||||
 | 
					    { containsUrl : Maybe Bool
 | 
				
			||||||
 | 
					    , includeRedundantMembers : Bool
 | 
				
			||||||
 | 
					    , lazyLoadMembers : Bool
 | 
				
			||||||
 | 
					    , limit : Maybe Int
 | 
				
			||||||
 | 
					    , notRooms : Maybe (List String)
 | 
				
			||||||
 | 
					    , notSenders : Maybe (List String)
 | 
				
			||||||
 | 
					    , notTypes : Maybe (List String)
 | 
				
			||||||
 | 
					    , rooms : Maybe (List String)
 | 
				
			||||||
 | 
					    , senders : Maybe (List String)
 | 
				
			||||||
 | 
					    , types : Maybe (List String)
 | 
				
			||||||
 | 
					    , unreadThreadNotifications : Bool
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					encodeRoomEventFilter : RoomEventFilter -> E.Value
 | 
				
			||||||
 | 
					encodeRoomEventFilter data =
 | 
				
			||||||
 | 
					    maybeObject
 | 
				
			||||||
 | 
					        [ ( "contains_url", Maybe.map E.bool data.containsUrl )
 | 
				
			||||||
 | 
					        , ( "include_redundant_members", Just <| E.bool data.includeRedundantMembers )
 | 
				
			||||||
 | 
					        , ( "lazy_load_members", Just <| E.bool data.lazyLoadMembers )
 | 
				
			||||||
 | 
					        , ( "limit", Maybe.map E.int data.limit )
 | 
				
			||||||
 | 
					        , ( "not_rooms", Maybe.map (E.list E.string) data.notRooms )
 | 
				
			||||||
 | 
					        , ( "not_senders", Maybe.map (E.list E.string) data.notSenders )
 | 
				
			||||||
 | 
					        , ( "not_types", Maybe.map (E.list E.string) data.notTypes )
 | 
				
			||||||
 | 
					        , ( "rooms", Maybe.map (E.list E.string) data.rooms )
 | 
				
			||||||
 | 
					        , ( "senders", Maybe.map (E.list E.string) data.senders )
 | 
				
			||||||
 | 
					        , ( "types", Maybe.map (E.list E.string) data.types )
 | 
				
			||||||
 | 
					        , ( "unread_thread_notifications", Just <| E.bool data.unreadThreadNotifications )
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					roomEventFilterDecoder : D.Decoder RoomEventFilter
 | 
				
			||||||
 | 
					roomEventFilterDecoder =
 | 
				
			||||||
 | 
					    D.map11
 | 
				
			||||||
 | 
					        (\a b c d e f g h i j k ->
 | 
				
			||||||
 | 
					            { containsUrl = a, includeRedundantMembers = b, lazyLoadMembers = c, limit = d, notRooms = e, notSenders = f, notTypes = g, rooms = h, senders = i, types = j, unreadThreadNotifications = k }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        (opField "contains_url" D.bool)
 | 
				
			||||||
 | 
					        (opFieldWithDefault "include_redundant_members" False D.bool)
 | 
				
			||||||
 | 
					        (opFieldWithDefault "lazy_load_members" False D.bool)
 | 
				
			||||||
 | 
					        (opField "limit" D.int)
 | 
				
			||||||
 | 
					        (opField "not_rooms" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "not_senders" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "not_types" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "rooms" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "senders" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "types" (D.list D.string))
 | 
				
			||||||
 | 
					        (opFieldWithDefault "unread_thread_notifications" False D.bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Filter that describes what should and shouldn't be included for rooms.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type alias RoomFilter =
 | 
				
			||||||
 | 
					    { accountData : Maybe RoomEventFilter
 | 
				
			||||||
 | 
					    , ephemeral : Maybe RoomEventFilter
 | 
				
			||||||
 | 
					    , includeLeave : Bool
 | 
				
			||||||
 | 
					    , notRooms : Maybe (List String)
 | 
				
			||||||
 | 
					    , rooms : Maybe (List String)
 | 
				
			||||||
 | 
					    , state : Maybe StateFilter
 | 
				
			||||||
 | 
					    , timeline : Maybe RoomEventFilter
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					encodeRoomFilter : RoomFilter -> E.Value
 | 
				
			||||||
 | 
					encodeRoomFilter data =
 | 
				
			||||||
 | 
					    maybeObject
 | 
				
			||||||
 | 
					        [ ( "account_data", Maybe.map encodeRoomEventFilter data.accountData )
 | 
				
			||||||
 | 
					        , ( "ephemeral", Maybe.map encodeRoomEventFilter data.ephemeral )
 | 
				
			||||||
 | 
					        , ( "include_leave", Just <| E.bool data.includeLeave )
 | 
				
			||||||
 | 
					        , ( "not_rooms", Maybe.map (E.list E.string) data.notRooms )
 | 
				
			||||||
 | 
					        , ( "rooms", Maybe.map (E.list E.string) data.rooms )
 | 
				
			||||||
 | 
					        , ( "state", Maybe.map encodeStateFilter data.state )
 | 
				
			||||||
 | 
					        , ( "timeline", Maybe.map encodeRoomEventFilter data.timeline )
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					roomFilterDecoder : D.Decoder RoomFilter
 | 
				
			||||||
 | 
					roomFilterDecoder =
 | 
				
			||||||
 | 
					    D.map7
 | 
				
			||||||
 | 
					        (\a b c d e f g ->
 | 
				
			||||||
 | 
					            { accountData = a, ephemeral = b, includeLeave = c, notRooms = d, rooms = e, state = f, timeline = g }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        (opField "account_data" roomEventFilterDecoder)
 | 
				
			||||||
 | 
					        (opField "ephemeral" roomEventFilterDecoder)
 | 
				
			||||||
 | 
					        (opFieldWithDefault "include_leave" False D.bool)
 | 
				
			||||||
 | 
					        (opField "not_rooms" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "rooms" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "state" stateFilterDecoder)
 | 
				
			||||||
 | 
					        (opField "timeline" roomEventFilterDecoder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| Filter that describes which events to include/exclude in a Matrix room.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type alias StateFilter =
 | 
				
			||||||
 | 
					    { containsUrl : Maybe Bool
 | 
				
			||||||
 | 
					    , includeRedundantMembers : Bool
 | 
				
			||||||
 | 
					    , lazyLoadMembers : Bool
 | 
				
			||||||
 | 
					    , limit : Maybe Int
 | 
				
			||||||
 | 
					    , notRooms : Maybe (List String)
 | 
				
			||||||
 | 
					    , notSenders : Maybe (List String)
 | 
				
			||||||
 | 
					    , notTypes : Maybe (List String)
 | 
				
			||||||
 | 
					    , rooms : Maybe (List String)
 | 
				
			||||||
 | 
					    , senders : Maybe (List String)
 | 
				
			||||||
 | 
					    , types : Maybe (List String)
 | 
				
			||||||
 | 
					    , unreadThreadNotifications : Bool
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					encodeStateFilter : StateFilter -> E.Value
 | 
				
			||||||
 | 
					encodeStateFilter data =
 | 
				
			||||||
 | 
					    maybeObject
 | 
				
			||||||
 | 
					        [ ( "contains_url", Maybe.map E.bool data.containsUrl )
 | 
				
			||||||
 | 
					        , ( "include_redundant_members", Just <| E.bool data.includeRedundantMembers )
 | 
				
			||||||
 | 
					        , ( "lazy_load_members", Just <| E.bool data.lazyLoadMembers )
 | 
				
			||||||
 | 
					        , ( "limit", Maybe.map E.int data.limit )
 | 
				
			||||||
 | 
					        , ( "not_rooms", Maybe.map (E.list E.string) data.notRooms )
 | 
				
			||||||
 | 
					        , ( "not_senders", Maybe.map (E.list E.string) data.notSenders )
 | 
				
			||||||
 | 
					        , ( "not_types", Maybe.map (E.list E.string) data.notTypes )
 | 
				
			||||||
 | 
					        , ( "rooms", Maybe.map (E.list E.string) data.rooms )
 | 
				
			||||||
 | 
					        , ( "senders", Maybe.map (E.list E.string) data.senders )
 | 
				
			||||||
 | 
					        , ( "types", Maybe.map (E.list E.string) data.types )
 | 
				
			||||||
 | 
					        , ( "unread_thread_notifications", Just <| E.bool data.unreadThreadNotifications )
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					stateFilterDecoder : D.Decoder StateFilter
 | 
				
			||||||
 | 
					stateFilterDecoder =
 | 
				
			||||||
 | 
					    D.map11
 | 
				
			||||||
 | 
					        (\a b c d e f g h i j k ->
 | 
				
			||||||
 | 
					            { containsUrl = a, includeRedundantMembers = b, lazyLoadMembers = c, limit = d, notRooms = e, notSenders = f, notTypes = g, rooms = h, senders = i, types = j, unreadThreadNotifications = k }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        (opField "contains_url" D.bool)
 | 
				
			||||||
 | 
					        (opFieldWithDefault "include_redundant_members" False D.bool)
 | 
				
			||||||
 | 
					        (opFieldWithDefault "lazy_load_members" False D.bool)
 | 
				
			||||||
 | 
					        (opField "limit" D.int)
 | 
				
			||||||
 | 
					        (opField "not_rooms" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "not_senders" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "not_types" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "rooms" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "senders" (D.list D.string))
 | 
				
			||||||
 | 
					        (opField "types" (D.list D.string))
 | 
				
			||||||
 | 
					        (opFieldWithDefault "unread_thread_notifications" False D.bool)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,135 @@
 | 
				
			||||||
 | 
					version: v1
 | 
				
			||||||
 | 
					name: SpecObjects
 | 
				
			||||||
 | 
					objects:
 | 
				
			||||||
 | 
					  Filter:
 | 
				
			||||||
 | 
					    description: Main filter for filtering results
 | 
				
			||||||
 | 
					    fields:
 | 
				
			||||||
 | 
					      account_data:
 | 
				
			||||||
 | 
					        type: EventFilter
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      event_fields:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      event_format:
 | 
				
			||||||
 | 
					        type: Enums.EventFormat
 | 
				
			||||||
 | 
					        default: Enums.Client
 | 
				
			||||||
 | 
					      presence:
 | 
				
			||||||
 | 
					        type: EventFilter
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      room:
 | 
				
			||||||
 | 
					        type: RoomFilter
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					  EventFilter:
 | 
				
			||||||
 | 
					    description: Filter that describes which events to include/exclude.
 | 
				
			||||||
 | 
					    fields:
 | 
				
			||||||
 | 
					      limit:
 | 
				
			||||||
 | 
					        type: int
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      not_senders:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      not_types:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      senders:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      types:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					  RoomFilter:
 | 
				
			||||||
 | 
					    description: Filter that describes what should and shouldn't be included for rooms.
 | 
				
			||||||
 | 
					    fields:
 | 
				
			||||||
 | 
					      account_data:
 | 
				
			||||||
 | 
					        type: RoomEventFilter
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      ephemeral:
 | 
				
			||||||
 | 
					        type: RoomEventFilter
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      include_leave:
 | 
				
			||||||
 | 
					        type: bool
 | 
				
			||||||
 | 
					        default: 'False'
 | 
				
			||||||
 | 
					      not_rooms:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      rooms:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      state:
 | 
				
			||||||
 | 
					        type: StateFilter
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      timeline:
 | 
				
			||||||
 | 
					        type: RoomEventFilter
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					  RoomEventFilter:
 | 
				
			||||||
 | 
					    description: Filter that describes which events to include/exclude in a Matrix room.
 | 
				
			||||||
 | 
					    fields:
 | 
				
			||||||
 | 
					      contains_url:
 | 
				
			||||||
 | 
					        type: bool
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      include_redundant_members:
 | 
				
			||||||
 | 
					        type: bool
 | 
				
			||||||
 | 
					        default: 'False'
 | 
				
			||||||
 | 
					      lazy_load_members:
 | 
				
			||||||
 | 
					        type: bool
 | 
				
			||||||
 | 
					        default: 'False'
 | 
				
			||||||
 | 
					      limit:
 | 
				
			||||||
 | 
					        type: int
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      not_rooms:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      not_senders:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      not_types:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      rooms:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      senders:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      types:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      unread_thread_notifications:
 | 
				
			||||||
 | 
					        type: bool
 | 
				
			||||||
 | 
					        default: 'False'
 | 
				
			||||||
 | 
					  StateFilter:
 | 
				
			||||||
 | 
					    description: Filter that describes which events to include/exclude in a Matrix room.
 | 
				
			||||||
 | 
					    fields:
 | 
				
			||||||
 | 
					      contains_url:
 | 
				
			||||||
 | 
					        type: bool
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      include_redundant_members:
 | 
				
			||||||
 | 
					        type: bool
 | 
				
			||||||
 | 
					        default: 'False'
 | 
				
			||||||
 | 
					      lazy_load_members:
 | 
				
			||||||
 | 
					        type: bool
 | 
				
			||||||
 | 
					        default: 'False'
 | 
				
			||||||
 | 
					      limit:
 | 
				
			||||||
 | 
					        type: int
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      not_rooms:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      not_senders:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      not_types:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      rooms:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      senders:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      types:
 | 
				
			||||||
 | 
					        type: '[string]'
 | 
				
			||||||
 | 
					        required: false
 | 
				
			||||||
 | 
					      unread_thread_notifications:
 | 
				
			||||||
 | 
					        type: bool
 | 
				
			||||||
 | 
					        default: 'False'
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,10 @@ For example, this is used to store events by their event id, or store rooms by t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-}
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Dict exposing (Dict)
 | 
					import FastDict as Dict exposing (Dict)
 | 
				
			||||||
 | 
					import Json.Decode as D
 | 
				
			||||||
 | 
					import Json.Encode as E
 | 
				
			||||||
 | 
					import Hash exposing (Hash)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Hashdict a
 | 
					type Hashdict a
 | 
				
			||||||
    = Hashdict
 | 
					    = Hashdict
 | 
				
			||||||
| 
						 | 
					@ -20,6 +22,12 @@ empty : (a -> String) -> Hashdict a
 | 
				
			||||||
empty hash =
 | 
					empty hash =
 | 
				
			||||||
    Hashdict { hash = hash, values = Dict.empty }
 | 
					    Hashdict { hash = hash, values = Dict.empty }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					encode : Hashdict E.Value -> E.Value
 | 
				
			||||||
 | 
					encode (Hashdict h) =
 | 
				
			||||||
 | 
					    h.values
 | 
				
			||||||
 | 
					        |> Dict.toList
 | 
				
			||||||
 | 
					        |> E.object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fromList : (a -> String) -> List a -> Hashdict a
 | 
					fromList : (a -> String) -> List a -> Hashdict a
 | 
				
			||||||
fromList hash xs =
 | 
					fromList hash xs =
 | 
				
			||||||
| 
						 | 
					@ -46,6 +54,9 @@ keys : Hashdict a -> List String
 | 
				
			||||||
keys (Hashdict h) =
 | 
					keys (Hashdict h) =
 | 
				
			||||||
    Dict.keys h.values
 | 
					    Dict.keys h.values
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					toList : Hashdict a -> List (String, a)
 | 
				
			||||||
 | 
					toList (Hashdict h) =
 | 
				
			||||||
 | 
					    Dict.toList h.values
 | 
				
			||||||
 | 
					
 | 
				
			||||||
union : Hashdict a -> Hashdict a -> Hashdict a
 | 
					union : Hashdict a -> Hashdict a -> Hashdict a
 | 
				
			||||||
union (Hashdict h1) (Hashdict h2) =
 | 
					union (Hashdict h1) (Hashdict h2) =
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					module Internal.Tools.Iddict exposing (..)
 | 
				
			||||||
 | 
					{-| The id-dict stores values and gives them a unique id.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import FastDict as Dict exposing (Dict)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Iddict a
 | 
				
			||||||
 | 
					    = Iddict 
 | 
				
			||||||
 | 
					        { cursor : Int
 | 
				
			||||||
 | 
					        , dict : Dict Int a
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					empty : Iddict a
 | 
				
			||||||
 | 
					empty =
 | 
				
			||||||
 | 
					    Iddict
 | 
				
			||||||
 | 
					        { cursor = 0
 | 
				
			||||||
 | 
					        , dict = Dict.empty
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					get : Int -> Iddict a -> Maybe a
 | 
				
			||||||
 | 
					get k (Iddict { dict }) =
 | 
				
			||||||
 | 
					    Dict.get k dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					insert : a -> Iddict a -> (Int, Iddict a)
 | 
				
			||||||
 | 
					insert v (Iddict d) =
 | 
				
			||||||
 | 
					    ( d.cursor
 | 
				
			||||||
 | 
					    , Iddict { cursor = d.cursor + 1, dict = Dict.insert d.cursor v d.dict }
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keys : Iddict a -> List Int
 | 
				
			||||||
 | 
					keys (Iddict { dict }) =
 | 
				
			||||||
 | 
					    Dict.keys dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					remove : Int -> Iddict a -> Iddict a
 | 
				
			||||||
 | 
					remove k (Iddict d) =
 | 
				
			||||||
 | 
					    Iddict { d | dict = Dict.remove k d.dict }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					values : Iddict a -> List a
 | 
				
			||||||
 | 
					values (Iddict { dict }) =
 | 
				
			||||||
 | 
					    Dict.values dict
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					module Internal.Tools.VaultResult exposing (..)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Internal.Tools.Exceptions as X
 | 
				
			||||||
 | 
					import Task exposing (Task)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Info b a
 | 
				
			||||||
 | 
					    = Info a
 | 
				
			||||||
 | 
					    | NoInfo
 | 
				
			||||||
 | 
					    | InfoFailed { status : LoadingError, retry : Task X.Error b }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LoadingError
 | 
				
			||||||
 | 
					    = NeverRequested
 | 
				
			||||||
| 
						 | 
					@ -1,23 +1,113 @@
 | 
				
			||||||
module Internal.Values.Timeline exposing (..)
 | 
					module Internal.Values.Timeline exposing (..)
 | 
				
			||||||
 | 
					{-| The Timeline can be very complex, and it can be represented in surprisingly
 | 
				
			||||||
{-| This module shapes the Timeline type used to keep track of timelines in Matrix rooms.
 | 
					complex manners. This module aims to provide one single Timeline type that
 | 
				
			||||||
 | 
					accepts the complex pieces of information from the API and contain it all in
 | 
				
			||||||
 | 
					a simple way to view events.
 | 
				
			||||||
-}
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import FastDict as Dict exposing (Dict)
 | 
				
			||||||
import Internal.Config.Leaking as Leaking
 | 
					import Internal.Config.Leaking as Leaking
 | 
				
			||||||
import Internal.Tools.Fold as Fold
 | 
					import Internal.Tools.Fold as Fold
 | 
				
			||||||
import Internal.Values.Event as Event exposing (IEvent)
 | 
					import Internal.Values.Event as Event exposing (IEvent)
 | 
				
			||||||
import Internal.Values.StateManager as StateManager exposing (StateManager)
 | 
					import Internal.Values.StateManager as StateManager exposing (StateManager)
 | 
				
			||||||
 | 
					import Internal.Tools.DefaultDict as DefaultDict exposing (DefaultDict)
 | 
				
			||||||
 | 
					import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
 | 
				
			||||||
 | 
					import Internal.Tools.Iddict as Iddict exposing (Iddict)
 | 
				
			||||||
 | 
					import Internal.Tools.Filters.Main as Filter exposing (Filter)
 | 
				
			||||||
 | 
					import Internal.Config.Leaking exposing (nextBatch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| The Timeline is a comprehensive object describing a timeline in a room.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Any Timeline type contains the following pieces of information:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `events` Comprehensive dictionary containing all locally stored timeline events
 | 
				
			||||||
 | 
					- `batches` Comprehensive dictionary containing all batches. Batches are pieces 
 | 
				
			||||||
 | 
					    of the timeline that have been sent by the homeserver.
 | 
				
			||||||
 | 
					- `token` Dictionary that maps for each batch token which batches it borders
 | 
				
			||||||
 | 
					- `mostRecentSync` Id of the most "recent" batch in the timeline
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
type Timeline
 | 
					type Timeline
 | 
				
			||||||
    = Timeline
 | 
					    = Timeline
 | 
				
			||||||
        { prevBatch : String
 | 
					        { events : Hashdict IEvent
 | 
				
			||||||
        , nextBatch : String
 | 
					        , batches : Iddict TimelineBatch
 | 
				
			||||||
        , events : List IEvent
 | 
					        , token : DefaultDict String (List Int)
 | 
				
			||||||
        , stateAtStart : StateManager
 | 
					        , mostRecentSync : Maybe Int
 | 
				
			||||||
        , previous : BeforeTimeline
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| A BatchToken is a token that has been handed out by the server to mark the end of a  -}
 | 
				
			||||||
 | 
					type alias BatchToken = String
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type alias TimelineBatch =
 | 
				
			||||||
 | 
					    { prevBatch : List Batch
 | 
				
			||||||
 | 
					    , nextBatch : List Batch
 | 
				
			||||||
 | 
					    , filter : Filter
 | 
				
			||||||
 | 
					    , events : List String
 | 
				
			||||||
 | 
					    , stateDelta : StateManager
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Batch
 | 
				
			||||||
 | 
					    = Token BatchToken
 | 
				
			||||||
 | 
					    | Batch Int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					addNewSync :
 | 
				
			||||||
 | 
					    { events : List IEvent
 | 
				
			||||||
 | 
					    , filter : Filter
 | 
				
			||||||
 | 
					    , limited : Bool
 | 
				
			||||||
 | 
					    , nextBatch : String
 | 
				
			||||||
 | 
					    , prevBatch : String
 | 
				
			||||||
 | 
					    , stateDelta : Maybe StateManager
 | 
				
			||||||
 | 
					    } -> Timeline -> Timeline
 | 
				
			||||||
 | 
					addNewSync data (Timeline timeline) =
 | 
				
			||||||
 | 
					    let
 | 
				
			||||||
 | 
					        batchToInsert : TimelineBatch
 | 
				
			||||||
 | 
					        batchToInsert =
 | 
				
			||||||
 | 
					            { prevBatch = 
 | 
				
			||||||
 | 
					                [ Just <| Token data.prevBatch
 | 
				
			||||||
 | 
					                , Maybe.map Batch timeline.mostRecentSync
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					                    |> List.filterMap identity
 | 
				
			||||||
 | 
					            , nextBatch =
 | 
				
			||||||
 | 
					                [ Token data.nextBatch ]
 | 
				
			||||||
 | 
					            , filter = data.filter
 | 
				
			||||||
 | 
					            , events = List.map Event.eventId data.events
 | 
				
			||||||
 | 
					            , stateDelta = Maybe.withDefault StateManager.empty data.stateDelta
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					    in
 | 
				
			||||||
 | 
					        case Iddict.insert batchToInsert timeline.batches of
 | 
				
			||||||
 | 
					            ( batchId, batches ) ->
 | 
				
			||||||
 | 
					                Timeline
 | 
				
			||||||
 | 
					                    { events = List.foldl Hashdict.insert timeline.events data.events
 | 
				
			||||||
 | 
					                    , batches = batches
 | 
				
			||||||
 | 
					                    , mostRecentSync = Just batchId
 | 
				
			||||||
 | 
					                    , token =
 | 
				
			||||||
 | 
					                        timeline.token
 | 
				
			||||||
 | 
					                            |> DefaultDict.update data.prevBatch
 | 
				
			||||||
 | 
					                                (\value ->
 | 
				
			||||||
 | 
					                                    case value of
 | 
				
			||||||
 | 
					                                        Just v ->
 | 
				
			||||||
 | 
					                                            Just (batchId :: v)
 | 
				
			||||||
 | 
					                                        Nothing ->
 | 
				
			||||||
 | 
					                                            Just [ batchId ]
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                            |> DefaultDict.update data.nextBatch
 | 
				
			||||||
 | 
					                                (\value ->
 | 
				
			||||||
 | 
					                                    case value of
 | 
				
			||||||
 | 
					                                        Just v ->
 | 
				
			||||||
 | 
					                                            Just (batchId :: v)
 | 
				
			||||||
 | 
					                                        Nothing ->
 | 
				
			||||||
 | 
					                                            Just [ batchId ]
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- type Timeline
 | 
				
			||||||
 | 
					--     = Timeline
 | 
				
			||||||
 | 
					--         { prevBatch : String
 | 
				
			||||||
 | 
					--         , nextBatch : String
 | 
				
			||||||
 | 
					--         , events : List IEvent
 | 
				
			||||||
 | 
					--         , stateAtStart : StateManager
 | 
				
			||||||
 | 
					--         , previous : BeforeTimeline
 | 
				
			||||||
 | 
					--         }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type BeforeTimeline
 | 
					type BeforeTimeline
 | 
				
			||||||
    = Endless String
 | 
					    = Endless String
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -432,3 +432,8 @@ sync vault onResponse =
 | 
				
			||||||
rooms : Vault -> List Room.Room
 | 
					rooms : Vault -> List Room.Room
 | 
				
			||||||
rooms =
 | 
					rooms =
 | 
				
			||||||
    Snackbar.mapList Internal.getRooms
 | 
					    Snackbar.mapList Internal.getRooms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					settings : (Snackbar.Settings -> Snackbar.Settings) -> Vault -> Vault
 | 
				
			||||||
 | 
					settings =
 | 
				
			||||||
 | 
					    Snackbar.updateSettings
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,10 +33,8 @@ interact with the API.
 | 
				
			||||||
import Internal.Api.VaultUpdate as Api
 | 
					import Internal.Api.VaultUpdate as Api
 | 
				
			||||||
import Internal.Invite exposing (RoomInvite)
 | 
					import Internal.Invite exposing (RoomInvite)
 | 
				
			||||||
import Internal.Room exposing (Room)
 | 
					import Internal.Room exposing (Room)
 | 
				
			||||||
import Internal.Tools.Exceptions as X
 | 
					 | 
				
			||||||
import Internal.Vault
 | 
					import Internal.Vault
 | 
				
			||||||
import Json.Encode as E
 | 
					import Json.Encode as E
 | 
				
			||||||
import Task exposing (Task)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{-| The Matrix API requires you to keep track of a lot of tokens, keys, values and more.
 | 
					{-| The Matrix API requires you to keep track of a lot of tokens, keys, values and more.
 | 
				
			||||||
| 
						 | 
					@ -58,6 +56,13 @@ translate those instructions to a `VaultUpdate` that you can feed to your `Vault
 | 
				
			||||||
type alias VaultUpdate =
 | 
					type alias VaultUpdate =
 | 
				
			||||||
    Api.VaultUpdate
 | 
					    Api.VaultUpdate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| After evaluating an update, the `Status` type tells you how the Vault.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					type Status
 | 
				
			||||||
 | 
					    = Good
 | 
				
			||||||
 | 
					    | Warning String ((VaultUpdate -> msg) -> Cmd msg)
 | 
				
			||||||
 | 
					    | Break String ((VaultUpdate -> msg) -> Cmd msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{-| Create a new vault based on an access token.
 | 
					{-| Create a new vault based on an access token.
 | 
				
			||||||
Keep in mind that access tokens might eventually be revoked or expire,
 | 
					Keep in mind that access tokens might eventually be revoked or expire,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,9 +68,7 @@ allowed for every room admin.
 | 
				
			||||||
import Internal.Api.VaultUpdate exposing (VaultUpdate)
 | 
					import Internal.Api.VaultUpdate exposing (VaultUpdate)
 | 
				
			||||||
import Internal.Event as Event
 | 
					import Internal.Event as Event
 | 
				
			||||||
import Internal.Room as Internal
 | 
					import Internal.Room as Internal
 | 
				
			||||||
import Internal.Tools.Exceptions as X
 | 
					 | 
				
			||||||
import Json.Decode as D
 | 
					import Json.Decode as D
 | 
				
			||||||
import Task exposing (Task)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{-| A room represents a channel of communication within a Matrix home server.
 | 
					{-| A room represents a channel of communication within a Matrix home server.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,10 +32,8 @@ Once you have the event you want, you can explore it with the following function
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Internal.Api.VaultUpdate exposing (VaultUpdate)
 | 
					import Internal.Api.VaultUpdate exposing (VaultUpdate)
 | 
				
			||||||
import Internal.Invite as Internal
 | 
					import Internal.Invite as Internal
 | 
				
			||||||
import Internal.Tools.Exceptions as X
 | 
					 | 
				
			||||||
import Internal.Values.RoomInvite as IR
 | 
					import Internal.Values.RoomInvite as IR
 | 
				
			||||||
import Json.Encode as E
 | 
					import Json.Encode as E
 | 
				
			||||||
import Task exposing (Task)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{-| The `RoomInvite` type serves as an invite to a given room.
 | 
					{-| The `RoomInvite` type serves as an invite to a given room.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					module Matrix.Settings exposing (..)
 | 
				
			||||||
 | 
					{-| There are a lot of settings that you can change!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					These settings change how the Vault interacts with the Matrix API.
 | 
				
			||||||
 | 
					You can adjust these values for performance reasons, for customizability, benchmarking,
 | 
				
			||||||
 | 
					or maybe just because you like it. :)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It is common to set all settings in the `init` function, but you can adjust all settings on the fly.
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Internal.Vault exposing (Vault)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-| When your Matrix client synchronizes with the homeserver, the homeserver often
 | 
				
			||||||
 | 
					responds quite quickly, giving all the information that you need.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sometimes, the homeserver has nothing new to report, and instead makes you wait for a response.
 | 
				
			||||||
 | 
					This is called long-polling, and it's the homeserver waiting for an update to give to you.
 | 
				
			||||||
 | 
					Long-polling is very useful!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This setting sets a limit on how long the long-polling should last. It is smart
 | 
				
			||||||
 | 
					to make this equal to the interval at which you run the `sync` function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Default:** 10 (seconds)
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					syncTimeout : Int -> Vault -> Vault
 | 
				
			||||||
 | 
					syncTimeout timeout =
 | 
				
			||||||
 | 
					    Internal.Vault.settings \data -> { data | syncTimeout = timeout }
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					# Design refactors to be made
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The following refactors are to be made:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- We need an `Info` type that informs the user whether data exists, and if not, why it doesn't exist.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- We need a `Timeline` type that users can use to go through threads in the global room timeline.
 | 
				
			||||||
		Loading…
	
		Reference in New Issue