Skip to content

0.7.0

Compare
Choose a tag to compare
@davesmith00000 davesmith00000 released this 20 May 10:23
· 105 commits to main since this release

Thanks!

As always I would like to start with a thank you to all those of have contributed to making this release happen, either directly or indirectly. Everyone who engages with the project in anyway makes a difference, and all your support is appreciated. πŸ’œ

Migration example

Doing the most minimal migration to Tyrian 0.7.0 from previous versions is a fairly straightforward process of:

  1. Providing a NoOp message (meaning 'no operation')
  2. Implementing the router function that does nothing, returning the NoOp.
  3. Handling the NoOp in your app update by simply returning (model, Cmd.None) when it is encountered.

Examples can be found here.

Summary of key changes

Built-in Frontend Routing

At long last, frontend routing has come to Tyrian! ...whether you like it or not!

All Tyrian apps, along side the usual standard functions, will now need to implement a routing function:

def router: Location => Msg

Aside from organising suitable messages to tell your app what to do when the location changes, this is the only modification you will need to make. All the other plumbing is done for you in the background.

There are a few ways to implement the router function. Please note that in all cases, you are told if the link is considered internal or external and they are treated separately.

If your needs are simple, then the easiest way to implement the router is to use the helpers in the Routing module. For example, this one ignores all internal links and just routes external ones. Again, note that you need to provide suitable custom Msg's:

def router: Location => Msg = Routing.externalOnly(Msg.NoOp, Msg.FollowLink(_))

There are also Routing.basic (simple string match) and Routing.none variations.

Alternatively, you can do a full route match, like this:

  def router: Location => Msg =
    case loc: Location.Internal =>
      loc.pathName match
        case "/"      => Msg.NavigateTo(Page.Page1)
        case "/page1" => Msg.NavigateTo(Page.Page1)
        case "/page2" => Msg.NavigateTo(Page.Page2)
        case "/page3" => Msg.NavigateTo(Page.Page3)
        case "/page4" => Msg.NavigateTo(Page.Page4)
        case "/page5" => Msg.NavigateTo(Page.Page5)
        case "/page6" => Msg.NavigateTo(Page.Page6)
        case _        => Msg.NoOp

    case loc: Location.External =>
      Msg.NavigateToUrl(loc.href)

The Location type comes with all sorts of information in it for you to decide how to route, it is similar to the JavaScript Location type.

The expectation is that you could also decide to pull in some fancy url parsing library to build a clever router here, if you feel like it.

Once you've done your routing, you'll want to use some of the new Nav cmds. For internal links, you can use Nav.pushUrl to update the address bar (this does not tell the browser to change locations, it just adjusts the history and what is displayed in the address bar), and for external links, you can use Nav.loadUrl to tell the browser to follow the link.

Please note that the old Navigation module that provided simple hash based routing has now been deprecated, since the new work makes it redundant.

Happy routing!

Runtime re-write & Performance

TL;DR: Tyrian's start up is much faster. Code defensively when using methods like getElementById, i.e. make sure the element exists before you try and use it.

This version of Tyrian moves to Cats Effect 3.5 (still works with both IO and ZIO), which thanks to wizardry beyond mortal comprehension, has made Tyrian both lighter in terms of memory consumption and substantially faster in general.

Tyrian's tiny runtime has also been completely re-written to a more idiomatic form, all credit to @armanbilge for this accomplishment.

Taken together, performance in general is theoretically better, although most people won't notice under normal usage. The part that is noticeable is start up time and resource consumption stability: Tyrian apps are now much quicker off the mark than they used to be!

While this is a very welcome development, it does cause a new, high-quality problem. In situations where an app starts up and looks up an element that is dynamically created by the app itself, there is a good chance that the element will not be immediately ready anymore. This didn't used to be a problem because Tyrian was just a bit slower than the renderer, but there is now a suspicion that things were working more by coincidence than design.

Practically, this means we need to code defensively, as we all probably should have been doing anyway. Specifically that means that if you're going to look up an element by ID (for instance), that's fine, but don't just use it, check it exists first. If it doesn't exist, you'll need a simple retry mechanism.

Main method launcher

You can now embed a Tyrian app into a web page without needing to use JavaScript to call it's launch method.

This is thanks to some excellent work by @stevechy, who has also provided an example of launching two apps at the same time, with data properties.

Here is how the example embeds the two apps, notice that data attributes are being used to supply flags too!

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Main Launcher Example</title>
  <script type="module" src="./target/scala-3.2.2/main-launcher-fastopt.js"></script>
</head>
<body>
  <h1>Main launcher example</h1>
  <div data-tyrian-app="CounterApp" data-tyrian-flag-initial-counter="42"></div>
  <div data-tyrian-app="ChatApp" data-tyrian-flag-initial-message="Hello"></div>
</body>
</html>

Better CSS property name generation

A very welcome usability improvement by @krined-dev. All CSS properties are now generated in both camel and kebab case.

For example, you can use CSS.`background-image` , as you could previously, but now you can also use the more convenient: CSS.backgroundImage.


What's Changed

New Contributors

  • @krined-dev made their first contribution in #194
  • @corem made their first contribution in #196
  • @stevechy made their first contribution in #200

Full Changelog: v0.6.2...v0.7.0