_blogs
// blogs / 20260827.md
// blogs / 20260827.md
Dev Log: August 27 Wrap-up
Overview
Today was a bit of a mix—jumping between some deep-tissue maintenance on my personal expense tracker and some architectural refactoring at work. I spent a good chunk of time modernizing the Android stack and then pivoted to fixing some annoying UI persistence issues in our web app.
What I Worked On
Modernizing the Android Stack
I finally bumped ExpenseTracker to version 1.4.0. Most of the work here was under the hood. I integrated ML Kit, which I'm excited to play with more, but the real "quality of life" update was migrating the Room database compiler from kapt to ksp. ksp is just faster and more Kotlin-friendly, and it felt good to strip out some of that old boilerplate.
I also ran into a sneaky issue where our CI wasn't picking up changes to the AndroidManifest.xml. Basically, the unit tests would stay UP-TO-DATE even if I changed permissions or listeners. I fixed it by explicitly telling Gradle to track the manifest file for those test tasks:
tasks.withType(Test).configureEach {
// Ensure manifest changes trigger a re-run for privacy-related tests
inputs.file("src/main/AndroidManifest.xml")
.withPathSensitivity(PathSensitivity.RELATIVE)
.withPropertyName("manifestInput")
}
Persisting the AI Chat Widget
On the web side of things, I had to deal with a UX annoyance where the AI chat widget was being destroyed and recreated every time a user navigated between pages. It was living in a base component that got torn down on every route change.
I moved it up to the AppComponent level. Now, it stays mounted throughout the entire session. The tricky part was making sure it didn't show up on the login screen, so I gated it behind a simple isLoggedIn check that only flips when the auth state actually changes. It's much smoother now—no more flickering chat bubbles when you're just clicking through menus.
Caching and Cleanup
I also spent some time in the template adaptor services. We're implementing a caching flag for datasources, but the incoming data was a mess—sometimes a boolean, sometimes a string "true". I added some logic to normalize that so the runtime doesn't get confused.
// Cleaning up the datasource config
if (component.datasource && typeof component.datasource === 'object') {
if (component.datasource.cachable === true || component.datasource.cachable === 'true') {
component.datasource.cachable = true;
} else {
// If it's not explicitly true, we don't want the key at all
delete component.datasource.cachable;
}
}
Finally, I deleted some dead code in the iframe loader. There was some legacy logic for handling third-party credentials that wasn't being used anymore. Nothing feels better than a negative line count at the end of a commit.
Wrapping Up
Solid day overall. The Android app feels a lot more modern with compileSdk 35 and KSP, and the web app's UI feels a bit more robust now that the global components aren't tethered to the page lifecycle. Tomorrow, I'll probably dive deeper into that ML Kit implementation. Sign-off for now.