Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Isabelle/Tools/Find_Facts/web/src/   (Beweissystem Isabelle Version 2025-1©)  Datei vom 16.11.2025 mit Größe 10 kB image not shown  

Quelle  Main.elm   Sprache: unbekannt

 
{- Author: Fabian Huch, TU München

Find Facts application.
-}
module Main exposing (init, main, subscriptions, update, view)


import About
import Delay exposing (Delay)
import Details
import Html.Attributes exposing (href, style)
import Html.Events as Events
import Html.Lazy as Lazy
import Http
import Browser
import Browser.Navigation as Navigation
import Html exposing (..)
import Json.Decode as Decode
import Material.Theme as Theme
import Material.TopAppBar as TopAppBar
import Material.Typography as Typography
import Query exposing (Query, Query_Blocks)
import Results
import Searcher
import Url exposing (Url)
import Url.Builder
import Utils


{- main -}

main: Program () Model Msg
main =
 Browser.application {init = init, view = view, update = update, subscriptions = subscriptions,
   onUrlChange = Url_Changed, onUrlRequest = Link_Clicked}


{- model -}

type Page = Not_Found | About | Detail Details.Model | Search Searcher.Model Query Results.Model

home: Page
home = Search Searcher.empty Query.empty Results.empty

type alias Model = {nav_key: Navigation.Key, url: Url, page: Page, delay: Delay.Model}

should_query : Maybe Query -> Query -> Bool
should_query query query1 = query /= Just query1 && not (Query.empty_query query1)

populate: Page -> Searcher.Model -> Page
populate page0 searcher =
  case page0 of
    Search searcher0 query0 results0 ->
      let
        query = Searcher.get_query searcher
        results =
          if should_query (Just query0) query then Results.loading
          else if Query.empty_query query then Results.empty
          else results0
      in Search (Searcher.populate searcher0 searcher) query results
    _ ->
     let
       query = Searcher.get_query searcher
       results0 = Results.empty
       results = if should_query Nothing query then Results.loading else results0
     in Search searcher query results

init: () -> Url -> Navigation.Key -> (Model, Cmd Msg)
init _ url key =
  case url.fragment of
    Nothing -> (Model key url home Delay.empty, home |> url_encode url |> push_url False key)
    Just fragment ->
      let
        page = fragment_decode (populate Not_Found) fragment
        cmd =
          case page of
            Search _ query _ ->
              if should_query Nothing query then get_result query else Cmd.none
            Detail details -> get_block (Details.get_id details)
            _ -> Cmd.none
      in (Model key url page Delay.empty, cmd)


{- url encoding/decoding -}
{- NB: routing only in URL fragment. -}

aboutN = "about"
searchN = "search"

url_encode: Url -> Page -> Url
url_encode url page =
  case page of
    Not_Found -> {url | fragment = Nothing}
    About -> {url | fragment = Just aboutN}
    Detail details ->
      {url | fragment = Just (Details.get_id details)}
    Search searcher _ _ ->
      let params = Searcher.params searcher
      in {url | fragment = Just (searchN ++ (Url.Builder.toQuery params))}

fragment_decode : (Searcher.Model -> Page) -> String -> Page
fragment_decode make_page fragment =
  case String.split "?" fragment of
    [] -> Not_Found
    page :: qs ->
      let
        parse parser f =
          Utils.parse_query (String.join "?" qs) parser
          |> Maybe.map f
          |> Maybe.withDefault Not_Found
      in
        if page == aboutN && List.isEmpty qs then About
        else if page == searchN then parse Searcher.parser make_page
        else if List.isEmpty qs then Detail (Details.init page)
        else Not_Found

url_decode: Url -> Page -> Page
url_decode url page0 =
  case url.fragment of
    Nothing -> home
    Just fragment -> fragment_decode (populate page0) fragment


{- commands -}

push_url: Bool -> Navigation.Key -> Url -> Cmd msg
push_url save key url =
  (if save then Navigation.pushUrl else Navigation.replaceUrl) key (Url.toString url)

get_result: Query -> Cmd Msg
get_result query =
  Http.post {url="api/query", expect = Http.expectJson (Query_Result query) Query.decode_result,
    body = query |> Query.encode_query |> Http.jsonBody}

query_delay: Query -> Delay Msg
query_delay query = {name = "query", delay = 500, event = get_result query}

get_blocks: Query -> String -> Cmd Msg
get_blocks query cursor =
  Http.post {url = "api/blocks", expect = Http.expectJson (Query_Blocks query) Query.decode_blocks,
    body = {query = query, cursor = cursor} |> Query.encode_query_blocks |> Http.jsonBody}

get_block: String -> Cmd Msg
get_block id =
  Http.post {url = "api/block", expect = Http.expectJson (Query_Block id) Query.decode_block,
    body = id |> Query.encode_query_block |> Http.jsonBody}


{- update -}

type alias Scroll_Info = {pos: Float, top: Float, height: Float}
type Msg =
  Link_Clicked Browser.UrlRequest |
  Url_Changed Url |
  Searcher_Msg Searcher.Msg |
  Delay_Msg (Delay Msg) |
  Results_Msg (Results.Msg) |
  Query_Result Query (Result Http.Error Query.Result) |
  Query_Blocks Query (Result Http.Error Query.Blocks) |
  Query_Block String (Result Http.Error Query.Block) |
  Scroll_Event Scroll_Info

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    Link_Clicked urlRequest ->
      case urlRequest of
        Browser.Internal url ->
          if url.path == model.url.path then (model, push_url True model.nav_key url)
          else (model, Navigation.load (Url.toString url))
        Browser.External href -> (model, Navigation.load href)

    Url_Changed url ->
      let
        query0 =
          case model.page of
            Search _ query _ -> Just query
            _ -> Nothing
        page = url_decode url model.page
        (delay, cmd) =
          case page of
            Search _ query _ ->
              if should_query query0 query then Delay.now model.delay (query_delay query)
              else (model.delay, Cmd.none)
            Detail details -> (model.delay, get_block (Details.get_id details))
            _ -> (model.delay, Cmd.none)
      in ({model | url = url, delay = delay, page = page}, cmd)

    Delay_Msg msg1 ->
      let (delay, cmd) = Delay.update model.delay msg1
      in ({model | delay = delay}, cmd)

    Searcher_Msg msg1 ->
      case model.page of
        Search searcher _ results ->
          let
            (searcher1, update_search) = Searcher.update msg1 searcher
            ((delay, search_cmd), query1, results1) =
              case update_search of
                Searcher.None query -> ((model.delay, Cmd.none), query, results)
                Searcher.Empty query ->
                  ((Delay.cancel model.delay (query_delay query), Cmd.none), query, Results.empty)
                Searcher.Later query ->
                  (Delay.later Delay_Msg model.delay (query_delay query), query, Results.loading)
                Searcher.Now query ->
                  (Delay.now model.delay (query_delay query), query, Results.loading)
            page = Search searcher1 query1 results1
            nav_cmd = url_encode model.url page |> push_url False model.nav_key
          in
            ({model | page = page, delay = delay}, Cmd.batch [nav_cmd, search_cmd])
        _ -> (model, Cmd.none)

    Results_Msg (Results.Selected id) ->
      (model, url_encode model.url (id |> Details.init |> Detail) |> push_url True model.nav_key)

    Query_Result query res ->
      case model.page of
        Search searcher query1 _ ->
          case res of
            Result.Ok result ->
              if query /= query1 then (model, Cmd.none)
              else
                let
                  searcher1 = Searcher.set_result result searcher
                  results = Results.result result
                in ({model | page = Search searcher1 query1 results}, Cmd.none)
            Result.Err err ->
              ({model | page = Search searcher query (Results.error err)}, Cmd.none)
        _ -> (model, Cmd.none)

    Query_Blocks query res ->
      case model.page of
        Search searcher query1 results ->
          if query /= query1 then (model, Cmd.none)
          else ({model | page = Search searcher query (Results.set_loaded res results)}, Cmd.none)
        _ -> (model, Cmd.none)

    Query_Block id res ->
      case model.page of
        Detail details ->
          if id /= (Details.get_id details) then (model, Cmd.none)
          else ({model | page = Detail (Details.set_loaded res details)}, Cmd.none)
        _ -> (model, Cmd.none)

    Scroll_Event scroll ->
      if (scroll.pos - scroll.top) > scroll.height then (model, Cmd.none)
      else
        case model.page of
          Search searcher query results ->
            case Results.get_maybe_cursor results of
              Nothing -> (model, Cmd.none)
              Just cursor ->
                let results1 = Results.set_load_more results
                in ({model | page = Search searcher query results1}, get_blocks query cursor)
          _ -> (model, Cmd.none)



{- subscriptions -}

subscriptions: Model -> Sub Msg
subscriptions model =
  case model.page of
    Search searcher _ _ -> Searcher.subscriptions searcher |> Sub.map Searcher_Msg
    _ -> Sub.none


{- view -}

decode_scroll : Decode.Decoder Msg
decode_scroll =
  Decode.map Scroll_Event (
    Decode.map3 Scroll_Info
      (Decode.at ["target", "scrollHeight"] Decode.float)
      (Decode.at ["target", "scrollTop"] Decode.float)
      (Decode.at ["target", "offsetHeight"] Decode.float))

view: Model -> Browser.Document Msg
view model =
  let
    view_navlink attrs page name =
      a (attrs ++ [href ("#" ++ page), style "margin" "0 16px", Theme.onPrimary])
        [text name]
    (title, content) = case model.page of
      Not_Found -> ("404 Not Found", [h2 [Typography.headline4] [text "404 Not Found"]])
      About -> ("Find Facts | About", [About.view |> Html.map never])
      Detail details -> ("Find Facts | Details", [Details.view details |> Html.map never])
      Search searcher _ results -> ("Find Facts | Search", [
        searcher |> Lazy.lazy Searcher.view |> Html.map Searcher_Msg,
        results |> Lazy.lazy Results.view |> Html.map Results_Msg])
  in {
    title = title,
    body = [
      div [style "height" "100vh", Events.on "scroll" decode_scroll, style "overflow" "scroll"] [
        TopAppBar.regular
          (TopAppBar.config
           |> TopAppBar.setDense True
           |> TopAppBar.setAttributes [style "position" "relative"])
          [TopAppBar.row [style "max-width" "1200px", style "margin" "0 auto"]
            [TopAppBar.section [TopAppBar.alignStart]
              [view_navlink [TopAppBar.title] searchN "Find Facts"],
              TopAppBar.section [TopAppBar.alignEnd]
                [view_navlink [Typography.button] searchN "Search",
                 view_navlink [Typography.button] aboutN "About"]]],
        div
          [style "padding-left" "16px", style "padding-right" "16px", style "max-width" "1200px",
           style "margin" "0 auto"]
          content]]}

[ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ]