I’ve been discussing the Mastodon UX wishlists with some new acquaintances there. This excerpt from a Bloomberg terminal for Mastodon concludes with part of my own wish list.
In a Mastodon timeline, a talkative person can dominate what you see at a glance. When we participate in social networks, we are always making offers for the attention of others. As feed publishers, it’s wise to consider how a flurry of items can overwhelm a reader’s experience. But it’s also useful to consider the ways that feed readers might filter out a chatty source. Steampipe’s SQL database offers an easy and natural way to do this. This is part of the query that drives the list view.
select distinct on (list, person, hour) -- only one per list/user/hour person, url, hour, toot from data order by hour desc, list, person
It was easy to implement a rule that limits each person to a maximum of one touch per hour. The next steps here will be to apply this rule to other views, show the number of collapsed toots, and enable said rules per person.
As a warm up exercise, I decided to first add a simple control for pulses that allows me to view my home timeline with or without pulses. To give technically-inclined readers an idea of what’s involved in doing this sort of thing with Steampipe, I’ll describe the changes here. I’m obviously biased, but I find this programming environment accessible and productive. If that sounds like that to you, too, you might want to try some of the items on your own UX wish list. And if you do, let me know how it goes!
Here are the original versions of the two files I changed to add the new feature. first there is home.sp
that defines the board for the start timeline.
dashboard "Home" {
tags = {
service = "Mastodon"
}
container {
// a text widget with the HTML links that define the menu of dashboards
}
container {
text {
// a block that displays the HTML links that form a menu of dashboards
}
card {
// a block that reports the name of my server
}
input "limit" {
width = 2
title = "limit"
sql = <<EOQ
with limits(label) as (
values
( '50' ),
( '100' ),
( '200' ),
( '500' )
)
select
label,
label::int as value
from
limits
EOQ
}
}
container {
table {
title = "home: recent toots"
query = query.timeline
args = [ "home", self.input.limit ]
column "person" {
wrap = "all"
}
column "toot" {
wrap = "all"
}
column "url" {
wrap = "all"
}
}
}
}
And here is the new version. Add an input block called boosts
and passes its value to the referenced query.
dashboard "Home" {
tags = {
service = "Mastodon"
}
container {
// a text widget with the HTML links that define the menu of dashboards
}
container {
text {
// a block that displays the HTML links that form a menu of dashboards
}
card {
// a block that reports the name of my server
}
input "limit" {
// as above
}
input "boosts" {
width = 2
title = "boosts"
sql = <<EOQ
with boosts(label, value) as (
values
( 'include', 'include' ),
( 'hide', ' ' ),
( 'only', ' 🢁 ' )
)
select
label,
value
from
boosts
EOQ
}
}
container {
table {
// as above
args = [ "home", self.input.limit, self.input.boosts ]
}
}
}
Steampipe boards are built with two languages. HCL (HashiCorp Configuration Language) defines the UX widgets and SQL populates them with data. In this case we are selecting static values for the boosts
input. But any Steampipe query can be executed there! For example, here’s the input block I use in the dashboard that filters the timeline by the list I’ve assigned people to.
input "list" {
type = "select"
width = 2
title = "search home timeline"
sql = <<EOQ
select
title as label,
title as value
from
mastodon_list
order by
title
EOQ
}
Now here is the referenced query, query.timeline
from the file query.sp
containing queries used by all dashboards.
query "timeline" {
sql = <<EOQ
with toots as (
select
account_url as account,
case
when display_name="" then user_name
else display_name
end as person,
case
when reblog -> 'url' is null then
content
else
reblog_content
end as toot,
to_char(created_at, 'MM-DD HH24:MI') as created_at,
case
when reblog -> 'url' is not null then '🢁'
else ''
end as boosted,
case
when in_reply_to_account_id is not null then ' 🢂 ' || ( select acct from mastodon_account where id = in_reply_to_account_id )
else ''
end as in_reply_to,
case
when reblog -> 'url' is not null then reblog ->> 'url'
else url
end as url
from
mastodon_toot
where
timeline = $1
limit $2
)
select
account,
person ||
case
when in_reply_to is null then ''
else in_reply_to
end as person,
boosted || ' ' || toot as toot,
url
from
toots
order by
created_at desc
EOQ
param "timeline" {}
param "limit" {}
}
And here is the new version of that query.
query "timeline" {
sql = <<EOQ
with toots as (
// as above
),
boosted as (
select
$3 as boost,
boosted,
account,
in_reply_to,
person,
toot,
url
from
toots
order by
created_at desc
)
select
account,
person ||
case
when in_reply_to is null then ''
else in_reply_to
end as person,
boosted || ' ' || toot as toot,
url
from
boosted
where
boost = boosted
or boost="include"
or boost="n/a"
EOQ
param "timeline" {}
param "limit" {}
param "boost" {}
}
The original version uses a single CTE (aka common table expression, aka WITH
clause), toots
to gather data for the conclusion SELECT
. The new version inserts another CTE, increases, in the pipeline. Uses $3
refer param "boost" {}
which is assigned to the self.input.boosts
past of home.sp
The SQL code is all standard. Postgres is the engine inside Steampipe, and I sometimes use Postgres-specific idioms, but I don’t think any of that is happening here.
The HCL code may be unknown. Steampipe uses HCL because its primary audience is DevSecOps professionals who are familiar with Terraform, which is based on HCL. But it’s a pretty simple language that can be used to describe all sorts of resources. Resources here are widgets that appear on dashboards.
The other thing to know, if you want to roll up your sleeves and try to create your own boards, is that the developer experience is, again in my skewed opinion!, pretty good because if you’re using an autosave editor, you’ll see your changes ( both in HCL and SQL code) reflected in real time.
To illustrate that, here’s the screencast we included in our blog post introducing the dashboard system.
It’s not shown there, because we wanted to focus on the happy path, it’s the real-time feedback when your SQL queries cause Postgres errors. The experience is very similar to that advocated by Bret Victor in Inventing on Principle. The core tenet: “Creators need an immediate connection to what they are creating.”
Here is the wrong path that too often constrains us.
If there’s something wrong with the scene, or if I go and make changes, or if I have more ideas, I have to go back to the code and edit the code, compile and run, see what it looks like. Anything wrong, I go back to the code. Most of my time is spent working on the code, working in a text editor blindly, with no immediate connection to this thing, which is what I’m really trying to do.
And here is the right path.
I have this image on the side, and the code on the side, and this part draws the sky and this part draws the mountains and this part draws the tree, and when I make any changes to the code, the image changes immediately. So the code and the image are always in sync; there is no compile and run. I just change things in the code and see things change in the image.
We want to work the right way whenever we can. The experience isn’t available everywhere, yet, but it is available on Steampipe, where it powerfully enables the experimentation and prototyping that many of us are inspired to do as we delve deeper into Mastodon.
If you’d like to try this out for yourself, check out the setup instructions for the plugin that maps Mastodon APIs to Postgres tables and the panels that use those tables, and ping me (Mastodon if you’d like!) with any question you may have. have.
These series:
- Autonomy, pack size, friction, fanout and speed
- Create a Mastodon panel with Steampipe
- Navigating the fediverse
- A Bloomberg terminal for Mastodon
- Create your own Mastodon UX
- Lists and people on Mastodon
- How many people on my Mastodon feed also tweeted today?
- Qualified Mastodon URLs per instance
- Mastodon Ratio Charts
- Working with Mastodon lists
- Images considered harmful (sometimes)
- Mapping the broader fediverse
Copyright © 2023 IDG Communications, Inc.
Be First to Comment