Close a modal after a few seconds in Blazor - modal-dialog

So basically I am want to show a modal when the user hits a button to show them if the action was successfull or if there were problems. but then after a few seconds I want to close it so the user doesn't have to manualy close it each time. Is there a way to do this without pausing the whole program for a few seconds? or is there a better way to show a user a message instead of a modal?
right now i got these 2 methods for opening and closing the modal:
public void Open(string title, string message)
{
ModalDisplay = "block;";
ModalClass = "Show";
ShowBackdrop = true;
ModalTitle = title;
ModalBody = message;
StateHasChanged();
}
public void Close()
{
ModalDisplay = "none";
ModalClass = "";
ShowBackdrop = false;
StateHasChanged();
}

If your event/method can be async, you can skip the timer and just use Task.Delay(milliseconds), something like
public async Task Open(string title, string message)
{
ModalDisplay = "block;";
ModalClass = "Show";
ShowBackdrop = true;
ModalTitle = title;
ModalBody = message;
StateHasChanged();
await Task.Delay(2000);
ModalDisplay = "none";
ModalClass = "";
ShowBackdrop = false;
StateHasChanged();
}

So i have solved my issue and added a timer so now after i show the dialog i start a timer for 2 seconds and when that expires it executess a method that hides the modal:
private void ToggleModal(string title, string message)
{
//Showing modal
ModalDisplay = "block;";
ModalClass = "Show";
ShowBackdrop = true;
ModalTitle = title;
ModalBody = message;
StateHasChanged();
//Hiding modal
Timer timer = new Timer();
timer.Interval = 2000;
timer.Elapsed += OnTimedEvent;
timer.AutoReset = false;
timer.Enabled = true;
}
private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
{
InvokeAsync(() => {
ModalDisplay = "none";
ModalClass = "";
ShowBackdrop = false;
StateHasChanged();
});
}
there might be a better way to achieve this but this is what works for me for now!

Instead of a modal, I have just a div rendered on the page. This is the razor component:
#if (!string.IsNullOrWhiteSpace(Text))
{
<div class="alert #alertClass">#Text</div>
}
#code {
private string messageType;
private string alertClass = "alert-info";
[Parameter] public string Text { get; set; }
protected override async Task OnParametersSetAsync()
{
await Task.Delay(2000);
Text = "";
}
[Parameter]
public string MessageType
{
get { return messageType; }
set
{
messageType = value;
switch (messageType)
{
case "Error":
alertClass = "alert-danger";
break;
case "Warning":
alertClass = "alert-warming";
break;
case "Success":
alertClass = "alert-success";
break;
default:
break;
}
}
}
}
Usage:
<AlertMessage Text="Lorem ipsum dolor sit amet." MessageType="Error"/>

Related

MVVM AsyncExecute causing lag

AsyncExecute method causing lag in my treeview application when I am expanding a branch.
Important parts of my TreeView
public DirectoryItemViewModel(string fullPath, DirectoryItemType type, long size)
{
this.ExpandCommand = new AsyncCommand(Expand, CanExecute);
this.FullPath = fullPath;
this.Type = type;
this.Size = size;
this.ClearChildren();
}
public bool CanExecute()
{
return !isBusy;
}
public IAsyncCommand ExpandCommand { get; set; }
private async Task Expand()
{
isBusy = true;
if (this.Type == DirectoryItemType.File)
{
return;
}
List<Task<long>> tasks = new();
var children = DirectoryStructure.GetDirectoryContents(this.FullPath);
this.Children = new ObservableCollection<DirectoryItemViewModel>(
children.Select(content => new DirectoryItemViewModel(content.FullPath, content.Type, 0)));
//If I delete the remaining part of code in this method everything works fine,
in my idea it should output the folders without lag, and then start calculating their size in other threads, but it first lags for 1-2 sec, then output the content of the folder, and then start calculating.
foreach (var item in children)
{
if (item.Type == DirectoryItemType.Folder)
{
tasks.Add(Task.Run(() => GetDirectorySize(new DirectoryInfo(item.FullPath))));
}
}
var results = await Task.WhenAll(tasks);
for (int i = 0; i < results.Length; i++)
{
Children[i].Size = results[i];
}
isBusy = false;
}
My command Interface and class
public interface IAsyncCommand : ICommand
{
Task ExecuteAsync();
bool CanExecute();
}
public class AsyncCommand : IAsyncCommand
{
public event EventHandler CanExecuteChanged;
private bool _isExecuting;
private readonly Func<Task> _execute;
private readonly Func<bool> _canExecute;
public AsyncCommand(
Func<Task> execute,
Func<bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute()
{
return !_isExecuting && (_canExecute?.Invoke() ?? true);
}
public async Task ExecuteAsync()
{
if (CanExecute())
{
try
{
_isExecuting = true;
await _execute();
}
finally
{
_isExecuting = false;
}
}
RaiseCanExecuteChanged();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
bool ICommand.CanExecute(object parameter)
{
return CanExecute();
}
void ICommand.Execute(object parameter)
{
//I suppose that here is the problem cause IDE is hinting me that I am not awaiting here, but I don't know how to change it if it is.
ExecuteAsync();
}
}

microsoft bot framework typing indicator form flow(Form Builder)

I need to add typing indicator activity inside the form flow, I have used the following code but it only works out side of form flow, once the user enter the form builder the typing indicator does not appear.
Activity replytyping1 = activity.CreateReply();
replytyping1.Type = ActivityTypes.Typing;
replytyping1.Text = null;
ConnectorClient connector2 = new ConnectorClient(new Uri(activity.ServiceUrl));
await connector2.Conversations.ReplyToActivityAsync(replytyping1);
I am using the following code inside dialog to call the form builder:
var myform = new FormDialog<TrainingForm>(new TrainingForm(), TrainingForm.MYBuildForm, FormOptions.PromptInStart, null);
context.Call<TrainingForm>(myform, AfterChildDialog);
my form builder code:
public enum MoreHelp { Yes, No };
public enum Helpfull { Yes, No };
[Serializable]
public class TrainingForm
{
public string More = string.Empty;
public string usefull = string.Empty;
[Prompt("Is there anything else I can help you with today? {||}")]
[Template(TemplateUsage.NotUnderstood, "What does \"{0}\" mean?", ChoiceStyle = ChoiceStyleOptions.Auto)]
public MoreHelp? needMoreHelp { get; set; }
[Prompt("Was this helpful? {||}")]
[Template(TemplateUsage.NotUnderstood, "What does \"{0}\" mean?", ChoiceStyle = ChoiceStyleOptions.Auto)]
public Helpfull? WasHelpful { get; set; }
public static IForm<TrainingForm> MYBuildForm()
{
return new FormBuilder<TrainingForm>()
.Field(new FieldReflector<TrainingForm>(nameof(needMoreHelp))
.SetActive(state => true)
.SetNext(SetNext2).SetIsNullable(false))
.Field(new FieldReflector<TrainingForm>(nameof(WasHelpful))
.SetActive(state => state.More.Contains("No"))
.SetNext(SetNext).SetIsNullable(false)).OnCompletion(async (context, state) =>
{
if (state.usefull == "No")
{
await context.PostAsync("Sorry I could not help you");
}
else if (state.usefull == "Yes")
{
await context.PostAsync("Glad I could help");
}
if(state.More == "Yes")
{
await context.PostAsync("Ok! How can I help?");
}
context.Done<object>(new object());
})
.Build();
}
If you are attempting to send the typing activity from the dialog that loaded the FormFlow dialog, it will not work because the code in the parent dialog does not execute every time the FormFlow dialog is loaded.
However, you can modify the MessagesController and inspect the dialog stack. If the FormFlow dialog is the last dialog on the stack, then send typing:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity) {
if (activity.Type == ActivityTypes.Message)
{
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(default(CancellationToken));
var stack = scope.Resolve<IDialogTask>();
if (stack.Frames != null && stack.Frames.Count > 0)
{
var lastFrame = stack.Frames[stack.Frames.Count - 1];
var frameValue = lastFrame.Target.GetType().GetFields()[0].GetValue(lastFrame.Target);
if(frameValue is FormDialog<TrainingForm>)
{
var typingReply = activity.CreateReply();
typingReply.Type = ActivityTypes.Typing;
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
await connector.Conversations.ReplyToActivityAsync(typingReply);
}
}
}
await Conversation.SendAsync(activity, () => FormDialog.FromForm(TrainingForm.MYBuildForm));
}
else
{
this.HandleSystemMessage(activity);
}
return Request.CreateResponse(HttpStatusCode.OK);
}

codename one FB authentication

I have been using the following code
String clientId = "1171134366245722";
String redirectURI = "http://www.codenameone.com/";
String clientSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXX";
Login fb = FacebookConnect.getInstance();
fb.setClientId(clientId);
fb.setRedirectURI(redirectURI);
fb.setClientSecret(clientSecret);
//Sets a LoginCallback listener
fb.setCallback(...);
//trigger the login if not already logged in
if(!fb.isUserLoggedIn()){
fb.doLogin();
} else {
//get the token and now you can query the facebook
String token = fb.getAccessToken().getToken();
...
}
After login into facebook account, it directly takes me to the sendRedirectURI(XXX) as specified in code and the callback function is not working. I need to run setcallback(), how do I achieve that?
You have a couple of things to do for Facebook login to work.
You need to define what kind of data you will like to fetch. The best way is to create a UserData interface and implement it in your class:
public interface UserData {
public String getId();
public String getEmail();
public String getFirstName();
public String getLastName();
public String getImage();
public void fetchData(String token, Runnable callback);
}
Then implement it like this:
class FacebookData implements UserData {
String id;
String email;
String first_name;
String last_name;
String image;
#Override
public String getId() {
return id;
}
#Override
public String getEmail() {
return email;
}
#Override
public String getFirstName() {
return first_name;
}
#Override
public String getLastName() {
return last_name;
}
#Override
public String getImage() {
return image;
}
#Override
public void fetchData(String token, Runnable callback) {
ConnectionRequest req = new ConnectionRequest() {
#Override
protected void readResponse(InputStream input) throws IOException {
try {
JSONParser parser = new JSONParser();
Map<String, Object> parsed = parser.parseJSON(new InputStreamReader(input, "UTF-8"));
id = (String) parsed.get("id");
email = (String) parsed.get("email");
first_name = (String) parsed.get("first_name");
last_name = (String) parsed.get("last_name");
image = (String) ((Map) ((Map) parsed.get("picture")).get("data")).get("url").toString();
} catch (Exception ex) {
}
}
#Override
protected void postResponse() {
callback.run();
}
#Override
protected void handleErrorResponseCode(int code, String message) {
if (code >= 400 && code <= 410) {
doLogin(FacebookConnect.getInstance(), FacebookData.this, true);
return;
}
super.handleErrorResponseCode(code, message);
}
};
req.setPost(false);
req.setUrl("https://graph.facebook.com/v2.10/me");
req.addArgumentNoEncoding("access_token", token);
req.addArgumentNoEncoding("fields", "id,email,first_name,last_name,picture.width(512).height(512)");
NetworkManager.getInstance().addToQueue(req);
}
}
Let's create a doLogin() method that includes the setCallback()
void doLogin(Login lg, UserData data, boolean forceLogin) {
if (!forceLogin) {
if (lg.isUserLoggedIn()) {
//process Facebook login with "data" here
return;
}
String token = Preferences.get("token", (String) null);
if (getToolbar() != null && token != null) {
long tokenExpires = Preferences.get("tokenExpires", (long) -1);
if (tokenExpires < 0 || tokenExpires > System.currentTimeMillis()) {
data.fetchData(token, () -> {
//process Facebook login with "data" here
});
return;
}
}
}
lg.setCallback(new LoginCallback() {
#Override
public void loginFailed(String errorMessage) {
Dialog.show("Error Logging In", "There was an error logging in with Facebook: " + errorMessage, "Ok", null);
}
#Override
public void loginSuccessful() {
data.fetchData(lg.getAccessToken().getToken(), () -> {
Preferences.set("token", lg.getAccessToken().getToken());
Preferences.set("tokenExpires", tokenExpirationInMillis(lg.getAccessToken()));
//process Facebook login with "data" here
});
}
});
lg.doLogin();
}
long tokenExpirationInMillis(AccessToken token) {
String expires = token.getExpires();
if (expires != null && expires.length() > 0) {
try {
long l = (long) (Float.parseFloat(expires) * 1000);
return System.currentTimeMillis() + l;
} catch (NumberFormatException ex) {
}
}
return -1;
}
Finally, call doLogin() after fb.setClientSecret()
String clientId = "1171134366245722";
String redirectURI = "http://www.codenameone.com/";
String clientSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXX";
Login fb = FacebookConnect.getInstance();
fb.setClientId(clientId);
fb.setRedirectURI(redirectURI);
fb.setClientSecret(clientSecret);
doLogin(fb, new FacebookData(), false);

Refresh suggestion list on change - cn1 autocomplete

I've implemented a custom autocomplete text field in a cn1 app, but I've noticed it only loads the suggestions list once, after that any change in the text doesn't trigger a change in the list, and the getSuggestionModel() is never called again. How can I achieve this (in my mind, basic) functionality?
This is my autocomplete class:
public class ForumNamesAutocomplete extends AutoCompleteTextField {
List<String>suggestions = new LinkedList<String>();
List<Map<String,Object>> fData;
StateMachine mac;
int currentIndex;
String prevText;
public static final String KEY_FORUM_NAME = "name";
public static final String KEY_FORUM_ID = "id";
public static final String KEY_FORUM_DESC = "desc";
public ForumNamesAutocomplete(StateMachine sm){
super();
mac = sm;
if(sm.forumData != null){
fData = mac.forumData;
}
}
#Override
protected boolean filter(String text) {
if(text.equals(prevText)){
return false;
}
setSuggestionList(text);
fireDataChanged(DataChangedListener.CHANGED, text.length());
prevText = text;
return true;
}
#Override
public void fireDataChanged(int type, int index) {
super.fireDataChanged(type, index);
}
public void setSuggestionList(String s){
if(suggestions == null){
suggestions = new LinkedList<String>();
}else{
suggestions.clear();
}
LinkedList<String> descList = new LinkedList<String>();
for(int i = 0;i<fData.size();i++){
boolean used = false;
Map<String,Object> forumMap = fData.get(i);
if(((String)forumMap.get(KEY_FORUM_NAME)).indexOf(s) != -1){
suggestions.add((String)forumMap.get(KEY_FORUM_NAME));
used = true;
}
if(!used && ((String)forumMap.get(KEY_FORUM_DESC)).indexOf(s) != -1){
descList.add((String)forumMap.get(KEY_FORUM_NAME));
}
}
suggestions.addAll(descList);
}
#Override
protected ListModel<String> getSuggestionModel() {
return new DefaultListModel<String>(suggestions);
}
}
This used to be simpler and seems to be a bit problematic now as explained in this issues.
Technically what you need to do is return one model and then mutate said model/fire modified events so everything will refresh. This is non-trivial and might not work correctly for all use cases so ideally we should have a simpler API to do this as we move forward.
After additional debugging, I saw that the getSuggestionModel() method was being called only during initialization, and whatever the suggestion list (in suggestion object) was at that point, it remained so. Instead I needed to manipulate the underlying ListModel object:
public class ForumNamesAutocomplete extends AutoCompleteTextField {
ListModel<String>myModel = new ListModel<String>();
...
#Override
protected boolean filter(String text) {
if(text.length() > 1){
return false;
}
setSuggestionList(text);
return true;
}
private void setSuggestionList(String s){
if(myModel == null){
myModel = new ListModel<String>();
}else{
while(myModel.getSize() > 0)
myModel.removeItem(0);
}
for(int i = 0;i<fData.size();i++){
boolean used = false;
Map<String,Object> forumMap = fData.get(i);
if(((String)forumMap.get(KEY_FORUM_NAME)).indexOf(s) != -1){
myModel.addItem((String)forumMap.get(KEY_FORUM_NAME));
used = true;
}
if(!used && ((String)forumMap.get(KEY_FORUM_DESC)).indexOf(s) != -1){
myModel.addItem((String)forumMap.get(KEY_FORUM_NAME));
}
}
}
...
}

Dynamic styling with IceFaces: how can I change the color of the outputText at the time of rendering?

I'm new to ICEFaces and I have to maintain someone else's code. I'm working on a web chat where the user can send and receive messages. I would like the messages to have different colors depending on whether they were sent by the user or by someone else.
I currently have the following code in my xhtml file:
<h:dataTable id="history" value="#{chatBean.messages}" var="message" border="0" align="left" style="width: 100%" >
<h:column width="590" height="25" align="left" valign="bottom" >
<h:outputText value="#{message}" styleClass="#{chatBean.messageColor}" />
</h:column>
</h:dataTable>
This shows all messages sent and received, but all with the same color, even though the messageColor property of the chat bean changes: I did an experiment and appended the result of getMessageColor() at the end of each message and it does change, but the text is still rendered in the same color.
The CSS has the following classes (among others):
.class1
{
color:#818181;
width: 100%;
font-size: 15px;
font-family: Arial, Helvetica, sans-serif;
font-weight: bold;
}
.class2
{
color:#00657c;
width: 100%;
font-size: 15px;
font-family: Arial, Helvetica, sans-serif;
font-weight: bold;
}
Here's the code for the ChatBean class:
#ManagedBean(name = "chatBean")
#SessionScoped
public class ChatBean implements Serializable {
private static final long serialVersionUID = -12636320254821872L;
private static final String PUSH_GROUP = "chatPage";
private PortableRenderer renderer;
private String message;
private String lastMessageSent = "";
private Date lastMessageTime = new Date();
private String isDown = "false";
private List<String> messages = new ArrayList<String>();
private String buttonDisabled = "true";
private String buttonCancelDisabled = "true";
private String pollDisabled = "false";
private String id = "";
private ChatClient chat;
private Timer timer = new Timer();
private String messageColor;
public class ChatThread extends Thread implements Serializable {
private static final long serialVersionUID = -7636532554421738019L;
private Map<String, String> data;
private String mail;
public ChatThread(final Map<String, String> data, final String mail) {
this.data = data;
this.mail = mail;
}
#Override
public void run() {
chat = new ChatClient(new ChatClient.Event() {
#Override
public void handle(String msg) {
if(msg != null && msg.length() > 0)
pushMessage(msg);
}
#Override
public void agentConnected(String msg) {
buttonDisabled = "false";
buttonCancelDisabled = "false";
pushMessage(msg);
}
#Override
public void agentDisconnected(String msg) {
buttonDisabled = "true";
pushMessage(msg);
try {
timer.cancel();
} catch (Exception e) {
e.printStackTrace();
}
}
});
chat.login(mail, data);
chat.join(mail, data.get("partner"), data.get("business"));
timer.scheduleAtFixedRate(new RefreshTimerTask(), 0, 1000);
}
}
public class RefreshTimerTask extends TimerTask implements Serializable {
private static final long serialVersionUID = 1852678537009150141L;
public void run() {
chat.refresh();
}
}
public ChatBean() {
if(getSession() != null) {
id = getSession().getId();
PushRenderer.addCurrentSession(PUSH_GROUP + id);
renderer = PushRenderer.getPortableRenderer();
setMessageColor("class1");
Log.getLogger().debug("New chat bean.");
if(getData().containsKey("login_chat")) {
ChatThread chat = new ChatThread(getData(), getSessionAttribute(GenesysSingleton.getInstance().getConfigApp().getDisplayName(), "<mail>"));
chat.start();
}
}
}
private void pushMessage(String msg) {
if(msg != null && !msg.isEmpty()) {
ChatBean.this.isDown = "true";
messages.add(msg);//Acá se puede acceder a textColor.
try {
PushRenderer.render(PUSH_GROUP + id);
} catch (Exception e) {
renderer.render(PUSH_GROUP + id);
}
setMessageColor("class1");
}
}
private String getSessionAttribute(String key, String ref) {
Object value = getSession().getAttribute(key);
return value != null ? value.toString() : ref;
}
private Map<String, String> getData() {
Map<String, String> data = new HashMap<String, String>();
HttpSession session = getSession();
#SuppressWarnings("rawtypes")
Enumeration enums = session.getAttributeNames();
while(enums.hasMoreElements()) {
String key = enums.nextElement().toString();
if(!"com.sun.faces.application.view.activeViewMaps".equals(key)
&& !"com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap".equals(key)
&& !"javax.faces.request.charset".equals(key))
data.put(key, session.getAttribute(key).toString());
}
return data;
}
public void sendMessage(ActionEvent event) {
sendMessage();
}
protected synchronized void sendMessage() {
if (message != null && !message.trim().isEmpty()){
Date now = new Date();
//No permito mandar el mismo mensaje 2 veces seguidas en un intervalo menor a un segundo.
message = message.trim();
if (message.equals(lastMessageSent)&&(now.getTime()<(1000+lastMessageTime.getTime()))){
message = null;
}
else{
setMessageColor("class2");
lastMessageSent = message;
message = null;
lastMessageTime = new Date();
chat.refresh(lastMessageSent);
}
}
}
public String disconnect() {
pollDisabled = "true";
return "login";
}
public void sendClose(ActionEvent event) {
}
public void receiveMessage() {
}
#PreDestroy
public void destroy() {
buttonDisabled = "true";
try {
//pushMessage(SISTEMA_3);
} catch (Exception e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
};
System.out.println(id + "- ssssssss");
try {
timer.cancel();
} catch (Exception e) {
}
chat.logout();
chat.close();
}
private HttpSession getSession() {
return (HttpSession) getContext().getSession(false);
}
private ExternalContext getContext() {
return getFacesContext().getExternalContext();
}
private FacesContext getFacesContext() {
return FacesContext.getCurrentInstance();
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getButtonDisabled() {
return buttonDisabled;
}
public void setButtonDisabled(String buttonDisabled) {
this.buttonDisabled = buttonDisabled;
}
public List<String> getMessages() {
try {
JavascriptContext.addJavascriptCall(getFacesContext(), "document.getElementById('scrollDataTable').scrollIntoView();");
} catch (Exception e) {
e.printStackTrace();
}
return messages;
}
public void setMessages(List<String> messages) {
this.messages = messages;
}
public String getPollDisabled() {
return pollDisabled;
}
public void setPollDisabled(String pollDisabled) {
this.pollDisabled = pollDisabled;
}
public String getButtonCancelDisabled() {
return buttonCancelDisabled;
}
public void setButtonCancelDisabled(String buttonCancelDisabled) {
this.buttonCancelDisabled = buttonCancelDisabled;
}
public String getIsDown() {
return isDown;
}
public void setIsDown(String isDown) {
this.isDown = isDown;
}
public String getMessageColor() {
return messageColor;
}
public void setMessageColor(String textColor) {
this.messageColor = textColor;
}
}
All help will be appreciated. Thank you in advance.
One possible way which I have changed css dynamically depending on a bean property is through using the styleClass attribute of the <h:outputText>.
In your css file define two varying classes such as
.class1
{
color:red; //Put your colour here (#818181)
}
.class2
{
color:green; //Put your colour here (#00657c)
}
Then in your java bean code you could declare a String field with getters and setters such as
private String messageColor;
Then in your code where you do
setTextColor(COLOR_AGENTE);
You can change this to the class which you would like to change the text to such as:
setMessageColor("class1");
Then on your <h:outputText> attach
styleClass="#{chatBean.messageColor}"
This should hopefully work;
Due to my original suggestion not working, you could try this.
Remove private String messageColor; from you chatBean and the getters/setters along with any calls to setMessageColor("class1");.
But keep the two classes in your css.
Now declare a boolean property with getters and setters in your chatBean:
private boolean colourAgente;
Declare a method:
public String setColor() {
if (colourAgente) {
return "class1";
} else {
return "class2";
}
}
Then in your xhtml change the styleClass attribute to:
styleClass="#{chatBean.setColor()}"
Finally, in your java code change:
setMessageColor("class1");
to either colourAgente = true; or colourAgente=false; depending on what colour you want to set.
I finally did it, but I had to use an ugly JavaScript workaround. That is, I'm now running this script every time the chat is refreshed:
function updateColors(){
var username = document.getElementById("form:username").value;
if (username.length > 0){
var x = document.getElementsByClassName("class1");
if (x != null){
for (i = 0; i < x.length; i++){
if (x[i].innerHTML.indexOf(username) === 0){
x[i].className = "class2";
}
}
}
}
}
Anyway, thanks for your help, LiamWilson94. I still don't know what part of the code I'm working with makes it so that your answers don't work, but you have given me a lot of insight which helped me arrive to this "solution", and I have learnt a few things about IceFaces in the process.
OK, I have found a better solution.
I created a TextModel class:
import java.io.Serializable;
public class TextModel implements Serializable {
private static final long serialVersionUID = -8470475291191399871L;
private String text;
private String clase;
public TextModel() {
}
public TextModel(String text, String clase) {
this.text = text;
this.clase = clase;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getClase() {
return clase;
}
public void setClase(String clase) {
this.clase = clase;
}
public String toString() {
return text;
}
}
Then I changed messages in ChatBean from List to List, and changed the following functions in ChatBean.java:
private void pushMessage(String msg) {
if(msg != null && !msg.isEmpty()) {
ChatBean.this.isDown = "true";
messages.add(new TextModel(msg,clase));
try {
PushRenderer.render(PUSH_GROUP + id);
} catch (Exception e) {
renderer.render(PUSH_GROUP + id);
}
clase = "class1";
}
}
protected synchronized void sendMessage() {
if (message != null && !message.trim().isEmpty()){
Date now = new Date();
message = message.trim();
if (message.equals(lastMessageSent)&&(now.getTime()<(1000+lastMessageTime.getTime()))){
message = null;
}
else{
clase = "class2";
lastMessageSent = message;
message = null;
lastMessageTime = new Date();
chat.refresh(lastMessageSent);
}
}
}
Where clase is either "class1" or "class2" (could be neater, but it works for now, I can always make it neater later).
Finally, on chat.xhtml, I changed the outputtext tag to:
<h:outputText value="#{message.text}" styleClass="#{message.clase}" />
That's it. No more messy JavaScript patches.
The trick was making the class a property of the message itself rather than the ChatBean.
I hope this helps someone else in the future.