0.7.0
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:
- Providing a
NoOp
message (meaning 'no operation') - Implementing the
router
function that does nothing, returning theNoOp
. - 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
- #193 CSS properties in camel-case by @krined-dev in #194
- Fix typo "Tyrian > Architecture & Patterns" by @corem in #196
- Frontend Routing by @davesmith00000 in #197
- Main method launcher #198 by @stevechy in #200
- Take 2: refactor the entire runtime instead ;) by @armanbilge in #171
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