Implementing a Recipes Manager using Solid
I'm still on a path to rebuild my online experiences using Solid, and next up is an alternative to Evernote Food. It has been dead for years, so I've kept my cooking recipes in Evernote and the experience has been subpar. It's time to make it better.
In contrast with my previous Solid apps, this one will have sharing functionality from the beginning - because recipes are made for sharing! One of the things I'm looking forward to explore is Solid panes, or how to customize the display of a resource in its public url. I'll also make sure that this app works in different PODs, because NSS is not the only option anymore. I'll test with NSS, CSS, ESS and PSS.
And this will be the first time I use Vue 3 in an app, so a lot of things I'm excited for in this task!
Activity
I have been tinkering with many projects under development, so it's been a rough couple of weeks but I'm ready to share some progress now.
First I'll start with Vue 3 and Vite. I was really excited to start using those, because I have been hearing about them for a while but I hadn't used them myself. At first it was great, and I think they are the future of my development workflow. But soon, things started to break down. To their credit, it's not exactly their fault.
Vite uses esbuild to compile the dependencies during development, and while it makes it really fast, it also means that the libraries have to be compatible with ES modules. There are ways to work around that limitation, but I spend way too much time trying to make things work and I decided to leave it there. To make things worse, Vite uses Rollup for production builds. While I do prefer Rollup over Webpack, having two build systems increases the possibility for headaches. In the end, I wasn't even sure if the problem was that the packages were not compatible with ES modules, what I know is that using Webpack all my problems were solved. I hope some time from now this is not the case, because I really liked Vite. But at the moment, I don't think it's worth it if you're going to use third party libraries.
Vue 3, on the other hand, has been a better experience. The only thing I haven't been able to use is the script setup sugar because it doesn't work with Vetur. But I am using the Composition API and everything else has been a smooth sailing.
In other news, something I'm starting to do with this project is using my own domain to serve applications. I'm still hosting them on github pages, but other than removing the domain dependency with github this also helps with sandboxing. I have been serving my apps under noeldemartin.github.io/{app-name}
, and they are all sharing things like localStorage which is not a good thing. So you can now find my latest finished Solid App at https://ramen.noeldemartin.com.
Wait, what? No, I haven't finished this task yet! Let me explain.
One of the things I want to achieve with this task is to test my apps with multiple server implementations. This is the perfect chance because the codebase is really simple, but as it continues evolving it will become too complex to use as a compatibility tool. So I started making some jokes, one thing lead to another, and I decided to make a separate application just for this purpose.
You can learn what it does in the project's README. In a nutshell, it checks if you've got a Ramen recipe in your POD and if you don't you can create it. I was really tempted to use Mr JΓ€gger's Ramen recipe (click through this link at your own risk), but I ended up using Jun's Ramen recipe.
I have tested this app with the 4 servers I mentioned for this task, and it only works properly with 2 of them (more details about that also in the README). The two where it doesn't work are still under development, so that's to be expected. But it's been helpful to tinker with them anyways, because I've learned some things I wasn't clear about before. Now I understand the difference between identity providers and POD providers (I learned about it in this issue). This lead me to use a new strategy for logging users into my apps. In my other apps, I just send the url that users introduce to the authentication library, but some times that may not work (for example, if they are writing their WebId and the identity provider is hosted on a different server). In this app, I am reading their profile and trying to search for solid:oidcIssuer
or infer it doing some requests. Most of the time this will result in some unnecessary requests, but it only happens on login and all of them should be very small.
There are still some rough edges with authentication, but some of them are not in my hands so for now I'll leave them as things to improve. Something I don't like but I don't think I'll be able to avoid is using two authentication libraries: @inrupt/solid-client-authn-browser for new servers that support DPoP authentication and solid-auth-client for old servers that don't. I have been able to avoid increasing the bundle size by code-splitting both libraries and only loading the one that's used (although I found some problems with the latest version of solid-auth-client). But I still haven't found a foolproof way to know if a server doesn't support DPoP authentication, so I'm relying on heuristics and hardcoded domains for now.
And, long as it was, that's it for today's update. With this I think I've covered most of the boring part about this task: authenticating and working with different servers. Now I can get into the fun part: building a recipes manager. For now I am calling it "Umai".
On my last update I mentioned that I'd start working on the recipes manager, but some things have come up and I continued working on the authentication workflow. I was already uneasy about hardcoding domains in the code, but this backfired sooner than I expected. After seeing this and having other conversations, I decided to abandon the strategy of using heuristics. However, asking people what authentication method to use is not a good idea either. So I ended up with a halfway approach. Instead of showing a dropdown with authentication methods, I use DPoP by default, and I've hidden the dropdown behind a "Can't log in?" button. With this approach, most people should log in without any friction and only those experiencing problems will see anything about authentication methods. I also refactored the implementation to decouple authentication libraries from the rest of the codebase. Now it's easy to add any authentication library by extending a class, and I've added solid-auth-fetcher to the list of options for testing purposes.
So, I deployed this new approach to Ramen and that was it. I wasn't planning on adding it to Media Kraken yet, because there is still an issue with Inrupt's library that makes UX sub-optimal. However, I was invited to present at Solid World! This meant that new people would probably check it out, and it would be a shame if they try it with servers that only support DPoP. So I've gone ahead and added it to Media Kraken as well. Now I should be able to forget about authentication for a while. I also found that I'm relying on something that is not part of the spec, but I can't do anything about it.
Also in preparation for Solid World (albeit tangentially), I decided to take a stab at sharing public data with Media Kraken Viewer. I'll explore this a lot more with recipes, but it can hopefully be useful for some people already. If you want to see an example in action, here's a list with 25 of my favorite movies.
So yeah, that was Media Kraken and Solid World. I'm very happy to have presented, and I met more people that have been using my apps and reading this journal. If you want to watch the recording, you can find it here.
Now, back to the recipes manager. Although I've been doing other things, I also did some preparations for it. I started reading Shape Up again, and I want to go back to applying the methodology to my side projects (we all know how that ended last time). I realize one of my mistakes is that I only used the concept of appetite, without doing any of the actual shaping or looking at rabbit holes. This time, I've been doing that and I've written a "proper" pitch. It's a bit pointless because I'm selling it to myself, but I found the process useful. I only included 4 goals for this upcoming cycle, and I've listed a lot of things that are out of scope and rabbit holes.
First, I've made a simple diagram of the initial version of the app. This is the main deliberable, everything else can be dropped or reduced with the scope hammer. I know the diagram is stupidly simple, and that's the point.
Second, I've decided to create my own ontology to augment the Recipe type on schema.org. I'll talk more about this when it is done, but essentially the schema.org vocabulary is very limited and I want more nuance in my data. But it's also more popular than whatever I make, so I'll try to mixing both in a way that other applications can understand for interoperability.
Third, given that this is my 3rd Solid app, I'll start creating a framework. It's not my intention to announce this as a new project, it's just a way to extract code to avoid repeating myself. But it's very likely that I'll eventually clean it up for others to use. I recently discovered Vitesse, and it seems very nice so I'll probably draw a lot of inspiration from it. You may have noticed that it's built on top of Vite, and I said in my last update that I wouldn't use it. Well, as part of the shaping process I've given it another chance and I decided to give it another chance. If any library doesn't work with Vite, I'll just compile it with webpack and load the chunk dynamically. Not a great solution, but it doesn't seem like I'll be able to resist the temptation. It's already bad enough that I haven't used Laravel in months because I don't write backend code anymore.
And fourth, I'll bring Soukai up to speed with some things I've learned in the last months. Tinkering with Vite and creating my utils library taught me a lot. One of the things I'm excited to solve is an issue I've had since its inception.
This time I'll follow the recommended 6-week cycle length, and I'm not tracking hours like I did last time. I'll start next week, so this should be done by March 22nd. Hopefully, I'll write some updates before that!
It's been 4 weeks since the cycle started, and here's how the Hill Chart looks like (yes, I even started using Basecamp!):
You'll notice that I've only gotten over the hill in one scope: Soukai Next Generation. It may look disappointing, but to be frank I'm happy with the results (which I'll get into in a second). I think this time I'm finally doing Shape Up rightish. One of the ideas I was more excited about from the book was "fixed time, variable scope". Specially given my situation; I'm only working one day a week on Solid. So time is more precious than usual.
Now, let's get into the meat of Soukai Next Generation. Like always, I'm wondering whether the work I'm doing is even worth it or I'm shaving yaks. 90% of what I've been doing these 4 weeks has been purely focused on developer experience, or said differently, "not necessary". But I'm enjoying it and I'm happy with the progress, so it's worth it in my book.
The first thing I did was migrating to Rollup and ESLint (from Webpack and TSLint). This seemed pretty useless at first, but later on it paid off when I started working in Viteland. I started doing this by hand in each project, but it became cumbersone quickly so I ended up extracting the build configurations into their own repository. Other than upgrading the tooling, I also learned a bit more about all the flavors of javascript. I identified 3 environments where my libraries can be used: Other libraries or apps (will normally use the ESModules build), scripts and tests (will normally use the CommonJS build) and plain HTML (will include the UMD build as a script tag in a page). The latter use-case is the only one that I don't use myself, but I'll still document it because I think it's interesting for getting started without a build step. During this upgrade, I also understood the polyfills I'm actually shipping with the libs and the environments I'm supporting (before this I think webpack was using babel under the hood but I'm not even sure).
Something else I did was improving TypeScript inference in models. It's still not as good as I'd like, but I've removed a couple of annoyances and improved many things. So that's been a big win. I'll document all of this more in depth when I release the next version (which I already know will not happen during this cycle). Here are some tests about the new inference in case you're curious.
As you can see in the Hill Chart, I've also advanced on using Vite and implementing the app. Thanks to the previous work, Soukai now works out of the box with both Vite and Webpack. As I suspected, Vite is a joy to work with once you don't have issues with external libraries. I've even been using a couple of plugins to do some magic stuff and it's working great. I know it won't always be like this, because some library will eventually fail to compile with Vite (I already know this happens with some of the authentication libraries I'm using). But I also have a plan for that, I'll bundle problematic libs with webpack as I did in this issue.
The current status of the app is very basic, but I'd say the foundations are laid out and that's why it's halfway uphill. It's not usable at all, but if you're curious about the code now it's actually a great moment to take a look because there is so little.
The custom vocab thing is something I didn't even start, and I think I'll drop it for this cycle. The nice thing about Shape Up is that this doesn't mean that I'll pick it up on the next cycle, because it's important to keep the slate clean. So it'll compete against all the other things I want to do in my next betting table.
Today is the end of the cycle, and this is where things are at:
In the end, I didn't finish v0.0.1 as I'd budgeted, that's why that scope is not all the way down the hill. But I almost got there, the only thing that's missing is configuring servings. I'm happy with the results, because the part I was interested in was laying out the foundations. And now I can say that Umai exists! It's definetly not finished, definetly not production-ready, but it exists. From now on, I "only" need to improve it.
The work on Soukai Next Generation has moved a bit, but in reality it hasn't changed much since my last update. I had done a lof of refactors and improvements, but I hadn't actually used them anywhere. Now that I'm using them in Umai, I'm more confident that the scope is further downhill.
The part that's changed the most since last time is the framework. I'm writing what I'd consider framework code in a different folder, and that's what I'll eventually extract into a package to use across applications. While doing this, I've created a couple of new patterns that I'm very happy with (and borrowed some from my favourite frameworks, like Laravel's Facades). If you look around the source code without going into the framework folder, you'll see there is almost no boilerplate. Most of the code is specific to this application. I'm enjoying a lot working on the framework, and I'm itching to document it and share it with others. But I'll resist the urge, at least until I finish Umai.
These two, Soukai Next Generation and the framework, come together during development in a big way. So far I've been able to work on the app without being bothered about any Solid specifics. Foregoing a POD was already possible using a browser engine from Soukai, and now that I've incorporated the Authenticators pattern I introduced in Ramen, I don't have to use an identity provider either. Soukai isn't perfect, and the framework isn't even a thing. But this is promising because I can see a day where I'll be working on apps without thinking of them as "Solid apps". And that's my end goal, Solid should just be a given.
Going back to planning though, this is where the Shape Up methodology breaks down for me. Seeing this cycle from a pure Shape Up perspective, it's a failure because the only "finished" thing I have is Umai v0.0.1. But to really consider it finished I would need to tag the v0.0.1 version in github, release new versions of all the related libraries (soukai, soukai-solid, utils, scripts), and all that goes with that (like documentation). All of that should have been part of this cycle, but I'll leave it for later. That's ok, though. Nobody is forcing me to be a purist. And I'd be surprised if the guys at Basecamp followed it to a T during the development of HEY. So I'm making the most out of it, and this time I can say it's been really useful because I got something done and I've dropped the vocabs thing that could have become a rabbit-hole (may come back in the future though!).
For the upcoming weeks, I'll use the cooldown to do some chores in other projects and shape the work for the next cycle. I'm still not sure if the next cycle will be the one where I release the app, but it probably won't. There are many things I still want to do that I consider essential.
Since my last update, I've been thinking on what to work on next. Something that's been nagging me for a while is that the authentication workflow I have in Media Kraken is not great. I realized it when I prepared the demo for my Solid World presentation.
The basic idea is that a user would start using the app with browser storage, so far so good. But then, when they want to move to Solid, they have to download a json, log out, log in, and import the json. That isn't so bad, but it's definitely more cumbersome than it should (and for some people it'll be a barrier). It's also annoying that even though the app works mostly offline, opening the app takes ages in mobile, because it reads the entire movies container at launch (and I have 1671 movies in my collection at this point). This was probably solved after NSS#1460 was closed, but I'm still using an old version of NSS (and my mobile phone is also quite old). But I'm in no rush to upgrade, after all that's how I notice these things.
Thinking how to improve this situation I brushed up on some ideas I had on my backlog. And now I am convinced that offline-first is the solution to these problems. In Umai, users will be using browser storage by default, and they'll be able to add synchronisation backends (for example, Solid). Reading about this topic, I came across some interesting concepts like CRDTs (and some funny encounters, like leap seconds). In particular, I enjoyed a lot an article about Local-first software.
So now it's almost certain that the next cycle will be focused on making Umai offline-first. But I don't think I'm ready to start yet, so I'll spend some indefinite ammount of time (hopefully not too much) shaping the work. I've already started some of the shaping process, and I opened a couple of posts on the Solid forum to get feedback. If you're interested in participating, check out these posts:
Hi again!
It's been only a week since I updated this task, but I decided to go ahead and kickstart the next cycle. The conversations about authentication and CRDTs are still ongoing, but I didn't get any strong arguments against what I had in mind, so I'll start working on it. There are some concerns that I haven't resolved, but I think the best way to proceed is to start working on the real thing.
As part of the shaping process, I implemented a proof of concept using a Solid POD to sync changes across devices:
There is a lot of hard-coded parts, that's why I didn't push this code to github. But I am very pleased with the result.
So, for this cycle I'll focus on the following:
- Offline-first: This may seem small, but there are actually a lot of things to do here. My intention is to be done with the data layer after this, so it should take the main focus.
- Recipe ingredients: This is important to see that the offline-first approach works for more complex structures, like lists.
- Interoperability: Like last time with the vocabs, this is sort of a wildcard goal. So maybe I won't do anything, but the idea is to be compatible with other apps who would interact with the POD storing the recipes.
The cycle should be done by May 31st.
You may notice that this cycle still doesn't have many features in scope. My idea right now is to use this cycle to get all the non-UI stuff finished, and focus the next cycle on UI, branding, and adding more features. Although I cannot tell for sure because that's the whole point of the Shape Up methodology.
So yeah, the first version of the app won't be finished at least until July. But that's ok, I'm in no rush.
PS: Google recently started tracking people using Google Chrome, even if your site doesn't use Google services. You can prevent it by adding an HTTP header, as I did with my sites. But I cannot do it in my apps because they are hosted using Github Pages, so I'm considering giving render.com a try. I'll report on the experience in a future update.
Today is the end of the cycle I mentioned in my previous update, and here's the current status of the project:
Yeah, I have barely advanced in a month and a half. Turns out that building the proof of concept for real was more work than I expected. But the real problem have been yaks.
This is not the first time that this happens, but I intended to avoid it by following the Shape Up methodology. At the time, when I got stuck building Media Kraken, I referred to these yaks as \"rabbit holes\". The interesting point is that this is mentioned explicitly in the Shape Up book, and I thought I was guarding against it by doing the proof of concept during the cooldown. Who could have guessed that it would take me almost 6 weeks (working 1 day a week!) to clean up something that I put together in a couple of hours :/.
But as I mentioned, the real problem here have been yaks. However, as I argued the last time, I'm in no rush and I enjoy the process so \"It'll be done when it's done\" :). To be honest, I don't think this will happen too often, because it isn't every day that I embark into a new paradigm like offline-first or CRDTs. And I still think the Shape Up methodology is useful, so I'll consider this cycle done and go into cooldown. Maybe the next cycle will come out of the leftovers from this one. But maybe not, and that's the point.
So, what did I actually do all this time? Inspired by this tweet by Anthony Fu, here's my own Yak Map:
As you can see, there were more things going on than I anticipated. To be frank, the Cypress tests and jest/chai plugins were completely optional. But I'm sure I'll use those for a lot of my projects going forward. Something cool I've been doing here is that I'm using the community-server in my CI pipeline, so the integration tests are now running against a real Solid server. This is particularly useful here given that Umai uses two engines at the same time, one local (for offline-first) and one against a Solid POD (to sync on the background). You can find the tests here if you want to see some code.
I found this concept of a Yak Map really interesting, and it speaks to what I have been doing for a while. Here's the Yak Map with my latest projects:
What I really want to do are apps, but I ended up developing a bunch of libraries and packages. Which is cool, it's also my own fault because I have an acute case of the NIH syndrome.
PS: I have also been translating Penny to Catalan and Spanish! Penny is a POD browser for Solid developers, so check it out and maybe you can contribute your own translations (there isn't a lot of text). It was interesting to see how to translate a Solid app, because there are some Solid terms that I had only come across in English. This has also been useful because I'll definitely localize my apps at some point. But for now, that'll remain a hairy Yak.
It's been a while since my last update, and the main culprit has been a blog post I've been writing that ended up way longer than expected. So long, in fact, that I decided to implement an animated table of contents in my website to make it more palatable (which I also added to task comments!). Still, the estimated read time is 40 minutes so yeah... Sorry. I didn't have time to write a shorter one. You can look forward to reading (or ignoring) it next week.
Now, other than writing and working on my website, I've also kept hammering at some Solid stuff. In particular, I have been thinking how to model lists in Solid. That's where I left off in the last cycle, in order to add a list of ingredients to a recipe.
This is how you'd go about doing an "unordered list":
<#ramen>
a schema:Recipe ;
schema:name "Ramen" ;
schema:recipeIngredient "Broth", "Noodles" .
That's pretty standard, basically you'd have multiple values for a property. That's what Soukai's FieldType.Array
does. However, the order is not guaranteed to be kept the same, specially when you add or remove values. I've been investigating, and turns out there is already a common pattern to do this, using an rdf:List
. This is how you'd write it in turtle:
<#ramen>
a schema:Recipe ;
schema:name "Ramen" ;
schema:recipeIngredient ( "Broth" "Noodles" ) .
Which actually means:
<#ramen>
a schema:Recipe ;
schema:name "Ramen" ;
schema:recipeIngredient _:b0 .
_:b0
a rdf:List ;
rdf:first "Broth" ;
rdf:rest _:b1 .
_:b1
a rdf:List ;
rdf:first "Noodles" ;
rdf:rest rdf:nil .
But I have some existential doubts about doing this. If you look at the recipeIngredient
specification, you'll notice that it only accepts Text
values. Or, said differently, the range of the schema:recipeIngredient
property is Text
. In that case, isn't an rdf:List
an invalid value for this property? There are two ways to look at it. The first one is about correctness, and I would say that's incorrect because of what I just mentioned. I think we should strive to create data that is modeled properly. The second one is interoperability, which I think is super important. If there are other applications out there working with recipes, developers should be able to make it interoperable with mine just by reading what's on the vocabulary definition. They shouldn't even aim for interoperability, it should just happen if good practices and proper modeling are followed.
But, I do have my doubts about this because it could potentially be a common pattern. Having sorted data is certainly very common, and I think forcing each developer who wants sorted data to define their own vocabulary would be detrimental for interoperability. It would be nice if every property definition had rdf:List
implied in their range (although I'm not sure how you'd go about specifying that the items on that list should be of a given type, because the range of the rdf:first
property is rdfs:Resource
).
So yeah, I'm not sure about this. In any case, seeing all the complexity I found, I will probably conform with the schema.org vocab completely without doing any shenanigans. I've also played with the idea of having my own vocab to extend schema:Recipe
, but for the time being I think I'll avoid it. I've already been working on this app long enough, so it'd be nice if it starts converging :). Don't get excited though, there are still some months to go.
Now, with that said, here's what I'll be working on for the next cycle. It is going to be a weird one, because I have some holidays in the middle, and I'm always more busy (in my personal life) in summer. These are the things I'll look into:
-
Ingredients & instructions: I already started working on ingredients last cycle, but I wasn't finished. This time, I'll do instructions as well (which can be sorted using schema.org's vocab!).
-
Offline first: I'd say this is done for the most part, but I have to review a couple of things to see that logging in and out of the app works properly and doesn't mess up the local data.
-
Dependencies bundling: I'm still struggling with adapting some libraries to Vite, although there have been some recent improvements that look very promising (I still haven't tried them though). And that is not limited to external dependencies, my own
soukai-solid
library has some issues with Stream polyfills. So I'm going to explore some alternatives (like bundling compiled dependencies, crazy as that sounds).
Given the aforementioned summer schedule, this will be a shorter cycle and it should finish by August 16th.
As I write this, I'm preparing my backpack to continue the Camino de Santiago where I left it last year, and turning my holidays mode ON.
So it's with a bittersweet note that I have to say I didn't finish any of the goals from my last update :(. But that's ok, and I thought I'd use this opportunity to talk (a little bit) about Impostor Syndrome. I've heard some people mention it in the last couple of weeks, and I want to share my thoughts on that. First of all, yes, I also suffer from it. I think everybody does (at least everyone in the tech industry). So when it happens to me, I try to see it as what it is: the manifestation of the ups and downs of a normal learning journey (one day you think you know everything, the next day you realize you knew nothing). But of course, the following thought crosses my mind: "what if that is just an excuse to avoid facing that I just suck?" Yes, it could be an excuse. But I'm all for good placebos. That's why, instead of being hard on myself, I have systems in place to make sure that I'm doing a good job. And today, even though I am yet again late on my predictions... I'm fine with it :). I'll just recalibrate, reflect on what I did, and move on. I also realize that most of my anxiety is my own doing. Nobody is forcing me to work on these side-projects, and my livelihood doesn't even depend on it. Like Seneca said, we suffer more often in imagination than in reality.
Anyways, that's enough of my ramblings. Here's what I have done since my last update.
Some days ago, CSS v1.0.0 was released. I had been using it in my CI pipeline with Cypress, but I was cheating somewhat because I wasn't logging in. I am still doing unauthenticated requests in most tests, but thanks to some new additions in this release I was able to test authenticating with my app. And it turned out to be really useful, because I found a couple of things I was relying on that aren't part of the Solid spec (so I shouldn't use if I want my apps to work with most PODs). I have listed them and some more in this github comment about migrating apps that work on NSS to work with CSS.
Other than that, what has taken most of my time have been some heavy refactors, as always. This offline-first/CRDTish approach is turning out to be more complicated than I thought, but I actually think it's good because it's pushing Soukai's boundaries. The design itself wasn't that complicated, but Soukai wasn't ready to handle it because there are some complex relationships. A Recipe has instructions, and each instruction has its own metadata + change history. So Soukai needs to handle a 3-levels nested hasMany
relation in the same document. I think the library is becoming more robust, but it's also increasing in complexity, so we'll see where this takes me. I haven't really added anything to the scope, this was part of my initial vision, but I hadn't stress-tested it yet. It's also getting a bit ridiculous on the TypeScript front, so I can see why some people don't like it and prefer writing plain JavaScript.
And that's basically what I've been up to. Since I haven't really finished what I wanted to do, and I'm going on holidays, I won't bother doing any planning for now. When I'm back, my idea is to finish all this relationship madness and then I'll start thinking about finally working on the UI. If anything, I would say using the Shape Up methodology is kind of working because I've already cut a lot of scope from my initial idea. Only that it isn't happening in a single cycle, I guess it's the drawback of working just one day a week on this.
Hey, I'm back!
Actually, I've been back since early September. But I wanted to finish working on the data layer before posting an update. It's been a slog, but I'm finally ready to move on.
I've been working on this app for a long time, and I don't think I'll be done with it before the end of the year. So it's very likely that this task becomes the first one to span multiple years in my development journal. Sure, I've been doing other things here and there, but it's certainly taking a lot longer than I expected. This is not something new though, if you read the development journal for Media Kraken, you'll notice a similar pattern. For Media Kraken, it was refactoring Solid and IndexedDB storage into a single API. For this task, it's been implementing a conflict resolution strategy for Solid. I worry a lot about accidental complexity, and I'm pretty sure I'm prone to over-engineer. But looking back, I'm glad I spent the time doing the refactor in Media Kraken. Only time will tell if the work I just finished was also worth it.
So, keeping that in mind, I've decided to do an alpha release of the app. The app will probably look nothing like it does now when it's finished, but I'm interested in getting feedback about the conflict resolution strategy before releasing for production. If you are interested in giving feedback, read about it in the Solid forum: Umai alpha
release feedback.
Now that I'm done with that, I can finally focus on the UI. Before starting the next cycle, I'll do some wireframes and think about what I want to include in the first version. All of this time, I've been having many ideas for the UI and some cool features, so I'll have to make an effort to reduce all of that into something doable in 6 weeks.
After doing some brainstorming, I've decided to focus next cycle on just the basics. Eventually, I would like to add 4 sections in the app: Cookbook (to manage recipes), Kitchen (to cook recipes), Pantry (to keep track of ingredients storage and make shopping lists), and Calendar (for meal planning). But all of that can get out of hand soon, and I'm not sure if I'll include that in the first version (probably not). This is very similar to Media Kraken; I wanted to track other types of media but I ended up only tracking movies (for now).
So here's what I'm focusing on for this cycle:
-
Branding: I'm still not super happy with the name "Umai", and I haven't made a logo either. So I'll use this cycle to finish those. I can always change them later on, but it's time to settle on what I'll do for the first release.
-
Design System: I've been using TailwindCSS which already has some constraints, but I'd like to organize the UI even more. So far, I've been a big fan of design systems in theory, but I haven't really used them in practice. So I'm looking forward to experiment with this. I've been playing with Storybook, and it's been a bit of a pain to configure with Vite, but I think I've got it sorted out.
-
Panache: I think one of the main reasons why people like Media Kraken is the animations, and I've had a lot of fun making them. But I always thought I could take it up a notch, and I've seen a couple of ideas I'd like to play with. But of course, there is also the danger of taking it too far, and I worry about that. I don't want to end up with an app that is annoying to use because everything is moving around. I also don't want to spend too much time working on this. Fun as it is, it's just a nice to have and there is a lot more things I want to do.
I'm starting this cycle next week, and I'm going back to the standard 6-week length. So it should be finished by November 29th.
The current cycle is coming to a close, and let me start by showing what the application looks like right now:
Isn't that nice? :D. I didn't manage to get as much done as I would've liked, but I'm definitely happy with the results. From the 3 areas I mentioned last time, Panache is the one that I consider almost finished. It's not finished in the sense that I won't have to do anything else, but I've laid the foundations and adding more should be easyish from now on.
As for the other areas, this is how the hill chart looks like right now:
Recipes & Cookbook is not something I mentioned explicitly before, but that scope basically consists in applying the improvements to all pages of the app. It's not over the hump because I haven't done any work in the edit or create pages yet. But as you could see in the video; home, cookbook, and recipe details are pretty much done.
The Design System is something I started working on at the beginning of the cycle. I started making some of the "final" components with Storybook, and I started to apply some concepts from Atomic Design in the process of building the pages. But I didn't take it much further, because I got into the rabbit hole of what to do about page transitions.
At the start of the cycle, I pointed to a couple of examples that inspired me. Unfortunately, looking at existing implementations none of them worked as I expected. The closest thing I could find was a library called v-shared-element, but the result was a little clunky because the animations cannot be customized much. So of course, I ended up developing my own thing from scratch π
. To be fair, for most people v-shared-element
will be enough, and it was very easy to set up. Looking at their source code also helped me to come up with my own solution, but in the end I wanted something more flexible. It's not as seamless to work with, but gives me the control I need to make the animations I had in mind. We'll see how this evolves as I continue making more pages, maybe I'll even release it as a stand-alone library if I think it can be useful to others.
And finally, this takes me to Branding. You'll notice it's right at the bottom of the hill. I haven't struggled this much to come up with a name for while, and to be frank I'm kind of blocked at the moment. To make things worse, I've been looking at existing applications and websites related with food, and almost none of them inspire me (one of the few exceptions is Chipotle, I love that one). So yeah, I don't know how this will turn out, but I still have some time until I'm done with v1, so I'll continue chipping at it.
My current ideas for names, none of which I'm completely happy with, are the following:
-
Umai: This is what I'm currently using, but I feel it's more a codename than the name I'll end up using on release. I don't dislike the word per se, but it has a couple of problems. First, it's not a very common word and can easily be confused with Umami (I was already suspicious before seeing that tweet though). Even though it's not a common word that people will remember, it's common enough in japanese circles that it'll be impossible to find my app just with the name. So when I tell people about it, I'll have to say "search for Umai Solid" or "search for Umai noeldemartin" or whatever. The word itself also doesn't inspire me on what the logo should be, or even the color scheme or branding for the app. Not great, and the word is not that good to begin with to go through all that trouble.
-
Grateful Bite: In spirit, this is the name I like the most so far. But that's it. No logo comes to mind, no associated branding, and the people I've asked to didn't like it as much as me.
-
Pepper Bell: This may be the front-runner for now, because some logo and branding come to mind. I also like the fact that it's a word play with "Bell Pepper" inverted, which may make it easy for people to remember and hopefully easy to search for. But in spirit, I don't care much about it. It doesn't give me joy, like Media Kraken does, and it's not as descriptive of what the app is for as Solid Focus. So it's not perfect either.
I also came up with some other ideas, but none good enough to even mention. So yeah, I'll continue struggling with this and we'll see how it turns out in the end. The problem is that this issue kind of blocks everything else. Without a name and a branding, I'm making the rest of the app in the dark. I didn't want the app to feel the same way as Media Kraken, but so far it's eerily similar because the branding doesn't speak for itself. But this is part of the creative process, eventually something should click π€.
At this point, I would normally go into a cooldown cycle for a couple of weeks and plan what to work on next. But seeing how things are going, and that new year is around the corner and I'll be on holidays, I'll extend the cooldown until and undecided date next year (probably the 2nd week of January). It won't be a traditional cooldown either, because I'll probably we working on what I didn't manage to accomplish in this cycle. So this will probably be my last update for the year.
PS: I've also been working on a very simple Hello World for Solid, built using plain JavaScript and HTML, with as little dependencies as possible (no Soukai, and no frameworks!). If you've been meaning to get started with Solid yourself, but didn't know where to begin, I encourage you to take a look. As always, feel free to contact me or open an issue if you have any doubts or comments.
Hi there!
I'm back from Christmas break, and I was AFK for a couple of weeks so I'm eager to pick up where I left off. I recently looked at my Last Year's Achievements, and even though I didn't manage to complete most of my New Year Resolutions for 2021 (like closing this task π ), I'm very happy with everything I accomplished. But this year, for sure, I will release this app!
Before going into the next cycle, here's some updates. First of all; naming. I was still struggling with this, but ultimately I decided to stick with Umai. I'm still not 100% convinced, but I didn't make any progress and I don't see an end to the rumiation. I can always change it later if inspiration strikes, but I'm not going to waste any more time on this. Umai it is.
Once I settled on the name, I started working on the logo. At first it was going great, because I had the idea of using the "U" from Umai as a bowl, but it hasn't translated into something I'm happy with so I'm still playing with the idea. I think this may turn out like the name itself, I don't love any of the choices so far but in the end I'll have to stick with one. In case you're curious, here's some early drafts without context nor explanation:
If I have to be honest, all of this is a bummer because even though I'm no designer this part of making products was one of the most enjoyable for me. But for this app, it's been a struggle so far. I guess that's life though.
But not everything has been a struggle! In my last update, I hadn't started working on forms and what I had in mind was very different to what I ended up doing. Initially, I thought forms would be separate screens/modals, but the more I worked on them, the more I realized that using the same layout was actually a decent option. The idea is that when you try to edit a recipe, you'll see the same layout but data will be editable. I could even make everything editable by default, but I don't think that's a good idea at this point. Something that worries me is that people may not realize how this works, so maybe I'll end up implementing some guided tours (although I'm generally averse to adding those).
Something funny (if you can call it that) that happened implementing these forms is that I made ingredients sortable and instructions unsortable. My idea was that you are not going to change the order on some instructions, but you may sort ingredients by quantities or something else. But later on, I realized the data was actually modeled the other way around (ingredients don't have an order whereas instructions do). Ideally, though, these are just UI affordances, but I'd like both lists to have an order. This is something I already talked about, and I still haven't found a good solution.
Something else I realized recently is that just because I'm using schema.org
, it doesn't mean that my apps will be interoperable out of the box. I have been using https://schema.org/
as the prefix, but turns out that some apps out there are using http://schema.org
. So that's a problem, even with Media Kraken. I may tackle this before I release the next version of soukai-solid
, but since I don't know how much of a problem this really is, it won't be a priority for now (is there an app that would interoperate properly after fixing this anyways?).
Finally, the last thing I want to mention is a11y. Some months ago I started learning about it at work, and I've realized how bad of a job I am doing in my other apps. So I want to improve it for this one. The problem is that I don't use the web like a disabled person would. And it's very difficult for me to walk in their shoes and understand what's important and what isn't. Reading about WAI-ARIA Authoring Practices and such is helpful, but I'm still not sure of what I'm doing wrong. That's why, when I tooted about my last update, I included some text only visible to screen readers asking for help. And you can imagine my surprise when someone offered :D. That will be fun. I want to be respectful of their time, so I won't be asking for a lot of details. But just knowing if the app works or it sucks will be very useful.
Now, it's time to talk about the next cycle. I have been working on this app for a long time. Too long, in fact. So I have to start wrapping up the initial release already. However, doing a release encompasses more work than it seems. I have to write the documentation, publish the new vocab I've created for history tracking, release and document all the changes in soukai, etc. And I'm still missing some features that I consider essential for release: error reporting, onboarding, etc. In my opinion, setting the sights on release for the next cycle would be too premature. But I just said that I've been working on this app for too long. So what I decided is that in the upcoming cycle, I'll finish the app, without actually doing a release. That way, I can do a "release candidate" of sorts. I will start using the app in production myself, and ask early adopters for feedback; but there won't be any guarantees of stability. In the next cycle, I'll aim for release and use it to document and iron out the bugs that I find during testing.
That's what the next cycle will be about. I'm starting today and it should be finished by February 27th.
Here we are, February 28th, a day after the end of the cycle. And the app is... not finished! Who's surprised? (not me, to be honest). All jest aside, it has become a pattern that I don't finish tasks as "estimated", and therein lies the crux of the issue, if I were following the Shape Up methodology properly I wouldn't need to follow estimations, I would cut the scope instead. The thing is, that I don't want to cut the scope any more than I have already done it, which is a lot. If you look at the app as it stands today (and Media Kraken, for that matter), you cannot say it has a huge scope when it comes to functionality. But if you start looking under the hood, you'll see there is a lot more than the eye can see.
So if it doesn't have a lot of functionality, what is taking so long? Complexity? In part yes, but if I had to reduce the reason for most delays to one word I would say "experimentation". The truth is that I rarely just implement a feature, I'm always refactoring and creating new patterns. Because I like doing it, and because I think eventually this will save me time. But to be honest, I don't care if it doesn't, I enjoy the process in and of itself. That's one of the freedoms I have since my livelihood does not depend on this.
In any case, having said that, I do want to get this done and move on to other things. So here's what I've done the past 6 weeks and what is left to consider the app production ready (which doesn't mean release, but I still haven't started using it myself either).
These weeks I've been working mostly on onboarding and login/sync. The basics were already there before, but I've been ironing out a better UX. For example, it's now possible to configure how often data is synced and if the app should reconnect automatically in every log in or not. And the coolest thing I've been doing is implementing importing recipes from the Web. Yes, you read that right, the Web. Anywhere on the Web :D. There is a lot of linked data in the wild used for Semantic SEO, and recipes are one of the most common. This is a clear example of something totally out of scope, and that I don't need personally. But I started tinkering with it and got so excited that I couldn't help myself. I still have to iron out some quirks, and I'm still pondering whether I should use a proxy or not to work around some CORS issues. But the core parsing is done and seems to work well enough.
I initially thought about having a Recipes Library built-in, and it's actually implemented in the app as of now. But I'm second-guessing if it makes sense or not, now that importing from the Web is supported. There are two main problems with it. The first one, is that I don't want to have so many options for creating a new recipe because that will be confusing (there are 4 at the moment: from scratch, from the Web, from the Recipes Library, and uploading a JSON-LD file). And the second one, maybe more important, is that I don't know what recipes to include. I have some recipes myself, but I'm not sure what would be a decent minimal cookbook for most people. So I'll probably drop this one.
I've also been working on some other small things, but nothing specially interesting comes to mind. You can always look at the commit history if you're curious of what I'm doing exactly, the commit messages should at least give you an idea.
Given the current state, I've decided to stop doing Shape Up until I have a version that is production ready. I don't know how long that will take, but I really think I'm not far off. Here's a list of the main things remaining:
- Recipes sharing
- Review overall UX/UI
- Review a11y
- Fix Mobile layout
- Smooth transitions/animations
- Smooth onboarding experience
- PWA configuration
- http vs https schema.org handling
- Vue ecosystem review (I started this project a while ago and Vue 3 is stable now, so I want to make sure that I'm following the latest best practices)
As you can see, there arem't really any features left to implement other than recipes sharing, just a bunch of ironing out. So once I add some form of sharing, I'm putting my Getting Things Done hat on.
I'm not done with the app yet, but last week I worked on something that I thought would be interesting as a stand-alone update. So, today's entry will be 100% technical :).
One feature I hadn't implemented yet was image uploading. If you check the list of things left to do from last time, you'll notice this wasn't there. And that's because I didn't intended to implement it for the initial release. However, after going through the onboarding, I realized this is actually very important. So far, I had just been grabbing recipes and images from the Web, but when I actually tried to create my own recipes, the inability of uploading images was too crippling. So I just went ahead and started implementing it. After all, I've already implemented image uploads in other apps many times, how hard could it be to do it in Solid? (said the ingenuous fool).
Turns out, it's not that easy. I was already aware that images cannot be inserted in the DOM like you would any normal image. If you write <img src="https://my-pod.com/recipes/ramen.jpg">
, this will probably fail to render the image. That happens because the image will be private, and the POD can't return its contents without proper authentication. The solution is to fetch the image using an authenticated fetch and inject the image contents dynamically with Javascript. That's actually what Inrupt's React SDK is doing. And that works, but I faced an additional issue. My app is offline-first! If I really mean it, you should be able to create new recipes offline (including images, of course). So that alone won't cut it.
After some tinkering, I came up with a solution that whilst not being perfect is good enough for the first version. Basically, I am storing images in IndexedDB. Something nice is that you can actually store Blobs in IndexedDB, so I didn't have to bother with serialization woes. When the user goes back online, I'll upload the images to the server and remove them from IndexedDB. This works better than I expected, and even though it complicated the image component a bit, it's worth it.
But there was still another issue. Browsers are pretty good at caching images; if you go offline, it's very likely that you can see the images you've encountered online without doing anything special. This is because images usually come with some special headers and get cached for a while. However, Solid servers don't seem to do that at the moment (I have to investigate why, but I don't think I'll be able to fix that anytime soon). So it's up to my app to cache everything. As far as I understand, that's not something Inrupt's SDK is having into account, so images are downloaded each time they are appear on screen which ain't nice (I could be mistaken though, I haven't used the library, I've only browsed the source code from time to time). What I ended up doing to work around this issue, is using the Cache API to store images. The good part is that since they are treated as cache, the browser can remove them whenever it needs some storage. The bad part is that it doesn't know anything about caching headers, so I have to handle all that manually. Which I'm not doing yet, so images are essentially cached forever. Which in turn causes a related issue: when images are updated in the POD, my app won't notice and it'll continue using the old version. I can probably fix that by doing a HEAD request and compare ETags or something, but I still haven't done any of that and this is where I'll leave it for the first version.
There is also something else that made me avoid this feature in the first place. And that is treating the images. In a typical application, you upload the raw image file and the server takes care of resizing it and generating thumbnails if necessary. But given that I'm doing a "pure" Solid App (one without a server component), I can't do that. Initially, I looked at the filepond library, which is pretty nice but felt too heavy for my use-case. After digging a little bit more, I ended up using a smaller library called image-blob-reduce; which ain't as fancy but gets the job done and has a much smaller footprint. I also decided not to generate thumbnails for now, but I think that should be fine for the initial release.
And that's it for today's update! I already started working on the sharing functionality and I think it's looking good, I want it to be useful for both Solid and non-Solid users alike. So it may end up being one of the most important features in the app. But more about that in an upcoming update.
Today I'm happy to report that I'm done with features for v0.1! π₯³
As always, I ended up going a bit overboard and actually implemented more than I intended... But I'm very confident that in the next update, the app will be ready for production. Not released, but good enough to start using it myself.
If you look at the list of things left that I wrote a couple of updates ago, the first one says "Recipes sharing". This is something that I think will be very important, because it'll make this app useful for non-Solid users as well. However, this has been my first time implementing anything related with authorization, so I had to learn a bit about it.
This could get very long, so I'll summarize saying that there are currently two systems for handling authorization in Solid: WAC and ACP. WAC was the first one, and in practice is supported by most servers (as far as I know, ESS is the only one that doesn't). ACP is the new approach, but it doesn't seem to be supported by most servers and to be honest, seems a lot more complicated (I could understand WAC pretty quickly, but reading the ACP spec went a bit over my head). So in the end, I decided to just support WAC for now.
There are multiple reasons for making this decision. First, as you may know if you're reading this, the development of this app is taking ages and I want to be done with it (scope hammer!). Second, the lack of support doesn't make it appealing (to be honest, this reminds me a lot to the interoperability spec; it sounds cool in theory, but it's still too experimental to be used in practice). And third, I don't think this is a must-have in my app. After all, users should be able to update the permissions of their files using a different app, that's the point of Solid. I could also start using Inrupt's client library, which supports both; but I don't think this use-case is strong enough to warrant adding more dependencies (and I want to keep them down to a minimum). This is also the first and only feature that doesn't really work offline, but given the nature of the use-case, I'd say that's ok.
So with that, recipes sharing is done!
Something else I've been working on these last couple of weeks was the "Smooth onboarding experience". When I wrote that, I thought this meant doing some UX/UI and copy writing. But the more I looked into it, the more I realized that something was missing. I said before that importing recipes from the Web will be the main mechanism for users to populate their cookbook. But there was still an elephant in the room called CORS. My previous solution was to instruct people into copy pasting the source of a website. But as you can imagine, that wasn't very user-friendly. So I bit the bullet, and I've ended up implementing a proxy server that will take care of this. There are still a couple of things left, though. The first one is that I still need to work on communicating this to users. I could just do it without even telling them, but it goes against my ethos for the app (100% client side, decentralized, etc.). And I also want to give them an opportunity to use their own proxy. The other thing I want to look into is to actually implement a proxy properly π
. What I've done thus far is too specific to my app (albeit very simple, I just have a /fetch
endpoint on a server that replies to POST
requests). Because of that, the only real option for someone who wants to use their own proxy is to self-host my own solution, which is Open Source. But it seems like HTTP proxies are a thing, and they follow a standard. So I should be able to implement that and any existing proxy service should work. I'll probably look into that after I start using the app myself (but before the release).
If you look at the list of things left, you'll notice there's still a bunch left. But I've decided to drop a couple. I won't be doing the "Vue ecosystem review" nor the "http vs https schema.org handling" yet. So that only leaves some general housekeeping tasks, and then you'll finally be able to experience the app for yourself.
I'm very confident that in the next update, the app will be ready for production.
Well, here we are in the next update, and the app is not ready π . Confidence really is the liquor of the fools. I really thought I would manage to do it, but I realize a month has gone by and I'm not there yet. So I reckon it's time for an update and I'll have to eat my own words.
However, that doesn't mean I haven't made any strides!
For one, I've settled on most of the branding and style. A while ago I mentioned that I wasn't feeling great about the branding for this app so far. But that's changed now! I'm pretty pumped about it, and I'm really looking forward to releasing it. The basic approach is the same I had been working with, but I came across a couple of visual techniques that add some spice; and I'm starting to like it a lot. After some iterations, I've come across a whiteboard-like feeling for the app that I think is really cool. I hope everyone else likes it as well :).
Something else that happened is that I realized I wasn't really done with features :(. I hadn't implemented deleting recipes yet, but I didn't even count it as "a feature" because it seemed trivial. Alas, being an offline-first app complicates things. Instead of just deleting an item, it has to be marked for deletion (or "soft deleted") and delete it for real on synchronization. It wasn't that difficult to implement, but I also had to implement a bunch of stuff in soukai for soft deletes and leaving a trace behind (to avoid dead links).
Which takes me to something else interesting that's been happening. Given that the app release is approaching, I started to think about publishing my custom vocab for CRDTs. Initially, I intended to host it myself at vocab.noeldemartin.com
, but Tim suggested that I could try to add it to w3.org
, so I started that process. However, I have to say that I'm a bit lost on what's the timeline for this to get accepted, so depending how it goes I'll end up hosting it myself. But something good already came up of this.
During the review, Tim mentioned that RDF is monotonic and so a subset of a graph cannot contradict the meaning of the complete graph. What that means is that I cannot use default values for missing properties on a resource. I had seen schema.org
doing it, but it seems like even though it is very widely used, when it comes to RDF purity schema.org
leaves some things to be desired. So I ended up refactoring the operations schema for better modeling. It's always good to keep learning the proper ways of doing things with RDF, and this reminds of something I mentioned many updates ago. Eventually, I did learn the proper mindset to understand that. You cannot say that it's "invalid" to do that sort of thing, it's just that it's conveying a different meaning (which can be complementary, not contradictory). If you want to learn more about that, you can check out the conversation I had about it in gitter right after posting that update.
Other than this, I've been spending more time than I'd like to confess working on animations and such. It's a shame because I enjoy working on them a lot, but it's also true that they are not that important. Just over a month ago, there was a presentation at Google IO about page transitions on the web. Whilst they are very interesting, I think they are too limiting for the type of animations I'm doing. But I'll definitely have to reconsider my approach, because so far it's taking far too much of my time.
To finish, seeing how it's going, I've decided to set a deadline for starting to use the app myself and releasing the beta. Looking at my calendar and the month ahead, I think a good date would be August 1st. I may have it ready before that, but we all know that ain't happening.
Until next time!
Just in the nick of time, here it is, the beta release! I've also started using the app myself, so you can finally see what I've been working on for the last couple of years. Sorry for the wait π.
It is, however, not ready for release. So I would refrain from sharing it with others. You can learn more about the beta and give some feedback in GitHub: Beta Feedback.
I'm sure you've heard this idea that if you're not embarrassed by something you release, you're launching too late. Believe it or not, I've kept this in mind the entire time. So yes, I'm still embarrassed to some extent, but I'm very happy with the way it turned out. We'll see how it goes with the daily usage, and the feedback I get from others.
However, there is something I'm really embarrassed about, and that's animations. What I thought would become an awesome system to implement cool transitions effortlessly, has turned into a bloated collection of hand-coded animations that is high-maintenance, error-prone, and sort of clunky to the eye. When it comes to the UI, the transitions system I cooked up has caused the most headaches by far, and I'm not happy with the way it turned out. The end result from a user perspective is not bad (although I wouldn't be surprised if it's broken in some edge case I didn't test properly), but the developer experience is awful. I still think the fundamentals of my approach are right, and I certainly learned a lot. But I'm not sure if I'm willing to dedicate the time to take this where I'd like it to be. For now it is good enough, but I'll have to think hard and long about upcoming apps and features.
Having said that, I'm really looking forward to the actual release and I don't plan on doing any big changes to the current version. It will also depend on how my daily usage goes, and the feedback I get, but my intention is to release the app as it is now from a user perspective.
I'll be going on holidays for some days in August, and taking more breaks than usual the rest of the month, so I probably won't make any substantial progress until September. And to be honest, I'd be surprised if I have the release out before October. But, hey, you can already use the app! If you want to follow the progress, you can subscribe to the issue on GitHub where I'll be posting about any new beta releases. Hopefully, the next entry on this journal will be about the release and I'll finally close this behemoth of a task.
Thanks for following along!
Hey, so now that summer is over and I'm heading back into a more productive season, I thought it was time for a new update.
Since the last time I wrote here, I've been on and off work for holidays and such, but overall I've made good improvements. I got a lot of feedback in GitHub, and that's mostly what I've been tackling (thanks to everyone who contributed!). I also implemented a couple new features that arised from suggestions or pains whilst using the app myself. You can find everything I've done in the changelog, but here's some highlights:
- Added the ability to see all of someone's public recipes in the viewer, and also to publish unlisted recipes.
- Added a servings selector, which automatically recalculates ingredient quantities. It's a bit clunky, but I think it's a good experimental feature to have that isn't too intrusive.
- Added a settings modal to configure language, animations, error reporting, and proxying for importing recipes.
- Improved error handling.
- Improved a bunch of interoperability use-cases.
- Improved a bunch of accessibility issues.
From those, there are a couple that are interesting to delve into in the technical side.
The first one, interoperability. Overall, I was quite worried about this, and didn't want to dedicate a lot of time if anything difficult came up. But I have to say, at least with the tests I've done, it was easier than expected. You can get an idea of the things I did looking at the interoperability tests, maybe the most interesting is the ability to work with both http://schema.org
and https://schema.org
. There's been a lot of headaches with this in the RDF world, but in the end I found a decent way to do it. By default, my application will use https, but as soon as it finds a recipe using http, it will switch to that. This was very easy to achieve thanks to soukai. Because I have all the RDF stuff encapsulated in the library, I hardly had to change anything in the app to make this work. I could even have a setting to let users choose which one to use, but I decided not to include it to avoid confusing people. The frustrating part about interoperability, though, is that in part it's outside of my control. It'd be nice if other apps worked this way, but the reality is they won't. There's nothing I can do if they are not able to interpret the recipes generated in my app. Hopefully, in the future, this type of solutions will be included at the library or protocol level, so that the entire ecosystem can benefit.
Another thing worth mentioning is accessiblity. Some months ago, I learned about it in my job at Moodle. But I wasn't too sure of what I was doing, because even though we passed one of those corporate accessibility reviews, I didn't interact with real users with accessibility requirements. And I wasn't going to pay for this type of service for Umai. So I was very lucky to find someone who oferred to test it for free. And I was super glad to get his reply: "it's one of the easiest and most simple web apps I've ever used". π₯³ So yeah, that was nice. If you're interested to learn about a11y as well, I'd recommend getting started with these resources:
- BingO Bakery: Headings, Landmarks, and Tabs β Video introducing some basic concepts.
- ARIA patterns β Learn about common components.
- ARIA DevTools browser extension β Navigate a site like a non-sighted user would.
- headingMaps browser extension β Navigate page headings.
- Landmarks Navigation browser extension β Navigate landmarks.
- Accessibility Pane in Chrome β Inspect accessibility roles and values.
Something else that gave me some problems is that Inrupt's WebIds are not editable, which broke all my assumptions about WebIds and logging into Solid PODs. Logging in itself is not a problem, since Inrupt's library does all the heavy lifting, but when it comes to type indexes and such, it's a mess. It wasn't too hard to fix for now, but this is unfortunately still under discussions so it may break again in the future. If you want to learn more about it, you can read this issue: solid/webid-profile#40.
And with all that, I'm confident now that the application is ready for its initial release. That is, functionality-wise. I still need to do all the releasy stuff: documentation, releasing stable versions of the dependencies, etc. I'm still not super happy with the stability and performance overall, because I think it's a bit clunky and I've seen more errors than I'd like while polishing. But I won't be working on any of this for now, because I want to get the first version out of the door already.
Looking at this task's history, I realize I started working on it on December 8th, 2020. So I guess that's a decent deadline to keep in mind, and wouldn't it be funny to release it exactly on that day? π In any case, I'm in the home stretch now. If I manage not to get too caught up in the documentation, it should be done soon.
Here we are about to finish 2022 and this task isn't finished yet. So it's official that it's going to be a side-project spanning 3 years π±οΈ. But that's ok, it's not like this is the only thing I've been doing. And now that I've been using the app for a couple of months, I can say I'm happy with it. But in any case, I'll leave the postmortem for the next update, which I can promise now that will be the last!
During this month, as promised, I haven't worked on any new features. But that doesn't mean I haven't been busy.
On the one hand, I worked on the app itself because I noticed it was becoming slower each time I opened it. After some digging, I found a pretty gnarly performance issue that made it duplicate CRDT operations on every refresh :/. The root problem was that Solid PODs, and RDF in general, can't have duplicated triples in a document. This alludes to the monotonic property of RDF that Tim mentioned.
For example, the following two documents are equivalent:
<#my-recipe> schema:recipeIngredient "Tomatoes" .
<#my-recipe> schema:recipeIngredient "Tomatoes", "Tomatoes" .
But in JavaScript, that's not true. So my code thought there was an ingredient missing, and tried to write it indefinitely.
These are the type of things that make me second-guess adding complexity. But it seems like most of the time I end up adding it anyways. At least when it comes to code, I think I'm much better at keeping UI and features simple. But it's also true that these are the type of things that help me learn more about RDF and harden soukai for more edge-cases. Or at least, that's what I tell myself.
Other than that, I've also been struggling with JavaScript bundling. I'll be honest, the current status of publishing JavaScript packages is awful. Even though I've spent countless hours trying to work around bundling issues, I still haven't cracked the nut. The current solution I found is good enough, but there are still some use-cases where I know it's problematic (for example, it doesn't work with vite-node). But I don't think I'll be wasting any more time on this for the time being.
Something else I've been doing is publishing my vocab. Which means I've been able to work with Laravel again ποΈ. If I just mentioned how difficult it is to work with JavaScript libraries, I can say the complete opposite for Laravel and PHP. In the last 3 years or so, I haven't used much Laravel. But still, every time I do, it's so easy to work with. In JavaScript, it's been a struggle just finding libraries to parse and serialize RDF. But in PHP, it took me less than a minute to find one library that parses and serializes all formats! And the library is adequately called EasyRDF. So that was nice :).
In case you're wondering, I did mention before that I would try to contribute this vocab to the Solid spec. But 7 months after opening the PR it doesn't seem to be going anywhere, so I decided to just publish it on my own domain. One of the doubts I had about it is that I wasn't familiar with best practices for publishing vocabs. But Angelo shared the following document that solved most of my doubts: Cool URIs.
Now that all that's done, the only thing that's missing is mostly writing documentation. That can take a long time or almost nothing, so I'll give it a couple of weeks at most. If anything, I can tell you the hard deadline for the release. And that is February 4th.
Hey, I bet you didn't expect to hear from me again this year (I didn't either). But I've got some news to share! I submitted a talk proposal for FOSDEM, and I'm glad to announce that it's been accepted π₯³!
I do admit the title is a bit cheeky, but hey, you've got to create some hype! Also, I couldn't come up with a better name π οΈ. But it does communicate pretty well what the talk will be about. I'll share everything I've learned since I started with Solid until now. It should be fun :).
So yeah, Umai will definitely be released by then.
FOSDEM will be held the 4th & 5th of February in Brussels. I hope to see you there :D.
The day has finally arrived, I'm finally closing this task!
Like always, there was more work than I expected. But thanks to having a real deadline, I've used the scope hammer like never before.
One of the things that fell by the wayside was documentation :(. Usually, I write some documentation for my apps in case anyone is interested in the technical details. But if I have to be honest, I'm not sure how helpful that is anyways (or if anyone's even read it). So this time, I didn't do any of that for Umai. But I've updated soukai
and soukai-solid
for the new versions, so it isn't like the documentation is completely out of date. In the future, I intend to consolidate all my apps into some kind of framework, so I'll probably work in better documentation at that point.
I've also been translating the app into Spanish and Catalan, which was more work than I expected. The good news is that after a first sitting, I don't think it'll take me a lot of work to maintain. I also wrote a small translations contribution guide. It's the first time I'm doing this and from personal experience, I'm not very happy with community-contributed translations. So we'll see how that goes.
I've also been working on implementing an Application ClientID. I wasn't too happy about this, because I've been making the app in a way that can run in any domain (even localhost). So I don't like the idea of having to tie an application with a url. After some thought, though, I realized it isn't that different from a Web Manifest. So I implemented it similarly (by the way, I did write my first Vite Plugin for this, and I have to say it was super easy β it definetly deserves coming out on top in the recent State of JS survey :D). But in the end, it wasn't as nice as I wanted. With a Web Manifest you can use relative urls, but ClientID documents require the client_id
field to be absolute. Which means that if anyone wants to self-host Umai, they'll have to build it themselves, instead of just downloading a .zip file :(. In any case, that's done!
So, before closing this task, I wanted to do some reflections like I did for Media Kraken.
I haven't been tracking time spent as closely, but one rough estimation gets me to 1000 hours O.o. In contrast with the 278 I spent for Media Kraken, it's almost 4 times as much. The scope for this was a lot bigger though: Vue 3, Vite, CRDTs, Offline First, Page Transitions, Files Upload, Permissions Management, Requests Proxy, Custom Ontology, etc. But if I translate that to full-time work, it comes out to about 6 months. That's definitely a non-trivial amount of time. Does the end result justify it? Well, I'm very happy with the app, but probably not. If I have to get anything out of this, it's that I overegineer too much. Which I already knew, but I think it's time to start taking it seriously. The good news is that I don't think I'll be tackling anything more complicated than Solid CRDTs in my upcoming endeavours. But then again, I also thought I was done with complexity when I finished Media Kraken.
Something else I talked about last time was my opinion of the Solid Community. Sadly, I can't say things have improved too much. I've had a much better experience with Solid this time around, but that's because I've been using my own libraries (and I'm biased). CSS has also come out, which has been pleasant to use. But other than that, I don't see much improvement in the overall DX, and things seem to be moving as slowly as ever. I also haven't seen any new apps I'm excited for. If anything, I've been disappointed.
But I don't want that to sound discouraging. Even with all that, I still think Solid is the best approach we have to making apps. Or at least, the type of apps I want to make (and use). The fundamentals are there, so I don't think there's anything stopping anyone from getting into Solid; even if their introduction isn't as smooth as I'd like. So I'll continue using Solid, and I'm still excited about the future.
And that's it for now! Feel free to spread the word, Umai is out!
Task completed
Task started