I have a label which displays error messages. If you double click on it you get a big dialog showing the whole stack trace. I have two observables: One for the errors and one for the click events:
final ConnectableObservable<Notification> errorNotifications = pm
.getNotificationObservable()
.filter(notification -> notification.getType().isError() && !notification.getLongMessage().isEmpty())
.replay(1);
errorNotifications.connect();
SwingObservable.fromMouseEvents(dialog.getMessagePanel().getMessageLabel())
.map(MouseEvent::getClickCount)
.filter(number -> number >= 2)
.subscribe(integer -> errorNotifications
.take(1)
.subscribe(notification -> ErrorDialog.showError(dialog.getFrame(), "Error", notification.getLongMessage())));
I filter the notification observable to only show erros and replay the last error if I subscribe from it from inside my click observable.
Now my question is, are there any operators in RxJava by which I can do this more... neatly? I tried to use combineLatest() but this had the effect, that every time an error ocured the dialog would open.
In a more abstract way: I have two observables, one is like the "master": If the master observable (click observable) emits an item, the other observable (my error notifications) should emit the latest item.
Using another Observable in a subscription is often a design flaw.
You may check the flatMap operator in this response. It will help you to emits error notification when you emit another event.
For example, if you want to use flatMap operator with your code, it can be updated like this :
final ConnectableObservable<Notification> errorNotifications =
pm.getNotificationObservable()
.filter(notification -> notification.getType().isError() && !notification.getLongMessage().isEmpty())
.replay(1);
errorNotifications.connect();
SwingObservable.fromMouseEvents(dialog.getMessagePanel().getMessageLabel())
.map(MouseEvent::getClickCount)
.filter(number -> number >= 2)
.flatMap(integer -> errorNotifications.take(1))
.subscribe(notification -> ErrorDialog.showError(dialog.getFrame(), "Error", notification.getLongMessage())));
Related
When I try to read the reactions added to a message sent I'm always getting an empty list.
MessageBuilder mb = new MessageBuilder();
channel.sendMessage(mb.build()).queue((t);
After adding reactions to it, I execute this code:
System.out.println(t.getReactions().size());
for (MessageReaction r : t.getReactions()) {
System.out.println(r.getReactionEmote().getName());
}
(this is yet to be implemented, I'm just trying to get to understand how I can use it)
I am expecting the output to be the amount of reactions I added, yet System.out.println(t.getReactions().size()); will always print 0 regardless of the amount of reactions added to the message sent.
Furthermore, when iterating of the list containing reactions, it always won't print anything to the console, since the list seems to be empty.
Is there something I need to add that I can use .getReactions()?
What I also tried is using an eventWaiter:
eventWaiter.waitForEvent(GuildMessageReactionAddEvent.class, (event) -> {
return "🎵".equals(event.getReactionEmote().getName()) && !event.getUser().isBot()
&& event.getMessageIdLong() == messageId;
}, (event) -> {
System.out.println("Reacting to reaction");
}, (long) 30, TimeUnit.SECONDS, () -> {
System.out.println("Timeout. No event was registered.");
});
This always outputs the timeout warning, regardless of reactions added.
This is not how JDA works.
The reactions do not get magically updated, you can get the reactions by fetching the message or by listening for the reaction add event.
To use the event waiter properly you must make sure that the same instance is also registered on JDA by using JDABuilder#addEventListeners.
So either listen for the event manually or make sure that you only use once instance of the EventWaiter class in your code that is also registered on the JDA builder.
I'm building a log reader with JavaFX as a side project and have gotten to a dead end when trying to implement filtering on the TableView.
What I have is a few CheckBoxes (LHS of the picture) that will basically act as the filters for what the TableView will display:
Once the Submit button is clicked, a background thread is opened to read the and parse the files. Once the operation terminates, the result of each log read is inserted into the global ObservableList<Log>:
public class Test_Filters extends Application {...
private ObservableList<LogTest> logs = FXCollections.observableArrayList();
...}
What I'm having trouble with is how to deal with:
A situation where more than one filter CheckBox is checked.
A situation where the CheckBox is unchecked.
For 1., I was wondering what's the best way to deal with this. Let's say I have x filters selected. This would mean that I have to basically filter out x values from the ObservaleList:
logTable.setItems(logTable.getItems().filtered(log -> !log.getSource().equals(checkBox.getText())));
You can use JavaFX's FilteredList, which accepts a Predicate. You can update the predicate on each filter, combining them as you want.
FilteredList<LogTest> items = new FilteredList<>(originalItems);
tableView.setItems(items);
... on update of filter UI items
Predicate<LogTest> containsFoo = i -> i.getName().contains("foo");
Predicate<LogTest> isSevere = i -> i.getLevel() == Level.SEVERE;
Predicate<LogTest> filter = containsFoo.or(isSevere);
items.setPredicate(filter);
If you want to show all records again simply set the predicate to null:
items.setPredicate(null);
Using the FilteredList you don't need to re-read the log records, as the filter is applied immediately on the existing items.
im trying to have a pattern, where my observable which produces some object, is transformed into domain events like Started, Success, Error emited around the observable producing, if that makes sense
public Observable<BookRenderingEvent> extractAndRenderObservable(String epubPath) {
return extractObservable(epubPath)
.flatMapObservable(extractedEpub -> renderObservable(extractedEpub)
.<BookRenderingEvent>map(renderedEpub -> new BookRenderingEvent.Success(renderedEpub))
.onErrorReturn(t -> new BookRenderingEvent.Error())
.startWith(new BookRenderingEvent.Started()));
}
private Observable<RenderedEpub> renderObservable(ExtractedEpub extractedEpub) {
return Observable.combineLatest(readerConfigObservable(), pagerDimensionsObservable(), ..)
.switchMapSingle(foo -> doRenderObservable()) <--- heavy work
.map(bar -> new RenderedEpub(bar))
}
renderObservable contains a heavy action so I want to emit these state events, so UI can react accordingly (with success containing the extractedEpub object as you can see in the map)
What my problem is that, renderObservable contains combineLatest(), so it "stays open" and emit mutiple times in time, whenever its obervables emit.
So the flow of events is Started, Success, Succes ... Success.
I want it to be Started, Success, Started, Success .. etc. i.e prepend Started event whever combineLatest emits, but my rx knowledge is insufficient.
Thanks
You could insert the following into the observable chain at the right place:
.flatMap( event -> Observable.just( new BookRenderingEvent.Started(), event )
This will emit the Started event before every event that it receives.
Of course, you could add in some logic so that you don't issue Started if the event is Started, etc.
Ok Ive managed to figure it out. The key info I was missing is that right side of flatmap gets subscribed when left side emits. Therefore the startWith had to be moved to the right side of flatmap observable, that gets subscribed to when ever combineLatest emits
public Observable<BookRenderingEvent> extractAndRenderObservable(String epubPath) {
return extractObservable(epubPath)
.flatMap(extractedEpub -> Observable.combineLatest(readerConfigObservable(), pagerDimensionsObservable(), ..)
.switchMap(foo -> renderObservable(extractedEpub)
.<BookRenderingEvent>map(renderedEpub -> new BookRenderingEvent.Success(renderedEpub))
.onErrorReturn(t -> new BookRenderingEvent.Error())
.startWith(new BookRenderingEvent.Started()));
}
I have Rx Stream where I'm using a grouped observable to have multiple observable according to a discriminant value. The thing is that I want to keep in memory the last subscription of each group observable in order to delete the stream when a new one is created (unsubscribe)
I'm using a simple Hashmap to store value according to a key which is an observable it self but it doesn't to worth it.
Here is where I am so far:
stream
.groupBy(ts -> ts.getLogin())
.map(receptor -> receptor.asObservable())
.forEach(consumer -> {
controlConsumer(consumer);
});
public void controlConsumer(Observable<Number> pConsumer) {
Subscription mySubscription = pConsumer
.buffer(2,1)
.subscribe();
// Remove the current consumer last subscription if existing
if(consumersLastSubcriptions.containsKey(pConsumer)) {
LOG.info("removing last subscription");
Subscription lLastSubscription = consumersLastSubcriptions.get(pConsumer);
lLastSubscription.unsubscribe();
}
// store the new consumer subscription
consumersLastSubcriptions.put(pConsumer, lSubscription);
}
UPDATE
Actually i might have different items defined by the login of the user emitting them. What i want is to store the last subscription of each user in order to remove it when i need to recreate a new one when needed. My purpose is to shutdown every stream no more used and don't let them open for nothing.
The "neverending" Observable emits a parameter item as soon as the user changes something on the ui.
depending on this item I need to do a request. as soon as the parameter item changes the request should be stopped and a new one started.
parameterObservable
.switchMap(this::search) // to stop an restart the request with new params
.toList()
.subscribe(resultList -> {/* do something*/});
The Problem here is that, the toList Operator waits for the parameterObservable to complete. Which will not happen.
To make the toList work I could do something like this:
parameterObservable
.subscribe(params -> search(params).toList()
.subscribe(/* do something */)
);
But then the switchMap is missing.
How can I achieve this?
How about this:
parameterObservable
.switchMap(params -> search(params).toList())
.subscribe(resultList -> {/* do something*/});