Get stream, System message customisation stream-chat - node.js

community
I implemented stream-chat & stream-chat-react getStream
Hi, I want to show the system message on some events
For Ex: User A added User B to the chat (Here user A and User B is the name of both user but I don't want to send those message as it is because I want if user A changes their name to USER X then those previous messages also updated.) I want guidance on how I can achieve this.
Stream is allowing me to send system messages with the addMemberToChannel event but I am not able to find how I can use it for my specific case.
Expected Output:

For your case, you will avoid saving hardcoded data in the message's text property.
First, you create a system message on adding a user to a channel like this:
channel.addMembers([userId], {
text: 'added_users',
mentioned_users: [userId],
});
With the addMembers method on a channel object, you can add members to a channel and also pass a message object.
The message object accepts the message text and the mentioned_users properties.
You can use added_users or any text that you want to keep as a standard message for adding-members-system-message. You'll see why I use "added_users" in a second.
The Channel component renders system messages using the EventComponent. This component displays the text of the system message, with the date and some added styles.
You can create a custom event message component for your added_users message. This component can look like this:
import { EventComponent } from 'stream-chat-react';
function CustomEventMessage(props) {
const { message } = props;
const { text, mentioned_users, user } = message;
if (text === 'added_users') {
const message = user?.name + ' added ' + mentioned_users[0].name;
return (
<div className="str-chat__message--system">
<div className="str-chat__message--system__text">
<div className="str-chat__message--system__line"></div>
<p>{message}</p>
<div className="str-chat__message--system__line"></div>
</div>
</div>
);
}
// use the default event component
return <EventComponent {...props} />;
}
The CustomEventMessage component above accepts a message object prop which has the:
text: system message text
mentioned_users: mentioned users in the message
user: user who triggered the message
Next, you check if the text is added_users. If it is, then you provide a custom UI and message, which consists of the name of the user who triggered the message and the mentioned user (who was added to the channel)
I also used classnames from Stream's stylesheets so that I don't have to build mine.
Next, you add this component to the Channel component:
<Channel MessageSystem={CustomEventMessage}>
// ...
</Channel>
The message will now read as "Person X added Person Y" as seen in the image below:
If the text is not added_users, you pass the props to the default EventComponent.
Since you're saving the id of the mentioned user and not hardcoding the text ("Person A added Person B"), you will also get updated details about the users, even when they update their information.

Related

Update <ChannelList /> after channel freeze/unfreeze with frozen applied filter

I'm trying to build a chat frontend using the stream react components with the return of a ChatPage component that has this structure.
<ChatConfig config={config}>
<Chat client={client} customStyles={options?.theme}>
<ChannelList
Preview={CustomChannelPreview}
filters={filters}
sort={sort}
/>
<Channel>
<Window>
<CustomChannelHeader/>
<MessageList/>
<MessageInput Input={CustomMessageInput}/>
</Window>
<Thread/>
</Channel>
</Chat>
</ChatConfig>
So there are the channel list and the channel window in the same page where the user con freeze or unfreeze a channel.
The FE of the react application is capable of getting the filters to apply from the window.location.search so that the applied filters let the user view frozen or unfrozen channels only (e.g. {"frozen":{"$eq":false}, .....}).
When a user freezes or unfreezes, is there a way to re-render the channel list after the channel update to match the updated channel state?
Is it possible to do that without reimplementing the whole filtering strategy in the onChannelUpdated function?
Thanks in advance.
The ChannelList component accepts a key prop. When the value of this prop changes, the component re-renders and matches the updated channel state.
In your case, you can keep track of the channel list key state:
const generateUniqueChars = () => {
// any generation strategy you want here
}
const [channelListKey, setChannelListKey] = useState(generateUniqueChars())
const updateChannelListKey = () => {
setChannelListKey(generateUniqueChars())
}
Then, you pass the key to the ChannelList component:
<ChannelList key={channelListKey} />
Using React context, or any means you prefer, you can pass the updateChannelListKey function to the component that freezes/unfreezes a channel. When the freeze/unfreeze is triggered, you call the update channel list function, which updates the channel list key state, and re-renders the channel list to match the new state of channels.

Display username in the profile section in the navbar

I'm trying to display the username in the navbar. I managed to do it using my profile service like this:
let user of profileService.getProfile(), which uses the subscribe in order to retrieve the data.
However, the first time that I log in the name won't pop out. If I refresh the browser, the name will be displayed. I guess that it has something to do with the subscribe and its asynchronous nature, but if that's the case, what would be the best solution to the problem?
As you mentioned in the comment your getProfile() returns an observable.
You can subscribe to it in your component and then display the user name in your HTML component:
userName: string;
constructor(private profileService: ProfileService){}
ngOnInit(): void {
this.profileService.getProfile().subscribe((resp)=>{
//Assuming response only returns username
this.userName = resp;
});
}
and then in your HTML you can just bind to userName.
<div>
Logged in user name is: {{userName}}
</div>
I think that you can create a BehaviorSubject in service, it has a method getValue() that give a current value :
user$ : BehaviorSubject<String> = new BehaviorSubject("");
setProfile() {
this.user$.next("newName");
}
getProfile(): string {
return this.user$.getValue();
}
In View, you can use a pipe (AsyncPipe) to subscribe observable like :
{{ userOfProfileService$ | async }}
However, you can also use Subject observable without getProfile() but just with AsyncPipe in view.
user$ : Subject<String> = new Subject();

Make next input message part of callback_query function in Telegram Bot

I'm programming a Telegram Bot where it is shown to the user different options as inline buttons, one of them being "Search".
Once in search, I intend the next sent message to be the string to be used to search. However, I don't know how to make it so that the sent message is for the search query and not for other option/purposes.
If the bot file is read again from start every time a message or button is pressed, there is no way I can tell the bot that the next message is a message related to the pressed button (search) or a standard message not related to the pressed button (I don't know if I'm clear enough with my doubt or I'm making it more complicated to understand).
So relevant part of the code looks like this:
if(isset($request->callback_query->data)){
$data = $request->callback_query->data;
switch($data){
case 'search':
sendMessage($request->callback_query->message->chat->id, "Write what you want to search.");
//(HERE is where the program should "wait" for the user to input something to be used in a DB query)
//DBquery function
break;
}
}else{
keyboard($request->message->chat->id, "Choose one of the following options:", $keyboard);
}
$keyboard = array(
"inline_keyboard" => [
[
["text" => "\xF0\x9F\x94\x8E"." Search item","callback_data" => "search"],
["text" => "\xF0\x9F\x8C\x8F"." Visit web","url" => "https://website.com"]
//more options to be written
]
]
);

How can I display an input view on bixby with pre-populated selections AND a custom input field?

Currently I am trying to set up a few continuation actions after an interaction. One such continuation will send an email to the user with the results from the last action. I set up the action that will get an email address and send the email. To fill this input, i would like to offer the email addresses contained in the profile/self capsule as selection options and alternatively allow the user to input a custom email.
i set up a default input to get email addresses from the profile capsule and then offer those as options for the email input:
input (emailAddress) {
type (contact.EmailAddress)
instantiation-behavior (ShowDefaultValueDecision)
min (Required) max (One)
default-init {
intent {
goal: ReturnSelfEmail
}
}
}
Then i display them in an input-view using selection-of:
Here is my view:
input-view {
match: contact.EmailAddress(this) {
to-input: EmailResults
}
message ("What is your Email address?")
render{
selection-of (this) {
where-each (email) {
paragraph {
value {
template ("#{value(email)}")
}
style (Detail_M)
}
}
}
form {
elements {
text-input {
id (emailAddress)
label("Email Address")
required (true)
type (contact.EmailAddress)
}
}
on-submit {
goal: contact.EmailAddress
value: viv.core.FormElement(emailAddress)
}
}
}
}
In that view, I tried to add a form component as well but this does not work. Any ideas on how to have a custom input option in addition to the selections? Or another way to deal with a situation where the selections do not satisfy the user?
You can't render a form after you've rendered a selection-of.
My IDE shows this error - does yours?
"Unreachable statement" means that that part of the code will never run.
Anyways, one possible way to model this is to offer a selection view of the user's real email addresses, and one final entry which is a dummy email address called "input my own." (this uses selection-of)
If that selection is made, you can do throw an error in your JavaScript, and replan to a goal such as EnterCustomEmail. (this uses the form).
Here is another way to approach the problem.
In the result-view of the interaction (assuming that the email is sent at the end of the interaction), I would ask the user a followup question "Should I send a confirmation email to the <email> on your profile.?"
If the user responds "Yes" then the Action associated with the goal for the on-confirm will send an email to the user.
If the user says "No", the Action associated with the goal for on-deny should prompt the user for an email address.
This approach uses the conversational features of the Bixby platform and makes this interaction a more natural experience for the end-user.

What is the proper way to style the error message for Global Message?

I want to apply some script and styling for a specfic error message for the corresponding GlobalMessage class, and wanted to know the way to achieve this without disturbing other error messages or flash messages from this class.
Extend class de.hybris.platform.acceleratorstorefrontcommons.controllers.util.GlobalMessage like this:
class StylableGlobalMessage extends GlobalMessage {
private cssClass;
setCssClass...
getCssClass...
}
Create a new method to add this message to the model. Analogue to
de.hybris.platform.acceleratorstorefrontcommons.controllers.util.GlobalMessages#addMessage
final StylableGlobalMessage message = new StylableGlobalMessage();
message.setCode(messageKey);
message.setAttributes(attributes != null ? Arrays.asList(attributes) : Collections.emptyList());
message.setCssClass(cssClass);
List<GlobalMessage> currentMessages = (List<GlobalMessage>) model.getModelMap().get(messageHolder);
// Check if null etc.
currentMessages.add(message);
Introduce new attribute to file /mystorefront/web/webroot/WEB-INF/tags/desktop/common/globalMessages.tag like this:
<div class="alert neutral ${msg.cssClass}">
<spring:theme code="${msg.code}" arguments="${msg.attributes}"/>
</div>
Keep in mind to check the type of msg before accessing cssClass attribute. Or you replace every GlobalMessage with StylableGlobalMessage. Then you can omit the check.

Resources