Studio 9
Kerning game!
Play this game until the end: Record your score and share it with the class. Who got the highest score?
Decomposing emojis

You can find out how an emoji decomposes into its component characters with a single line of JS! This utlizes the spread operator to convert a string into an array of characters.
Play around with this a little bit: how many component characters does the most complex emoji you can find have?
Async and signals practice: Class Chatroom
We're going to give you some practice using async, signals, and also introduce you to the infrastructure we're using for messaging. By the end of class you will be able to send messages to each other!
We have prepared some basic starter code that does messaging locally. We're going to modify this code to simulate the lag you would experience with a real server to help build out a good async UI. Then with some small changes, we'll plug in code to connect to the real server and you will be able to to talk to each other!
Step 1: Basic Usability Improvements
Currently, if you test out the application, you
should be able to "send" messages and see them appear in a feed.
If you take a look at index.js you'll
see the messages are just local variables.
We will eventually replace that with real functionality,
but to get started, lets make this "fake" chat app
a little more usable.
- Safety:
- Disable the send button when the message text is empty.
- Efficiency:
- Clear the message once it has been sent
Step 2: Sorting the messages
Currently new messages are going to the bottom of the chat which means the user will not get any feedback that their message sent when the chat gets long enough.
Lets make them appear on the top so that they appear right under the chat form.
Create a computed signal
that transforms the messageObjects into an array that is sorted in reverse chronological
order according to the object.value.published field of each object.
To do the sorting, you can use the built-in toSorted
Array method.
You will need to pass a function to toSorted. This function compares two array
elements a and b. If the function returns a negative number, the a will be sorted
before b otherwise, b will be sorted before a.
Typically you will want to do something like
array.toSorted((a, b) => getMySortValue(b) - getMySortValue(a));
Step 3: Pretend Async
In the sendMessage function, we're going to add a delay to
simulate the time it will take to send a message when using a real server.
The common way to add a delay is with the following Promise:
new Promise((resolve) => setTimeout(resolve, 1000));
First, try to parse what's going on with this promise:
its actually passing a function to a function within a function... what??
What is the argument of the Promise constructor?
What type of variable is resolve?
You can also read the documentation for setTimeout.
Once you have tried to wrap your head around it,
add the promise within sendMessage.
Does it add a delay? Probably not... remember to use the
async and await keywords!
Step 4: Loading...
Currently our app is a bit frustrating to use. When you click the send button nothing happens for a little bit and then suddenly things change. To fix all these issues, we need to provide feedback to the user.
Create another signal called isSending and set that signal to true when
the message is first sent and false when the send finishes.
Then use that signal to display some sort of feedback. This can be changing the text from "Send" to "Sending...".
Step 5: Preventing bugs and race conditions
Currently, the user can still send messages while the message is
being sent.
This can lead to a user sending messages multiple times, and
thus is a safety issue.
You want to prevent them from doing this,
by disabling relevant parts of the UI when they should not be interacted with using the isSending signal.
Step 6: Graffiti
Almost there!! We need to replace our "simulated" message functionality with real functionality.
For sending, we can swap out
messageObjects.push() with the following code.
It essentially does the same thing but instead
of adding your message to the local messageObjects array,
it posts it to a shared "channel".
To be able to chat with others in your studio, use the channel designftw-26-studio-NUMBER where number is the
studio number you are in (Friday - 1, Thursday@12:30 - 2, Thursday@2 - 3)
await graffiti.post(
{
value: {
content: myMessage.value,
published: Date.now(),
},
channels: [ `designftw-26-studio-NUMBER` ]
},
session.value,
);
For receiving, we can replace const messageObjects = ref([]); with the following
code which discovers all the messages
in the channel. Since Graffiti lets you put all sorts of objects in the
shared channel, we are only discovering objects that match a particular JSONSchema so that we don't get objects where content or published might be undefined or include other data types.
const { objects: messageObjects } = useGraffitiDiscover(
[ `designftw-26-studio-NUMBER` ],
{
value: {
required: [ 'content', 'published' ],
properties: {
content: { type: "string" },
published: { type: "number" },
},
},
},
undefined, // If you wanted private messages, this is where you'd supply a "session"
true // automatically poll for new messages
);
Try it out and make sure it still works. Your messages should now persist across refreshes.
Step 7: Real Messaging
Finally change the graffiti variable at the bottom of the
code to use GraffitiDecentralized rather than GraffitiLocal and remove
the articial delay from step 3.
You will need to log in with a Graffiti account.
Do you see your peer's messages? Can you send messages?
Step 8: Other improvements
If you've gotten this far you now have a working chat app. Yay! Now you can start tweaking it. Some suggestions:
- Add a button delete your own messages with graffiti.delete.
- Try displaying the person's username associated with each object with the following component:
<graffiti-actor-to-handle :actor="object.actor"></graffiti-actor-to-handle>
- Most chat apps have a chat bar on the buttom and the messages flowing up. Add CSS to invert the chat messages. You will likely need to use
flex-direction: column-reverse - Display the timestamp of a message (
object.value.published) in a nice human-readable form. - Make the messages bubbly and add additional styling