It's a race to a real-time chat widget in this episode. Bryant tries to build his own version of the Intercom Messenger - a business messaging widget that includes live chat, helpful articles, and other rich content.
Speaker 0: Alright. Welcome back to yet another episode of 100 Apps, 100 Hours. I'm your host, Brian Gillespie, developer advocate here at Directus. And if you're new to the show, the basic gist is this, we take 60 minutes and we try to build or rebuild some of your favorite apps or some of your wildest app ideas, in that 60 minutes, we achieve our stated goals or we publicly fail trying. And by we, I mean me.
That's okay though. When I fail, I fail spectacularly, usually. But I've got 3 little girls so I'm used to being hobbled. That's okay. There are just two rules.
Number 1, you have 60 minutes to plan and build, which does pose some challenges. There's no more, no less. And number 2 is you use whatever you have at your disposal, just like in the real world. You know, make it work. Figure it out.
And in today's episode, we are going to take advantage of rule number 2. So what are we building today? We are working on the intercom Messenger. Right? Intercom is a, like, help desk, onboarding tool, messaging tool.
They've got all these things that are wrapped up into it. It looks like their latest positioning is, like, customer service, which is great. But like most of their it comes from this little messenger widget over here. Right? So it's not just live chat.
It is kind of like a rich format where they can check product news, they can chat with the support team, there's bots, there's AI, there are articles integrated, you know, there's, like, product tours and stuff like that. The the main things that we're concerned with here are this kind of nice interface for this, and then number 2, like this actual, like, live chat functionality and maybe the ability to add articles. So we keep the user in context, That's what we're gonna be tackling today. I I think it's doable, not a 100% sure. Let's go in and start the clock on this.
We will start our timer, 60 minutes and away we go. Alright. So as far as what type of functionality we want out of this, you know, what are the the jobs to be done or what are the goals for this specific project. We want to be able to chat chat with visitors in real time and then, you know, maybe display rich content inside the messenger. This could take the form of articles, help center stuff, updates, etcetera, and I totally cannot spell.
How are we gonna get there though? Right? Normally, if you've watched any of the past episodes, at this point, I'm like doing some data modeling. The starting point for today is a little different in that I've already got a project that we're going to lean on. It's called Agency OS and it already has a a fully fleshed out data model.
You can explore the front end of this at agencyos.dev. But this actual project you can download and test out yourself. It lives in our Directus Labs organization at, Directus dash labs slash agency dash os on GitHub. Basically, there's some instructions, but the main features of this include a website, CRM project tracker type of thing, and then you have a client portal. So on the front end, it kinda looks like this, your standard agency website.
You know, we've got all these different blocks. It uses the page builder inside Directus. We got blog posts and articles. You know, there's a a help center that should be displaying some articles that doesn't. And then you have, this client portal where clients can log in, keep track of their different projects, see files, see their invoices, pay for those specific invoices.
Right? This is this is pretty neat functionality. And then, you know, you would basically manage your entire workflow from your deals all the way through, like proposals, projects, and tasks inside your direct assistance. Right? So I can see a list of all the tasks that are necessary to complete this specific project, who's the organization, who are our contacts for that organization, And I can manage this whole thing here.
But in this case, I'm using this because we've already got this conversations model and this messages collection. And we have things like help center collections and help center articles already mapped out, which is is really nice, just to kind of piggyback off of what I've already built in the past. So if we dive into conversations, you know, those are gonna be I can have multiple conversations ongoing at once. Those would be like the different threads. Inside that we've got like a user that created it.
Maybe a visitor ID in this case, if the visitor is not known. We've got some messages inside there. Inside the messages, each message has a bit of text, who is the user or the visitor ID that created it, and away we go. That's what it looks like. We're gonna dive into like some of the real time features of Directus as well.
So what have I actually got set up for you here? In starting this project, I've got a Nuxt application that I often use as a starter here for this. You know, the Nuxt application just has a direct us client using the SDK already preconfigured with, authentication and rest. I've got some login and register pages, so we can kinda simulate that. But that's kind of it.
The other like 2 minutes worth of setup that I have done, this Agency OS project, before the V2 release, I had a janky little chat widget that I was trying to use in this specific project. So I can go back in time here and find this little chat widget. Let's just copy this in. And, it looks like I've already got it copied in, and cleaned up. But just a little chat box that we could use.
And, I could never quite get this to work before the big release, so, it it ended up getting scrapped. So let's revisit this. I've got our chat widget. Let's drop this thing into our index page. So if we open up this incognito window so I don't spoil any of the, so I'm not using that direct to session token basically.
Alright, so I've got my page here, I'm just going to drop this chat widget in there. And now I've just got this shell for a chat widget. It opens, it closes, it kinda looks nice. That's really all it does, right? There's this motionable component that uses the view motion View Use Motion, composables.
Where is it? View Use Motion at view use slash motion. Just to get like some smooth animations using, I think it uses pop motion the hood. And it's just, tailwind for styling, right? Our content is gonna go in here.
But let's fulfill that first thing, right? We wanna be able to chat with visitors in real time. I don't even know why these are bold, that's gonna aggravate me. Alright, how do we chat with visitors in real time? Directus has powerful real time functionality built in.
Now inside my Docker Compose file for this, I've already got real time enabled. So that is the WebSockets enabled part here. And when you're working with real time and direct us, there's 3 authentication modes. There's public, handshake, and strict. Public just means anybody can open a connection.
Handshake means that you don't have to provide the authentication as the you could still connect but the first message has to be authentication. And then strict means you have to pass the authentication before you actually initiate a connection. Alright, so what we're wanting to do here basically, we're gonna have to extend our plugin that we have. So I'm gonna go into my Directus plugin that I've got. And again, it's just creating a simple SDK client to work with.
I'm gonna go in here and let's actually create a second client. Create Directus Real Time Client. So I'm just gonna create a new Directus client and I'll just copy paste this previous client. Great. Except with, we're not gonna use any of this, we're gonna use the real time composable.
Alright? And there is an auth mode property in this case and I'm just gonna pass public because we want anybody to be able to connect to this. So I'm gonna create that client and then the next thing that I'm gonna do is provide that client to you, the Nuxt application. Let's just call it directus sws for directus web socket. And we're gonna pass directus ws.
Okay. So now we have created this client. Our chat widget still doesn't do anything. That's fine. What we're gonna do is open up chat widget and we're going to access that client.
So we'll do directus ws, because Nuxt provide when you use Nuxt provide it adds a dollar sign to the front of it. And we're just going to do use Nuxt app composable. That will give us access to our web socket client. And then we're going to actually open a connection, right. And this application is using like server side rendering inside Nuxt.
So I probably want to like wrap this up inside a composable as well like the like an on mounted hook. So we'll await the Directus WebSocket connect. And I I don't work with Directus real time a lot, so we may get some funky behavior here that needs to be async. Okay. So I'm just gonna open up the console and see if I open up all and I go to web socket, we can show a connection here.
It says switching protocols. Okay, AWS local host. Alright, so looks like we've got the WebSocket connection opened. Alright, now let's just kind of dig into the documentation a little bit. Because I the next thing is gonna be, like, subscribing, but we probably need some message handlers.
So we'll go into get started with real time on the documentation. I am doing this, just as you might. So you can see that we've got the real time client here. We've opened a connection to the client, or at least I think we have. And then now we've got like this client.
We need to listen for changes. Right? So I might as well just plop this in our on mounted hook. And I'm just gonna replace client dot with dollar sign directus ws. And now let's see what we actually get out of this.
Do we see any actual messages here? I don't know why am I not seeing any messages. Directus cannot connect when state is open. Do we need to do we actually need to pass the auth mode to this? Is that an actual property?
Switching protocols, directus.ws.subscribe. Let's subscribe to messages and see what happens. On message data dot undefined. Message. What if we just console log the message here?
Console log message. Alright. Alright. So we see we're not getting anything there. Subscribe error, invalid collection.
Okay. So not getting the correct collection. Let's just take a look at this. This is messages. Oh, let's try subscribing to messages, right?
On open console log event open. I don't know why it is not not sure why it's not working correctly. You do not have permission to access this. Right? So as a public user, we don't have access to the individual messages.
Alright, so two ways around this. We could potentially provide some authentication, if I can actually talk, right, or we could give access to this. Alright. So if I go into my access control settings, I'm gonna go to public in this case, and I'm not gonna give all access, but maybe we use custom. Right?
2 things here, we get the public can create messages, that's fine. But when it comes to reading messages, we don't want people to be able to read other people's conversations. So in this case, let's add just a rule to it. If the conversation, conversation visitor ID contains what? Can we even do that, right?
We'd have to pass the let's do this instead. We'll use our website API user for this. And okay. We'll do messages. You can see conversations.
I tell you what, let's just open the floodgates here just to test this out and make sure we can see these things. Alright, so we've got a subscription. There's our data, okay. And if I open this up and I add a new message, right, which is the test message, save this, we should see that data come through, which we do over here. Right?
So we see our subscription for the messages. Do we see our actual data here? Great. There's our text, test message. Here's the user that created that.
That's gonna be me, the admin user. And we've got the date created. Right? We've got all the details of this specific message. Alright.
So at least now we are getting the messages. Alright, if we go back to the documentation, we're getting some messages here. That's great. You know, a couple things when we create a subscription, right? We can choose what event to subscribe to.
So the event wise, we'll probably do like create. And we can even add a query, right. So imagine we've got a website visitor. We want to basically, like, create a unique session or a unique ID for that visitor, so that that user can only see their messages. Right?
So what do we need to do there? This might be something like a Nuxt Middleware. Do I have do I have UUIDs in this? Okay. So I've just got like a function in here and and this is, like a lot of this is actually ripped from the AgisUS project to begin with.
But I've just got a function here for generating an ID, which basically just creates like a UUIDv4. Alright, so what I'm gonna do here, let's just create like a session dot global dot ts. I'm gonna add this in the middleware folder inside Nuxt. And when I do this, if I use dot global that will run on every route. So I'm just gonna go here, I'm gonna copy this, and I don't really need to here.
Basically, anytime we navigate to a route, we wanna do some things. Right. We want to, let's check like a session ID or visitor ID. Visitor ID equals use cookie so we can set a cookie that way we can access it server and client side. We got visitor ID.
If visitor ID dot value, if there's no visitor ID dot value, we are going to set up a where is it? I'm gonna do Visitor.id.value equals generate ID. There. Okay. Alright so now basically we just created a middleware that's gonna run globally on every route within this application and it will create a cookie for the visitor ID.
Okay. And now I can see that here that I've got a visitor ID cookie which is some random UUID value. If I delete that, refresh, we can see we get a new visitor ID. But that cookie will persist across sessions, which is nice, and we don't have to do anything else there. Okay, so as far as our query, if we just go back to the documentation, right, we can add a query, and we can do all of the same query params that we would in the regular Directus API.
So we do something like filter where, well actually let's back up a minute, we need to get that cookie, right? So visitor ID is equal to use cookie visitor ID. Okay. And then for our filter, we want the messages. Let's think about this, right?
We're gonna have to create a conversation. Let's just get some messages first, right? So we're gonna add the visitor ID to each message. Alright. So maybe we just wanna be able to see we're gonna have to like create a conversation.
There's no way getting around that. Alright. How can we create items, right? So we can use that same connection and then we can actually get all of our like CRUD operations. So let's just do this.
We're gonna actually set this up. I wanna subscribe to 2 items, right? Conversations. And in this case, we want to filter where the visitor ID is visitor.id.value. So inside our data model, right, when we create a conversation there's a visitor ID for it so we could track that.
Great. Okay. So I could see that there. Then we have the individual messages, and we we've honestly got a lot of extra stuff that we're probably not even really gonna need here. But we got visitor.
Id_id, that's what it is inside our database. And then we've got the cookie value here. So visitor dot ID, and then the param is gonna be like this, equals to visor.id.value. And then I think we can also pass a UID to this subscription. Let's look at it.
Learn more about authentication, learn more about subscriptions with WebSockets. Alright. Using UID's type is subscribe multiple subscription UID. Okay. So we'll call this UID And this will be visitor conversations.
Thank you, GitHub Copilot. Let's also open a connection for messages as well. Subscribe messages where messages dot create, query, UID. And in this case we can't just filter out for the visitor messages, right? We also have to be able to show the admin messages as well.
So in this case what we're gonna do, we're gonna work on the conversations because we can use the related fields inside direct us collection. So I can go up a level here. It's actually conversation. Is what's the parameter gonna be? Right?
So we could have potentially multiple conversations. Right? So we'll have conversations equals ref, and then we'll have messages. Actually, we might just store all of those. Right?
Conversation, let's look for our query params, global query parameters for our actual filter here, filter rules, we're looking for items within an array. So we're going to use n, in conversations dot value map conversation dot ID. Okay. I think this should give us what we want. And if I open this now we can see that we've got a subscription for conversations, we've got a subscription for messages.
I'm gonna just copy the visitor ID here. And the other thing I'm gonna do, I'm gonna create a new conversation. So we'll say new conversation. I'm gonna add that visitor ID here. We'll call this test convo.
And do we get a message for that? We can see this conversation here. And now let's just test this as well. Will we get, TestConvo not from visitor? Just want to make sure.
Okay. So you can see I created a new conversation that didn't have that user ID or that visitor ID. And now we don't actually see that. So how are we doing on time? We got about 35 minutes left.
We're looking okay. So let's start having some conversations. Right? On messages, on init, Subscription init. Get conversations.
Alright, subscribe on open on message. Okay, so basically all we're doing at this point is listening for messages. And just logging that out, right? So we need to do some other things here. Is there like a is there another property on a knit?
No, a knit. I need more practice with real time, obviously. Alright. So when we receive the message, let's look and see what our references are here. Full code sample, I think there's actually a guide that I built on multi user chat or somebody built this.
Alright. Connect cleanup, Subscribe. Okay. So you can get the actual subscription equals await client dot subscribe. Constant subscription equals ws await client.subscribe.
And then we can get our initial messages. So here, let's just call this get initial combos. And we might also want to grab all the fields within that as well. So we're gonna get all the root level fields for the conversation, and then we're also gonna get all the message values included in that. And then let's populate conversations.value=subscription.data.
I'm not even sure it's gonna be that. We'll just do it's actually console log subscription. See what we get there. Alright, subscription L2 generator. Cons to subscription.
Subscription, unsubscribe, generator state suspended. For message of subscription, for await combo of subscription push combo. Subscription is not iterable. Let's just wrap it like in the documentation here and see what we actually get. Right?
So now if I show let's just show the actual conversations. So we'll go into our content section, and we'll do divv4conversationinconversation. Flex call. Let's just add let's just log that out in conversation. Prepping the pretag.
Type subscription event, init. Yeah. So this might need to actually be updated. Right? Subscription event, await client subscribe.
Subscription Subscribe to messages, create event. What am I missing here? Subscribe if type. This message is create, receive message. Subscription started.
Okay. We're not actually getting the message history there. Display historical messages. Okay. Alright.
So basically to get all of the historical messages, we need to read the messages. So we'll do client dot, it's actually gonna be directus dot send message, and then we'll get all of the items within the conversations. So that'll just be items. The collection will be conversations. And the action is gonna be read here.
So we just wanna read all of the conversations. And then as far as the query, we'll set a filter where visitor ID equals visitor ID and we'll just get all of those conversations. And maybe we limit those to like the past 10 conversations, kinda like our documentation here. Alright. Send message.
Okay. So we're gonna get that message. Send message. There's our items that we could see. There's those conversations there.
We could see the individual messages within those, but we're not actually populating that data somewhere. Right? So we're going to need on our message function here, basically if the data type equals items, okay, so when we receive a message, we are going to take our message. Alright. If the message dot type conversations there we go.
GitHub Copilot knows what we want. And we'll just refresh when we open the connection. If I go in and we look for chat widget inside the dev tools, do we see our conversations? We don't. Alright.
Why not? Message. Message, message. If let's just update this as well. Is connected.
State is not open. Okay. So we're getting our messages. They're just not populating to what we want. Right?
So we see the data is an array. That should be what we want though. If we open this up, we should be seeing those conversations. Direct to send messages, items, conversations, if equals conversations, conversations dot value equals messages dot data. Conversations.
Yeah. That should be right. Collection equals conversations message dot type. What is the the type equals items? Oh, we don't really have a, maybe we wanna add a UID.
Type equal to UID equals get combos. Alright. So now if we take a look at that, right, we should have that UID get combos. And we could just target it that way. If message dotuid equals get combos, and now we populate that data.
Okay. Alright. So now we can see a conversation there. That was a lot of stress just to get this conversation. And let's just go in, and I'm gonna add, a message to this.
Hi. Hey. Okay. Now if I refresh, we can see the actual messages within that. That's solid.
Okay. And then we wanna do something like this where we have selected convo equals ref. Let's just call it conversation. Okay. Solid.
Alright. So now we need to actually like start showing our our conversations. Right? So within this, we want to maybe wrap this up. Right?
We'll do it in like a template tag. If there is no if no selected conversation, We're gonna show this information. Then we'll come down and do another template v if selected conversation will show something else. Alright. So instead of our actual conversations here, what if we show we wanna add a button.
Do we want to make it a button? Yeah, we do. And then we'll do like the conversation p conversation dot title, I think we've got a title for that. Alright, so we refresh, we see test convo. Let's give each one of these a lot of padding.
Okay. So we'll do like py6px4. You know, maybe we wrap these again. Div. Do like a divide y situation.
Okay. Test convo. Test messages dot text. And, you know, maybe we show like the user avatar as well. Alright, so again, like we might have to drill down into this a little further and do messages dot user created, user underscore created.
And this should give us like all the actual user data in addition to that. Right? So I wanna make sure that we've got let's see if we can get, like, the user avatar source equals, use files, conversations dot messages dot user ID. Can we show the user avatar? Where does this use files to?
File ID. Alright, let's just check the messages real quick. Chat widget, conversations, messages. Are we getting user created? We're not getting that information, so we probably need to control that with access control as well.
Alright. Users. Again, we're just gonna open the floodgates. Would not do this on a regular app. Make sure what other rules do we have here?
Public. Okay. And I really don't even have a I don't think the admin user has an avatar here either. So let's just add one for that. All files, just added avatar for me.
Refresh. Are we actually getting that avatar? We're not. Anonymous component chat widgets. We'll go in.
Here's our conversations. Here's our messages. We'll get the user created. We could see the avatar, access control public, Directus files. Let's just give all access.
Yep. Still not showing it. Use files. Let's just call it, like, constant file. It may not even be able to use files.
What is actually this? Get probably need to look at the actual file URL. That's the actual method here. That's why I'm not getting that correct. Alright.
So file URL. Okay. There we go. Alright. So that's not necessarily pretty or amazing.
Let's flex it, test convo. Maybe we'll just show like the number of messages dot length. And wrap this. And then we do, p, maybe like get I got a getRelativeTime conversation. Oops.
Just got to wrap that. Get relative time, conversation, date created. 16 minutes ago, we'll add a little bit of gap here. And let's do text left. Okay.
Alright, so we got the test convo and now when I click the button at click, we want to set the selected conversation to the conversation dot ID. Okay. And now we can see that goes away. And now we just need to show the messages within that, right? So selected, let's do a conversation messages.
Yep, get up Copilot knows what I want to do. If that is has no value, we're gonna return conversation messages. Let's see. Select a conversation dot value, conversations dot ID. Okay.
Return conversation messages. Okay. Yeah. Sometimes AI is not to be trusted. Sometimes it's okay.
Alright, so then let's just show the actual messages, right? Do the same thing. Let's copy this down. Div. Alright.
And then we're gonna show we'll wrap this. And then we need a div for each message. So v for message in selected conversations, key equals message ID. Let's see what this shows us. Right?
We've got the test conversation, selected conversation messages. Alright, so I select that. We can see this. We don't have the message text. Hey, there's the message text.
We're going to need like some padding here. Okay. Maybe we wanna wrap each message in like a let's add just a little bit of padding. We'll wrap each message in white light gray, gray 100. And give it a little bit of padding.
P p 2, Rounded XL. Okay. So there's our messages. How are we doing on time? We got like 17 minutes.
We are quickly running out of time, right? So now the bottom of this, we can see the messages generated from the user here. And the other thing that I wanna do here is when we send a new message, or when we receive a message, right, if this is the, so we whenever we create new messages, it needs to be we're subscribing to the messages. Alright. So here, we're gonna need to pass the messages are received.
We wanna add those add them to the proper conversation. Alright. So what is GitHub Copilot coming up with? We got conversations dot value dot find, message dot data dot conversation. Okay.
If conversation dot push. All right, let's just test this out and see if we're getting what we actually want here. Local host 8055. Alright. So we got our convo there.
I could click into it and see our test message. And now, like hopefully if I go in and add another message here and just save, We should get that, but we're not seeing that message. Test combo, yo. Why are we not receiving that? Console dot log dot message, web socket messages.
Conversation In That should be a message of the conversations. Subscribe. Is that after maybe we need to wait getting those initial messages? All right, so let's open this up. Now we can see both of those messages, right?
I do tttt, we're still not getting that message. So let's just drop this for now. We would come back and clean this up, but I I just wanna actually get the messages. Alright. T t t.
Test. So we're getting that visitor messages array. It's just not populating to the proper conversation. Alright. So don't trust GitHub.
If git combo and messages what's the actual message that we received? Git convo subscription event dot create visitor messages. It's basically we need to use that UID again. UID, those visitor messages and message data. I think that should take care of it.
Test convo. Still not getting that. Right? Chat widget. Select a conversation messages, select a conversation, conversation.
It's just not populating those messages here. What are we doing wrong? What are we doing wrong? If conversation oh, conversation dot value? Conversations dot value dot fun.
That's not really gonna work, is it? So we need to like we're getting the conversation value. So here's the message. Here's the data. We're getting back an array of data actually.
Conversations dot value dot find data. Message dot data is an array. Okay. Conversation. I don't even know if this is actually gonna work, is it?
That shouldn't work. We'll test it and see. Yeah. It's not gonna work. Promise dot avatar.
So we need to definitely add that as well. To our query, we're gonna add the same thing or similar, where we have fields, we're gonna have user created dot dollar sign. Okay. So we got our test convo. Can we receive messages from Reading an avatar.
Man. Okay. Conversation dot messages dot push. Why is it not seeing the avatar? Chat widget, conversations, messages within that.
It's pushing that into the array, message.data.0. That would be why. Alright. One more time again. I'm just gonna delete some of these.
Save it. We refresh over here. We can see there's our convo. We do yo. We save.
Okay. So now over here on the client, I can see that conversation or that message being populated. So we'll just add a little gap between these space space y 2. Okay. And we'll probably need to do like message dot user created dot if we have a message user dot created and message dot user created dot avatar.
Alright, so we're only gonna show that if we have it. And then the next thing that we need to do in a hurry here, about 9 minutes, is to actually start populating these messages, right? So we want to add some form fields to this down here at the bottom somewhere. So if we're inside the selected conversation, great, let's add the message form. Cool.
Alright, and we'll do, I've got this nice Nuxt UI library here. So we'll do Submit and Message and it just gives us some nice inputs here. Flex column, there's the wrapper. Where is this actually gonna be? Inset 0, uform, send message, v model, new message.
Alright, so let's add our new message. New message equals ref, that'll be blank. Let's have a function for send message. Okay, send messages, create conversation, select a conversation dot value, new message dot value, user created. Nope, Where did you go?
We can rely on AI here. And we don't want a UUID or UID for this. Oh, maybe we do. Send message. And we're we're not gonna use the user created.
Alright. Does this give us, where's my little bar at? User, User selected conversation. There's no selected conversations. Vfusermessage.created.
Oh, message dot user created dot avatar. Okay. So now we're going to form. The form should actually be pushed to the bottom. So that'll be flex 1 height full.
Okay. Then inside this form, I want that to flex, justify between. Okay. And we add a bit of padding to that as well, p 2. Give it a border, border t.
And we stick it to the bottom. Nope. This will be like overflow. Why? Auto.
Okay. Flex justify between, and this could probably even be like stuck to the bottom in the absolute. Alright. So let's just change this to like text area. Hey hey hey.
And now we can see we can actually chat with that specific person, inside our send message, we're probably gonna delete that new message as well. Just set that value once that's actually populating. Okay. So now I can see my convos. I can open up that convo.
I can send a message to their team. Great. How could we actually show the help center articles? Right? In 5 minutes, can we get this rich content within there?
We're going to be pushing that, right? Pushing it quite a bit. Alright, so we got our chat widget. We're probably gonna need like a page functionality or something. Like hey, what page are we on?
The default is, let's set that to chat. So this will be this is our conversations. And can we wrap this in another template tag? Or it might be easier just to stick it inside a div. Alright, so div if page equals chat, We're gonna show that.
Alright, so we should still see that now when we open this up. Still see that. Great. But let's just show another page. Div v if page equals articles.
Alright. So then we're gonna show the articles. Maybe we show like a card v 4, Article in articles. I don't think any of these are actually gonna apply. View card, we'll just do like a p tag for the article dot title And see what this shows.
Key is equal to article dot ID. Alright. And now we need like some kind of widget at the bottom of this to actually show, right? We need like a menu. So let's do div, do you like buttons?
This is not at all. Show an icon. Name equals herocon's chat bubble left ellipsis. Let's just call this chat. And actually, and then we need a new button for icon name equals articles.
How are we doing on time? 3 minutes. Are we gonna actually get this or not? Let's see what this shows. Chat.
This is gonna be absolute bottom 0, p 4flexgap4, give that a little gap. I don't particularly like the icons for this. Let's just okay. And then if click at click, this is gonna be page equals articles. There we go.
And at click page is equal to chat. Okay. Is that actually gonna work? Page is equal to chat. Page is equal to articles.
Okay. So now we're switching those back and forth. We just don't have any articles, right? So let's use our client here. We've got articles.
We could do a ref. Just make that an array. And then we'll have like a function, get articles. And we can use that same connection again. Right?
Fetch articles. Yeah. I could use the regular rest connection as well in this case. But let's try to use this. Send message.
We want collection of articles. This is gonna be a read. And the data, UID, we'll just do get articles. Okay. Oh, how we doing?
We got 1 minute 20 seconds. Get articles. That's our function. Okay. And then on our receive bit here, if message dot UID and message data articles dot value dot messages.
Okay. What else do we what else do we need to do here? We need to fetch those articles first, right? So let's do like a watcher. Watch page.
New page. If page equals articles, get articles. Is this actually gonna work? Error. Invalid collection.
Oh, no. We don't have articles. They're what? Post. Post.
Post. So what is this? There we go. With what time to spare? 21 seconds.
Right? Okay. So we can actually see a list of the articles. That's about as good as we're going to get in in this amount of time left. I'm calling that a win.
So we can actually show the list of articles here. The next part would be to just probably actually, like, set these up to to, like, be able to read the rich text for those articles right within the little widget. But, you know, that is a wrap. Great. And if I open up Directus, we can see all the messages within that conversation.
And honestly, I would probably build some kind of interface over here inside Directus to be able to manage this. Alright. So if we just wrap this one up, did we actually get to the intercom messenger? You know what? In an hour, almost impossible to rebuild.
I'm I'm calling that now unless you built it before. But, yeah, far cry. But hey, the basics are there. And it goes to show you just how, how far along you can get with Directus in an hour using the tools that are provided. So that's it for this episode of 100 Apps, 100 Hours.
Hope you enjoyed following along. You could probably see the sweat bubble up on my face at this point, but stay tuned for the next one. We'll see you.