This was somewhat harder than you'd imagine, because ConverseJS
and Strophe have changed a bit in the meantime. Nothing major, but
a small change had unintended consequences.
Some backstory: we use Rollup for bundling Convo into a format
that KaiOS' ageing engine can understand. The bad news: due to
a weird bug/issue/something, Rollup doesn't like the UMD format
that ConverseJS dependency Strophe bundles into. This bug has
been affecting a lot of people and has been extensively documented
in issues like this one:
https://github.com/rollup/rollup-plugin-commonjs/issues/139
(I think Strophe also bundles into other formats, but for some
reason the default one picked by Rollup is dist/strophe.umd.js
that it then immediately raises a complaint about).
The good news was that @converse/headless was inadvertently
sidestepping this issue because it imported the Strophe object
from a specific file in Strophe's src like this:
import { Strophe } from 'strophe.js/src/strophe';
which completely bypassed the problematic dist folder. Since
Rollup eventually bundled everything anyway, reading from source
to start with wasn't a problem.
The bad news was that, at some point, ConverseJS was updated to
use a newer version of Strophe. Unfortunately, the new Strophe
had renamed its src/strophe.js to src/index.js:
26f94b15f0
That is in itself a good practice, but it broke the import in
@converse/headless, which was still looking for the old file
instead of the new one.
Not a big problem either; a few days later @converse/headless
implemented the best practice of importing straight from
'strophe' instead of trying to specify where exacly to import
from, allowing the Strophe package to take on that job:
72f659cd1a
The only problem is, that "take it from wherever the package
decides" proved disastrous for Convo's Rollup setup, because
Rollup stopped looking in src/ at all. Instead, it went straight
to the dist/ folder like any sensible bundler would do. Except
that the first option in the dist/ folder happened to be the
strophe.umd.js file that Rollup had issues with as mentioned in
the beginning of this commit message!
I didn't know all this backstory at the time. All I knew was that
Rollup was producing the somewhat cryptic error:
'Strophe' is not exported by node_modules/strophe.js/dist/strophe.umd.js
which made no sense to me because of course it was!
With some DuckDuckGo'ing I realised that this is a not uncommon
problem with UMD packages in Rollup when using the commonjs
plugin. But though the problem was quite common, the workarounds
weren't. None of the workarounds (I wouldn't deign to call them
solutions) that were described, semi-described, or hinted-at
worked for me.
After sleeping over it, I decided to inspect the ConverseJS code
and see what exacly changed, which is when I realised the whole
setup described above.
The hacky solution implemented here is to override the clean
import { ... } from 'strophe.js';
statements with the more explicit
import { ... } from 'strophe.js/src';
...which is at least somewhat cleaner, given that 'strophe.js/src'
automatically resolves to 'strophe.js/src/index.js'.
In conclusion: bumping to a newer version of a JavaScript
dependency, despite being a seemingly innocous task, can be way
more complicated than you'd expect!
In our case, "list icons" usually means "avatars" so think
"rounded avatars, except the currently selected one which remains
square". This makes things look a bit more polished, but still
allow you to properly see the full avatar when you actually need
to.
By default, the back button exits the entire chat, which is an
overkill if we're just in the menu. This commit makes the options
menu catch the backspace event and simply close the menu instead
of letting it go all the way to closing the conversation.
Along the way, the function was also neatened a bit to use breaks
instead of returns.
Earlier, the toggle was only happening from the outside, but now
we can toggle the options menu from the menu itself. This is
useful when we want to close the menu from inside, for example
when an option has been selected or the back button has been
pressed.
It was supposed to be optional anyway, but setting it as undefined
to begin with makes it explicitly optional so Svelte doesn't start
giving warnings and stuff.
We optimise memory usage by only downloading the resolution of
image that needs to be displayed, rather than the full image. This
is described in a bit more detail on the kaios.dev blog:
https://kaios.dev/2023/04/kaios-app-optimization-tips/#media-fragments--moz-samplesize
However, images are loaded immediately without confirmation. We
might want to change that in future to check the file size, etc.
before attempting to display an image, which is what many XMPP
apps default to doing.
This commit implements #16, albeit minimally.
There are actually three kinds of list-item classes in the
kaios-native-ui stylesheet: ordinary, with indicator (a little
caret pointing to the side), and with icon (which lets you set an
image such as a profile picture, hint hint). Till now the ListItem
implementation had mixed it up a little, using the indicator
styles for an ordinary non-indicator list. Now, the code has been
revamped to properly support all three.
I can do this confidently now that I've figured out how to test
them live on the phone! (Colours there render pretty differently
from the ones on my laptop screen, so I've got to view it directly
to make it work.)
I don't know what the placeholder ones are all about, but the
vcards work well – including for own avatars! (You won't be in
your own contact list, but you will have your own vcard.)
With this, we close#15, the very freshly created issue.
I usually don't use semicolons for terminating JavaScript
statements, but this time I got nervous. (There are a few times
when it's needed to resolve ambiguity; if you look hard in the
last few commits I think you'll find one example.)
Just to make it not-blank! In future we should be displaying the
actual XMPP avatars here
This is the final feature on the to-do list for and therefore
closes#2
Partially addresses the concerns in #2! Own messages are now on
the opposite side in their own colour, and system messages (such
as "* is online", detected by having no "from" attribute) are
displayed without any bubble at all.
Along the way, we also fixed bug #14 where own messages would
show the other person's name underneath. That was because we were
looking at the nickname attribute, which, as it turns out, gives
the nickname for the entire conversation or something, not for the
chat itself. I think it wasn't a problem in groupchats, but anyway,
that's something to tackle another day...
This navigation overrides the main navigation, making it act as a
modal. This is complicated, so let's not try to do it again
anywhere else besides these options menus :P
Well actually not that complicated, but it is a bit of duplicated
work and feels a little hacky so I'm not entirely comfortable with
it. Maybe there's a DRYer way to get it working though?
Not that we'll need to change the height, but we need to refer to
it in some places and variables makes it more clear what exactly
we're referring to (as opposed to bare numbers).
Explanation for what exactly happened: I added a margin to the
header, made the app fullscreen, and activated a magic setting in
the manifest to render the system statusbar on *top of* the app.
Thanks to the following blog post for teaching me how:
https://kaios.dev/2023/03/complete-manifest.webapp-guide/
This "feature" of capitalising the first letter came from the
kaios-native-ui project, but in this case it's more like a bug
than a feature because case is important in things like usernames
and JIDs (as well as conversation titles, since people deserve the
right to be fancy).
If we decide to import more things in future, it'll be neater to
change it. (Full disclosure: I did in fact attempt to import an
arrowsStore; when that was abandoned I decided to retain the
formatting anyway so I didn't have to do it next time. The time
thus saved has been utilised in the composition of this commit
message.)
This allows you to view and start new conversations with contacts,
although it doesn't let implement a sliding window or allow you to
search through then.
Instead of rendering the message list every time a message comes
in, we give a delay of 5 seconds. If more messages have arrived in
the meantime, those messages will also get included in the render.
5 seconds is selected to offer the best performance; if it ends
up being too long we could later make it configurable or bring it
down to something like 1 second. Another option is to intelligently
ramp up the delays as messages flood in, and then ramp it down when
the flow reduces to just 1-2 messages. But all that is for another
day ;)
This commit hopefully addresses #10; it should theoretically work
but I haven't been able to fully test it yet due to not yet
receiving a full flood of messages :P
All this is in order to solve the big problem with Convo, which
was that a large number of messages coming in (as is the case with
highly active groupchats) would overload the entire app and cause
it to crash.
To fix this, I've taken the somewhat draconian measure of not
auto-joining any groupchats at all. Of course, this should be
better optimised in future, starting with an option to join
specific groupchats, and later perhaps allowing the user to "pin"
one or two important groupchats, or have some kind of cyclical
function that catches up on one groupchat at a time, checks for
important mentions, and then closes it to save memory.
Another option would be to load the groupchat data but offload it
from the working memory; unfortunately that'd require a greater
knowledge of the internal workings of ConverseJS than I currently
posess.
Instead of running the entire toArray of the chatboxes object, we
only render the information we need (id, title, last message text)
which will (hopefully) make for a less resource-hungry function
This is primarily so that we can read the entire chat message that
comes in, not just the beginning part that fits on the screen. The
implementation is still a bit jumpy and needs some work, but at
least you can now use it to have a functional conversation.
Addresses #3 but maybe needs more work.
The original kaios-native-ui was using a fancy mask-image property,
which was unfortunately a bit *too* fancy for KaiOS to handle.
Instead, we've fallen back to good ol' conditions and a number of
new SVGs for the selected version of the checkbox. The drawback is,
of course, that selected checkboxes will perforce be white (which
can be easily overridden to black if so desired, but not so easily
to any other colour: Inkscape is your friend though).
This gives us something prettier than a blank page while the app
is loading (although, of course, it could also prove a bit more
cryptic to debug if something goes wrong).
Closes#5
Earlier, we were using the centre softkey but this is a bit too
easy to trigger by mistake. Now, we use the centre softkey for
the more mundane but arguably equally important "Enter"...which
doesn't actually work because we're using a single-line input and
not a multi-line textarea, but we'll get to that sometime. Or,
maybe the centre key can become something more funky such as...
an emoji selector?
Fixes#4
They were in the main dependencies, which is not really necessary.
Come to think of it, *no* dependencies should be necessary once
the thing is compiled, but the template which this project is
based on kept `sirv` as a main dependency, so I'm keeping it there.
I guess you could argue that sirv is necessary for runtime if
you're going to be running this application on a desktop for some
reason and need someway to serve the app to you.
These helpers are there in the main ConverseJS utils but not in
the headless one; unfortunately they are used in some cases
(while parsing inline emoji) so not having them causes an error.
Ideally we'd patch ConverseJS to include this in the headless
version too, but for now as a quick-fix we're just defining
everything directly in our converse.js itself.
This is the mechanism to automatically scroll to the bottom of
the page when a new message comes in. This is now done by
listening for a change on the "mesages" element and acting upon
that. It's still a bit hacky like the earlier one, but at least
the hack is all in one place.
This is instead of using the stanza directly. To get this to work,
we had to import a couple of other util libraries just so that
they assign their respective utils to Converse's `u` object. This
is probably supposed to happen by itself when the respective
plugins are loaded, but for whatever reason it wasn't happening so
here we are.
We already defined window._converse earlier in the file. (In any
case, both these are temporary globals for debugging; once done
we ideally won't have a floating public _converse like that. Come
to think of it, we won't have a floating converse without the
underscore either!)