# Janet's Shenanigans

## The Cool Now

With the infrastructure I built in qml-lsp for parsing and analysing QML files, I thought “hm, since doxyqml is just a glorified qml parser –> c++ header file converter, wouldn't it be trivial to write the same thing in go reusing qml-lsp's infrastructure?” And that's exactly what I did. I wrote a 130-line program that faithfully replicated doxyqml's functionality in Go.

By virtue of being a Go program that calls on a pretty optimised parser in C, it ended up being a little over 10 times faster than doxyqml on my system.

I wasn't done there.

I thought “hmm, couldn't I reuse the semantic analysis I did for qml-lsp to improve the output a bit?”

So, that's pretty much what I did.

Currently, the most notable improvement over doxyqml is in producing a better superclass for the output:

doxyqml, with aliased import (import foo as bar):

class Avatar : public QtQuick.Controls.Control {


doxyqml, without aliased import:

class Avatar : Control {


One isn't valid C++ (I'm surprised Doxygen takes it at all), and the other fails to specifically name where Control comes from, leading to issues with Doxygen trying to locate the superclass.

qml-doxygen reuses the semantic analysis from qml-lsp to generate the following output, whether the import is aliased or not:

class Avatar : public QtQuick::Controls::Control {


It's both valid C++, and tells Doxygen exactly where the name is coming from.

The next thing I'm planning to do is to resolve the concrete type of an alias property, so that documentation generation for aliases can be improved without developers needing to explicitly tell the computer what type the alias points to.

I may also add the ability to “splat” grouped properties with a special sigil, so that something like readonly property AvatarGroup actions: AvatarGroup { } can be expanded into the properties of the AvatarGroup by qml-doxygen, resulting in better documentation.

Tags: #libre

## qml-lsp

I worked on fancying up qml-lsp's underlying infrastructure and added some new features. One of the more noticeable things is that attached properties can complete now:

There's also some lints:

Currently there's some lints warning against usage of language features that negatively impair readability, (with statements, alias, etc.), warning about using anchors in a Layout, and unused imports.

Shades is a small utility app I made that lets you grab shades of a colour.

It uses the OKLAB colour space to generate shades, which means it produces really good variations on a colour that only differ in visual brightness instead of going “off-brand” like other colour spaces would have.

## Tok

Yeah, Tok just has the “saved messages” room display as such instead of using your account's name and avatar. Not much, which is why it's getting lumped in here instead of in its own blog post.

Tags: #libre

## Porting Tok To Not Linux: The Journey Of Incessant Failure

This response from a reader pretty much sums it up:

## Stop One: Will Craft Work?

Craft seems like the no-brainer for a KDE project to use, considering it's in-house and supports all of KDE's frameworks and has packaging capabilities. Unfortunately, what sounds good on paper does not translate to what's good in execution.

When I first checked it out, the current version being shipped had incorrect code that would have failed to compile had it been a compiled language instead of Python. Heck, even running a typechecker for Python would have revealed the fact that the code was trying to call a function that didn't exist. Yet, this managed to get shipped. Not a good first impression.

After manually patching in the function into existence on my system, I ran into another hurdle: Craft's env script is broken; mangling PATH to an extent where entries like /usr/bin and /bin and other things got just truncated into oblivion, resulting in a shell where you couldn't do much of anything.

After manually patching PATH to be not mangled, I ran into another and the final hurdle before I gave up: Craft tried to use half bundled libraries and tools and half system libraries and tools, resulting in dynamic linker errors from system tools not finding symbols they needed from bundled libraries.

When I brought these issues up in the Craft chat, the answers basically amounted to a lack of care and “go use Ubuntu.” Not acceptable for Tok considering most of the people interested in building Tok like this don't use Ubuntu, and honestly doesn't make you have much faith in a system for porting utilities to other platforms if said system doesn't even work across the distributions of one platform.

## Stop Two: Maybe Conan?

Conan seems like the second-in-line no-brainer for Tok to use. It's the largest C++ package manager, even supporting Qbs. Of course, like with Craft, what sounds good on paper doesn't match up to execution.

Out of the gate, I looked at the Qt package, only to find that there was one (1) Qt package for it consisting of the entirety of Qt, WebEngne and all. Kinda oof, but not a deal breaker. Well, it wouldn't be a dealbreaker if Conan had prebuilt Qt packages for

- Settings: arch=x86_64, build_type=Release, compiler=gcc, compiler.version=11, os=Linux


But it doesn't. I'm not going to build an entire web engine just for an attempt at maybe getting a non-Linux build of Tok, and having to build a web engine as part of Tok's CI is a no-go in terms of disk, memory and CPU.

## Stop Three: vcpkg

Considering Microsoft has been a developer tools company for about as thrice as long as I've been alive, I hope their take at the C++ package manager thing is worth their salt.

Some weirdness ensued with the VCPKG_ROOT environment variable at first, but it was easy to fix by pointing it at the vcpkg repo.

While doing the vcpkg install, I found the output somewhat hard to follow, so I had no idea how far along it was. I just let it sit since it seemed to be making progress.

While vcpkg didn't have prebuilt binaries for my setup, it didn't require building all Qt modules like Conan did, so the ask was much more reasonable.

And then I noticed a big issue: vcpkg has absolutely zero versioning, other than the git repository with all the package manifests. This essentially means that in order to build with Qt5, I need to commit to an ancient version of vcpkg packages and stay there indefinitely. I also have to ask users to roll back their vcpkg install that far to build Tok. Not really acceptable as an ask for people who might want to build Tok for not Linux.

## Stop Four: Wait, It's Already Here (At Least For Macs)

Turns out Nix, the thing that Tok already supports building with, also supports macOS. Well, that was easy. While it doesn't spit out a premade .app like other porting systems can do, it does ensure a working build with available dependencies, which is already most of the way there.

## Conclusion: Apples And Penguins Rule, Everyone Else Drools

Cross-platform C++ packaging and distribution is still a very unsolved problem, unlike other languages/frameworks like Go, Electron, Rust, Zig, etc. as I learned painfully through these escapades. Nix seems the most promising on this front, as it offers a very consistent environment across platforms, which gets you most of the way there in terms of building. It doesn't support Windows (yet?), but simply being able to use a functional compiler instead of Apple Clang is already a killer feature for using it to port apps to macOS.

Qbs is also a huge help in terms of the porting process, as it natively supports building things that require auxiliary scripts or build system hacks with other build systems, like .app bundles, Windows installers, and multi-architecture .app/.aab/.apks with just a few or no extra lines in the build system.

For Tok on macOS, all I need to do is add these two lines to the build script in order to get a usable .app file from it:

Depends { name: "bundle" }
bundle.isBundle: true


While it lacks a lot of metadata that you need to fill in yourself, it's again, another 99% of the way there solution where the remaining 1% is mostly just a little data or boilerplate or running a tool.

I still haven't figured out what I'll be doing for Windows, but the need for an end-user Windows package is still a long ways off, considering Tok is still nowhere near a 1.0 release status. Perhaps I can make leverage of Fedora's mingw packages or check out https://mxe.cc/, or maybe just install dependencies from source to a Windows system without a package manager, and bundle them during the build process. If you have any suggestions, do feel free to hop on by in the Tok chat and drop them.

Tags: #libre

## Introducing hRPC: a simple RPC system for user-facing APIs

This is the sequel post to the previous post on hRPC; after hRPC has matured and gotten an actual spec written for it. This post contains much more information about hRPC, as well as tutorials for it.

This post is mirrored from our post on dev.to

Co-authored by: Yusuf Bera Ertan (yusdacra@GitHub), Danil Korennykh (Bluskript@GitHub), Janet Blackquill (pontaoski@GitHub)

hRPC is a new RPC system that we, at Harmony, have been developing and using for our decentralized chat protocol. It uses Protocol Buffers (Protobufs) as a wire format, and supports streaming.

hRPC is primarily made for user-facing APIs and aims to be as simple to use as possible.

If you would like to learn more, the hRPC specification can be found here.

What is an RPC system? If you know traditional API models like REST, then you can think of RPC as a more integrated version of that. Instead of defining requests by endpoint and method, requests are defined as methods on objects or services. With good code generation, an RPC system is often easier and safer to use for both clients and servers.

## Why hRPC?

hRPC uses REST to model plain unary requests, and WebSockets to model streaming requests. As such, it should be easy to write a library for the languages that don't already support it.

hRPC features:

• Type safety
• Strict protocol conformance on both ends
• Easy streaming logic
• More elegant server and client code with interfaces/traits and endpoint generation.
• Cross-language code generation
• Smaller request sizes
• Faster request parsing

## Why Not Twirp?

Twirp and hRPC have a lot in common, but the key difference that makes Twirp a dealbreaker for harmony is its lack of support for streaming RPCs. Harmony's vision was to represent all endpoints in Protobuf format, and as a result Twirp became fundamentally incompatible.

## Why Not gRPC?

gRPC is the de-facto RPC system, in fact protobuf and gRPC come together a lot of the time. So the question is, why would you want to use hRPC instead?

Unfortunately, gRPC has many limitations, and most of them result from its low-level nature.

### The lack of web support

At Harmony, support for web-based clients was a must, as was keeping things simple to implement. gRPC had neither. As stated by gRPC: > It is currently impossible to implement the HTTP/2 gRPC spec in the browser, as there is simply no browser API with enough fine-grained control over the requests.

### The gRPC slowloris

gRPC streams are essentially just a long-running HTTP request. Whenever data needs to be sent, it just sends a new HTTP/2 frame. The issue with this, however, is that most reverse proxies do not understand gRPC streaming. At Harmony, it was fairly common for sockets to disconnect because they are idle for long stretches of time. NGINX and other reverse proxies would see these idle connections, and would close them, causing issues to all of our clients. hRPC's use of WebSockets solves this use-case, as reverse proxies are fully capable to understand them.

In general, with hRPC we retain the bulk of gRPC's advantages while simplifying stuff massively.

## Why not plain REST?

Protobuf provides a more compact binary format for requests than JSON. It lets the user to define a schema for their messages and RPCs which results in easy server and client code generation. Protobuf also has features that are very useful for these kind of schemas (such as extensions), and as such is a nice fit for hRPC.

## A Simple Chat Example

Let's try out hRPC with a basic chat example. This is a simple system that supports posting chat messages which are then streamed back to all clients. Here is the protocol:

syntax = "proto3";

package chat;

// Empty object which is used in place of nothing
message Empty { }

// Object that represents a chat message
message Message { string content = 1; }

service Chat {
// Endpoint to send a chat message
rpc SendMessage(Message) returns (Empty);
// Endpoint to stream chat messages
rpc StreamMessages(Empty) returns (stream Message);
}


By the end, this is what we will have:

### Getting Started

NOTE: If you don't want to follow along, you can find the full server example at hRPC examples repository.

Let's start by writing a server that implements this. We will use hrpc-rs, which is a Rust implementation of hRPC.

Note: If you don't have Rust installed, you can install it from the rustup website.

We get started with creating our project with cargo new chat-example --bin.

Now we will need to add a few dependencies to Cargo.toml:

[build-dependencies]
# hrpc-build will handle generating Protobuf code for us
# The features we enable here matches the ones we enable for hrpc
hrpc-build = { version = "0.29", features = ["server", "recommended"] }

[dependencies]
# prost provides us with protobuf decoding and encoding
prost = "0.9"
# hrpc is the hrpc-rs main crate!
# Enable hrpc's server features, and the recommended transport
hrpc = { version = "0.29", features = ["server", "recommended"] }
# tokio is the async runtime we use
# Enable tokio's macros so we can mark our main function, and enable multi
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
# tower-http is a collection of HTTP related middleware
tower-http = { version = "0.1", features = ["cors"] }
# Logging utilities
# tracing gives us the ability to log from anywhere we want
tracing = "0.1"
# tracing-subscriber gives us a terminal logger
tracing-subscriber = "0.3"


Don't forget to check if your project compiles with cargo check!

### Building the Protobufs

Now, let's get basic protobuf code generation working.

First, go ahead and copy the chat protocol from earlier into src/chat.proto.

After that we will need a build script. Make a file called build.rs in the root of the project:

// build.rs
fn main() {
// The path here is the path to our protocol file
// which we copied in the previous step!
//
// This will generate Rust code for our protobuf definitions.
hrpc_build::compile_protos("src/chat.proto")
.expect("could not compile the proto");
}


And lastly, we need to import the generated code:

// src/main.rs
// Our chat package generated code
pub mod chat {
// This imports all the generated code for you
hrpc::include_proto!("chat");
}

// This is empty for now!
fn main() { }


Now you can run cargo check to see if it compiles!

### Implementing the Protocol

In this section, we will implement the protocol endpoints.

First, get started by importing the stuff we will need:

// src/main.rs
// top of the file

// Import everything from chat package, and the generated
// server trait
use chat::{*, chat_server::*};
// Import the server prelude, which contains
// often used code that is used to develop servers.
use hrpc::server::prelude::*;


Now, let's define the business logic for the Chat server. This is a simple example, so we can just use channels from tokio::sync::broadcast. This will allow us to broadcast our chat messages to all clients connected.

// ... other use statements

// The channel we will use to broadcast our chat messages


Afterwards we can define our service state:

pub struct ChatService {
// The sender half of our broadcast channel.
//
// We will use it's .subscribe() method to get a
// receiver when a client connects.
}


Then we define a simple constructor:

impl ChatService {
// Creates a new ChatService
fn new() -> Self {
// Create a broadcast channel with a maximum 100
// amount of items that can be pending. This
// doesn't matter in our case, so the number is
// arbitrary.
let (tx, _) = broadcast::channel(100);
Self {
}
}
}


Now we need to implement the generated trait for our service:

impl Chat for ChatService {
// This corresponds to the SendMessage endpoint
//
// handler is a Rust macro that is used to transform
// an async fn into a properly typed hRPC trait method.
#[handler]
async fn send_message(&self, request: Request<Message>) -> ServerResult<Response<Empty>> {
// we will add this in a bit
}

// This corresponds to the StreamMessages endpoint
#[handler]
async fn stream_messages(
&self,
// We don't use the request here, so we can just ignore it.
// The leading _ stops Rust from complaining about unused
// variables!
_request: Request<()>,
socket: Socket<Message, Empty>,
) -> ServerResult<()> {
// we will add this in a bit
}
}


And now for the actual logic, let's start with message sending:

#[handler]
async fn send_message(&self, request: Request<Message>) -> ServerResult<Response<Empty>> {
// Extract the chat message from the request
let message = request.into_message().await?;

// Try to broadcast the chat message across the channel
// if it fails return an error
return Err(HrpcError::new_internal_server_error("couldn't broadcast message"));
}

// Log the message we just got
tracing::info!("got message: {}", message.content);

Ok((Empty {}).into_response())
}


Streaming logic is simple. Simply subscribe to the broadcast channel, and then read messages from that channel forever until there's an error:

#[handler]
async fn stream_messages(
&self,
_request: Request<()>,
socket: Socket<Message, Empty>,
) -> ServerResult<()> {
// Subscribe to the message broadcaster

// Poll for received messages...
while let Ok(message) = message_receiver.recv().await {
// ...and send them to client.
socket.send_message(message).await?;
}

Ok(())
}


Let's put all of this together in the main function. We'll make a new chat server, where we pass in our implementation of the service. We'll be serving using the Hyper HTTP transport for the server, although this can be swapped out with another transport if needed.

// ...other imports

// Import our CORS middleware
use tower_http::cors::CorsLayer;

// Import the Hyper HTTP transport for hRPC
use hrpc::server::transport::http::Hyper;

// tokio::main is a Rust macro that converts an async fn
// main function into a synchronous main function, and enables
// you to use the tokio async runtime. The runtime we use is the
// multithreaded runtime, which is what we want.
#[tokio::main]
async fn main() -> Result<(), BoxError> {
// Initialize the default logging in tracing-subscriber
// which is logging to the terminal
tracing_subscriber::fmt().init();

// Create our chat service
let service = ChatServer::new(ChatService::new());

// Create our transport that we will use to serve our service
let transport = Hyper::new("127.0.0.1:2289")?;

// Layer our transport for use with CORS.
// Since this is specific to HTTP, we use the transport's layer method.
//
// Note: A "layer" can simply be thought of as a middleware!
let transport = transport.layer(CorsLayer::permissive());

// Serve our service with our transport
transport.serve(service).await?;

Ok(())
}


Notice how in the code above, we needed to specify a CORS layer. The next step of the process, of course, is to write a frontend for this.

### Frontend (CLI)

If you don't want to use the web client example, you can try the CLI client at hRPC examples repository. Keep in mind that this post doesn't cover writing a CLI client.

To run it, after you git clone the repository linked, navigate to chat/tui-client and run cargo run. Instructions also available in the READMEs in the repository.

### Frontend (Vue 3 + Vite + TS)

NOTE: If you don't want to follow along, you can find the full web client example at hRPC examples repository.

The setup is a basic Vite project using the Vue template, with all of the boilerplate demo code removed. Once you have the project made, install the following packages:

npm i @protobuf-ts/runtime @protobuf-ts/runtime-rpc @harmony-dev/transport-hrpc

npm i -D @protobuf-ts/plugin @protobuf-ts/protoc windicss vite-plugin-windicss

In order to get Protobuf generation working, we'll use Buf, a tool specifically built for building protocol buffers. Start by making the following buf.gen.yaml:

version: v1
plugins:
- name: ts
out: gen
opt: generate_dependencies,long_type_string
path: ./node_modules/@protobuf-ts/plugin/bin/protoc-gen-ts


The config above invokes the code generator we installed, and enables a string representation for longs, and generating code for builtin google types too.

Now, paste the protocol from earlier into protocol/chat.proto in the root of the folder, and run buf generate ./protocol. If you see a gen folder appear, then the code generation worked! ✅

#### The Implementation

When building the UI, it's useful to have a live preview of our site. Run npm run dev in terminal which will start a new dev server.

The entire implementation will be done in src/App.vue, the main Vue component for the site.

For the business logic, we'll be using the new fancy and shiny Vue 3 script setup syntax. Start by defining it:

<script setup lang="ts">
</script>


Now, inside this block, we first create a chat client by passing our client configuration into the HrpcTransport constructor:

import { ChatClient } from "../gen/chat.client";
import { HrpcTransport } from "@harmony-dev/transport-hrpc";

const client = new ChatClient(
new HrpcTransport({
baseUrl: "http://127.0.0.1:2289",
insecure: true
})
);


Next, we will define a reactive list of messages, and content of the text input:

const content = ref("");
const msgs = reactive<string[]>([]);


These refs are used in the UI, and these are what we'll ultimately need to use in order to reflect a change.

Now let's add our API logic:

// when the component mounts (page loads)
onMounted(() => {
// start streaming messages
client.streamMessages({}).responses.onMessage((msg) => {
// add the message to the list
msgs.push(msg.content);
});
});

// keyboard handler for the input
const onKey = (ev: KeyboardEvent) => {
if (ev.key !== "Enter") return; // only send a message on enter
client.sendMessage({
content: content.value,
}); // send a message to the server
content.value = ""; // clear the textbox later
};


Now let's add some layouting and styling, with registered event handlers for the input and a v-for loop to display the messages:

<template>
<div class="h-100vh w-100vw bg-surface-900 flex flex-col justify-center p-3">
<div class="flex-1 p-3 flex flex-col gap-2 overflow-auto">
<p class="p-3 max-w-30ch rounded-md bg-surface-800" v-for="m in msgs" :key="m">{{ m }}</p>
</div>
<input
class="
p-2
bg-surface-700
rounded-md
focus:outline-none focus:ring-3
ring-secondary-400
mt-2
"
v-model="content"
@keydown="send"
/>
</div>
</template>


If you are unsure what these classes mean, take a look at WindiCSS to learn more.

And with that we complete our chat application!

## Other Implementations

While we used Rust for server and TypeScript for client here, hRPC is cross-language. The harmony-development organisation on GitHub has other implementations, most located in the hRPC repo.

Tags: #libre #harmony

## QML-LSP: A Simple LSP Server For QML

qml-lsp is a simple LSP server for QML. it currently only offers primitive non-context-aware completions, without anything else. it understands:

• completing component names
• completing properties defined in surrounding component (not properties from its superclasses)
• completing enums

note that it doesn't understand project-local QML components, only stuff installed to the system QML dirs with qmlplugindump set up correctly.

regardless, it does a decent job at completing system types, and considering that Qt Creator struggles with some of the plugins that qml-lsp has no problem with, it's pretty usable and an improvement over the nothing found in editors other than Qt Creator.

you can check out the source at https://invent.kde.org/cblack/qew-em-el-el-ess-pee, or fetch a statically linked binary here. plonk the binary in your PATH under the name qml-lsp.

kate LSP config:

	"qml": {
"command": ["qml-lsp"],
"highlightingModeRegex": "^QML$" }  tags: #libre ## A Hoppy Week In Tok Tok has seen a handful of improvements this week, one being that file messages now show a thumbnail if available. ## Music View Tok can now show you all songs that have been uploaded to a chat. ## Hop To Message Tok now allows you to hop to a message by tapping on it, no matter how far back in history it is. ## Bugfixes: • messages with newlines can be sent • formatting works again • nix files updated • the message text field no longer keeps the last formatting applied when you send a message ## Obtaining Tok Tok can be built from source from https://invent.kde.org/network/tok. There's a Telegram room for Tok available at https://t.me/kdetok, where you can come on and chat about anything Tok related, such as asking questions on using or building Tok. ## Contributing Interested in contributing? Come on by the dev chat and say hello! Tags: #libre #tok ## Way More Than A Week In Tok Tok has had many changes since the last time I made one of these blog posts, the biggest one being that code blocks are syntax highlighted! Tok uses KSyntaxHighlighting, the same syntax highlighting engine that powers Kate, KWrite, and other KDE applications that feature syntax highlighting. Additionally, messages containing codeblocks are able to grow horizontally in width beyond the usual message size, letting you read horizontally wide code easier. ## Emoji Completion Tok now displays autocompletion for :emojis:, making the process of typing in emojis much more seamless. ## Edited Indicator Tok now indicates when a message has been edited by the sender. ## Jump To Start Buttons Tok now has buttons that allow you to hop back to the start of various views, such as the chats list and the messages view. ## Improved In-Window Menubar Tok's in-window menubar now has various improvements, such as using the colour of the rest of the header area, as well as the right sidebar respecting the menubar's appearance. ## Proxies Tok now supports configuring proxies, allowing you to access Telegram in countries that don't want you to access Telegram. And in true anticonvergent fashion, Tok has a dedicated mobile UI for proxies instead of simply using the desktop UI on mobile or the mobile UI on desktop. ## Better Notifications Tok notifications now display more information about the message, support more message types, and display the profile picture of the chat you're receiving the notification from. Tok now renders mentions as links that display the user's profile when clicked. ## Minor UI Improvements Chat list unread indicators now become pill-shaped whenever they grow horizontally. The typing indicator in the chat list is now accent-coloured, like the typing indicator in the header. ## Translation Fixes Tok now correctly loads translation files, allowing it to render in non-English languages. ## Optimisations Tok's startup time has been optimised by a few hundred milliseconds. ## Bugfixes Tok no longer resets the scroll position of the chats list whenever chats are moved. Alt-Up, Alt-Down, and Ctrl-K keyboard shortcuts work again. Formatting is generally less buggier, and message formats better match how they're supposed to look. ## Obtaining Tok Tok can be built from source from https://invent.kde.org/network/tok. There's a Telegram room for Tok available at https://t.me/kdetok, where you can come on and chat about anything Tok related, such as asking questions on using or building Tok. ## Contributing Interested in contributing? Come on by the dev chat and say hello! Tags: #libre ## Asynchronous QtQuick UIs and their implementation: The Toolbox This blog post goes over the set of tools you have to work with when doing QtQuick UIs that need to perform something asynchronously. ## Data Reactivity Probably the oldest tool in the toolbox, as it's foundational to QML's construction, and one you should already know how to use. BooksModel { id: booksModel } Button { text: The model has${booksModel.count} items
}
Button {
onClicked: booksModel.refresh()
}


QML is reactive, meaning that when you change a property, dependent bindings update automatically.

When applied to asynchrony, this means that asynchronous events or requests can simply mutate an object's properties on completion, and the UI will change when it does.

This is the most common form of asynchrony in a QtQuick UI, and it should be your first goto when you need to perform something asynchronously and display results in the UI, as it plays nicely with a declarative UI.

This is generally the right pattern to use when you can describe the asynchronous process as “the UI displays the current state of a model/object, which changes in response to events.”

## Thennables

The newest tool in the toolbox (at least in QML), Thennables play nicely with other cool and shiny things, such as C++20 Coroutines.

Button {
text: "Do it"
onClicked: getNewButtonTextFromTheInternet().then((it) => this.text = it)
}


Implementations of the Thennable pattern can be found in Qt Automotive, as the QIviPendingReply family of classes, and in Croutons, as the Future family of classes. These allow you to make an object in C++ that you can return as a Thennable to QML after scheduling a task that'll fufill or reject the thennable from the C++ side. On the backend, it usually looks like this:

QIviPendingReply<bool> foo;

// runs asynchronously
client->getStatus([foo](bool isOnline) mutable {
foo.setSuccess(response);
});

return foo;


Or, if you're using Croutons + C++20 Coroutines...

auto response = co_await client->getStatus();

co_return response.isOnline();


This is generally the right pattern to use when you can describe the flow of asynchronous actions as an “and then” chain. For example, “make a request and then display a notification to the user if it succeeds or fails”. If the asynchronous process can take advantage of data reactivity, this is probably not the correct pattern to use.

This is rarely the right pattern, and people that have to use your API will probably hate you if you ship this in a library. Despite that, it's a (thankfully uncommon) thing in the wild, so I'll document it here.

FileDownload {

url: "http://placehold.it/350x150"

onProgressChanged: status.text = Download \${progress}% done
}
Button {
}


This essentially consists of using a full-fledged object description in QML to describe a task, and to connect to signals on it. The object needs to be given an id, and that id needs to be used to invoke a .start() method or setting a running property to true.

Task objects have many disadvantages: – trying to describe an imperative process directly with a declarative language, which goes as well as you'd expect – require creating and binding properties for an extra object, which can add up fast, even if the object's task isn't actually triggered by a user action. e.g., 50 download buttons with corresponding FileDownload objects, and only a few of those buttons will ever get pressed. – non-UI matters in an otherwise UI-only language

You can often recognise task objects by distinct “input” properties and “output” signals (less commonly, “output” properties) for a given task.

Instead of using a task object, considering using a Thennable, or better utilising reactive data.

## Workers

I've honestly never seen the WorkerScript type get used in an actual program, so I'm not sure what their practical application is. Despite that, these are another option for currency, so I'll describe their behaviour.

A WorkerScript is a wrapper around the JS notion of a WebWorker, which runs in its own thread and receives and sends messages from the main thread. This is the only way to get data in and out of them, so you have to structure it as messages.

Tags: #libre

## This Week In Tok: Days Of Work, Seconds Of Experience

Tok now has TWO new completely revamped upload dialogues: one for desktop, and one for mobile. Despite how much of the app is spent using stuff that isn't the upload dialogues, the upload dialogues took a LOT of time and work to get implemented correctly.

The mobile dialogue makes it convenient to browse through your most recent photos, videos, music, and files to share them with your friends.

On desktop, the upload dialogue now offers a preview of your file, and for images, the option to compress it.

## Sending State

The sending state of outgoing messages is now displayed with a little icon by the timestamp.

## Adjusted Chat List Look

The look of the chat list has been slightly adjusted, to make it look more like other KDE apps. More information is shown, such as the sending status of outgoing messages and the timestamp of the latest message.

## Improved Pasting

Tok now has improved pasting capabilities, able to paste from apps that put images on the clipboard directly like Firefox and Spectacle now.

## Obtaining Tok

Tok can be built from source from https://invent.kde.org/network/tok.

There's a Telegram room for Tok available at https://t.me/kdetok, where you can come on and chat about anything Tok related, such as asking questions on using or building Tok.

## Contributing

Interested in contributing? Come on by the dev chat and say hello!

Tags: #libre #tok

## This Week In Tok: New Features And UI Improvements And Janet Got Tired Of Writing A Cool Title For Every Weekly-ish Post

Meta: The first thing I have to announce is, that I'm tired of trying to come up with a cool name for every blog post. I'll just use This Week In Tok from now on.

## Secret (E2EE) Chats

Tok now supports secret chats, which are indicated in the chat list using a lock icon. Secret chats are Telegram's E2EE solution, and since the clients (like Tok) are open source and the algorithim well-documented, you can easily verify the implementation for yourself. Wish I could say the same for some other E2EE chats.

## Color Scheme Persistence

Tok now remembers which colour scheme you picked between restarts.

## New Desktop Settings

Tok now has a revamped settings dialogue more in line with other KDE apps on desktop.

## New Mobile Settings

Tok now has new dedicated mobile settings that offer a touch-friendly interface.

## Creating Chats

Tok now allows you to create chats of all kinds: one-on-one private chats, secret chats, private groups, public groups, and channels.

You can now disable notifications globally in Tok quickly and easily and enable them just as easily.

## Image Improvements

Messages with images are now much less jumpy than they were before.

Images also now show a blurry thumbnail while loading.

## System Tray

Tok now offers the ability to minimize to the system tray on desktop systems, where you can still receive notifications without a visible Tok window and disable/enable them from the tray directly.

## Obtaining Tok

Tok can be built from source from https://invent.kde.org/network/tok.

There's a Telegram room for Tok available at https://t.me/kdetok, where you can come on and chat about anything Tok related, such as asking questions on using or building Tok.

## Contributing

Interested in contributing? Come on by the dev chat and say hello!

Tags: #libre #tok