How to reset a BehaviorSubject - stream

I have a BehaviorSubject that I would like to reset - by that I mean I want the latest value to not be available, just as if it was just created.
I don't seem to see an API to do this but I suppose there is another way to achieve the same result?
My desired behavior is that I need to emit events, and I'd like subscribers to get the latest event when they subscribe - if a particular manager is in a 'started' state. But when this manager is 'stopped' the latest event should not be available (just like if it was never started in the first place).

I assume you want to clear the BehaviorSubject (because otherwise don't call onComplete on it). That is not supported but you can achieve a similar effect by having a current value that is ignored by consumers:
public static final Object EMPTY = new Object();
BehaviorSubject<Object> subject = BehaviorSubject.createDefault(EMPTY);
Observable<YourType> obs = subject.filter(v -> v != EMPTY).cast(YourType.class);
obs.subscribe(System.out::println);
// send normal data
subject.onNext(1);
subject.onNext(2);
// clear the subject
subject.onNext(EMPTY);
// this should not print anything
obs.subscribe(System.out::println);

Another method of switching the value of an observable on and off is to use switchMap() to flip between the actual observable and an empty one.
Let's assume you have a manager object, and it has a observable that shows its state. Then,
subjectObservable = manager.getStateObservable()
.switchMap( state -> state == ON ? subject : Observable.never() );
will only emit values while the manager is in the ON state.

Just use setTimeout like this:
setOtpoint(value) {
this._setOption.next(value);
// Clear BehaviorSubject after emit value
setTimeout(() => {
this._setOption.next(null);
}, 100);
}

I find out a better solution for some cases and is:
subject.skiplast(1)
it can work to clean the last position on stream that is being retained because of the BehaviorSubject "behavior"

A problem with #akarnokd's answer is that the .cast prevents YourType from being an interface or a generic type such as List<String>.
Another option is to filter on a boolean field that you can switch on and off.
private BehaviorSubject<PandoraApp> subject = BehaviorSubject.create();
private boolean enabled = true;
Observable<PandoraApp> observable = subject.filter(v -> enabled);
If methods are being called on different threads you can use AtomicBoolean for the filter flag.

Here is my lib for this:
implementation "com.github.kolyall:rxjava2-empty:1.0.36"
Example:
private val myBehaviorSubject = BehaviorSubjectOptional.createOptional<MyItem?>()
errorBehaviorSubject.toObservable()
.subscribe{ item-> Log.d("onNext1", "item = $item")}
var item:MyItem? = MyItem()
myBehaviorSubject.onNextOptional(item)
//For reset:
myBehaviorSubject.clear()
//OR
item = null
myBehaviorSubject.onNextOptional(item)
errorBehaviorSubject.toObservable()
.subscribe{ item-> Log.d("onNext2", "item = $item")}

Related

How to best access data from QueryRenderer in a parent component in Relay Modern?

As you can see from the picture below, I'm rendering the popover using a react-relay QueryRenderer since the request is slow, and I do not want the rest of the page to wait for events to be fetched.
My problem is that in the navigation I have a button to show/hide the popover. That button should only be rendered when events has loaded, and the button also needs to show a count of how many events there is.
So my question is how to pass events data up from QueryRenderer (popover) to a parent component (toggle button)?
My first idea was to reuse my QueryRenderer for events and pass in dataFrom={'STORE_ONLY'}, to avoid a new HTTP request and use the cache instead, but unfortunately 'STORE_ONLY' is not an option... YET...
From looking at https://github.com/relay-tools/relay-hooks/issues/5 it seems like store-only will be supported by useQuery in the future, so is that the recommended solution to go about it, or how is the recommended way? Surely facebook, and many other applications, must have had this need frequently?
You can achieve redux-like relay store with custom handlers and local schema.
I'll be guessing what your queries, components and fields might be named like so don't forget to change it to correct values
Somewhere in project's src folder create a file ClientState.client.graphql to extend your root query type with new field for client state:
// ClientState.client.graphql
type ClientState {
showToggleButton: Boolean!
eventsCount: Int
}
extend type Query {
clientState: ClientState!
}
this will allow you to wrap Toggle button with fragment like this:
fragment ToggleButton_query on Query {
clientState {
showToggleButton
eventsCount
}
}
and spread this fragment in parent query (probably AppQuery)
Then in your second query, where you'll be fetching events, add #__clientField directive, to define custom handle for that field:
query EventModal {
events #__clientField(handle: "eventsData") {
totalCount
}
}
Create EventsDataHandler for handle eventsData:
// EventsDataHandler.js
// update method will be called every time when field with `#__clientField(handle: "eventsData")` is fetched
const EventsDataHandler = {
update (store, payload) {
const record = store.get(payload.dataID)
if (!record) {
return
}
// get "events" from record
const events = record.getLinkedRecord(payload.fieldKey)
// get events count and set client state values
const eventsCount = events.getValue('totalCount')
const clientState = store.getRoot().getLinkedRecord('clientState')
clientState.setValue(eventsCount, 'eventsCount')
clientState.setValue(true, 'showToggleButton')
// link "events" to record, so the "events" field in EventModal is not undefined
record.setLinkedRecord(events, payload.handleKey)
}
}
export default EventsDataHandler
Last thing to do is to assign custom (and default) handlers to environment and create init store values:
// environment.js
import { commitLocalUpdate, ConnectionHandler, Environment, RecordSource, Store, ViewerHandler } from 'relay-runtime'
import EventsDataHandler from './EventsDataHandler'
// ...
const handlerProvider = handle => {
switch (handle) {
case 'connection':
return ConnectionHandler
case 'viewer':
return ViewerHandler
case 'eventsData':
return EventsDataHandler
default:
throw new Error(`Handler for ${handle} not found.`)
}
}
const environment = new Environment({
network,
store,
handlerProvider
})
// set init client state values
commitLocalUpdate(environment, store => {
const FIELD_KEY = 'clientState'
const TYPENAME = 'ClientState'
const dataID = `client:${FIELD_KEY}`
const record = store.create(dataID, TYPENAME)
record.setValue(false, 'showToggleButton')
// prevent relay from removing client state
environment.retain({
dataID,
variables: {},
node: { selections: [] }
})
store.getRoot().setLinkedRecord(record, FIELD_KEY)
})

Reactor 3 create MonoEmpty from a map function

What should the return value in a map function be for the resulting mono to be MonoEmpty?
example:
Mono<Void> empty = Mono.just("ping").map(s-> ????);
or should the pattern be to do a flatMap if I need this functionality?
Mono<Void> empty = Mono.just("ping").flatMap(s-> Mono.empty());
If you need a transformation to take place most of the time, but be empty on some condition, use handle (which has the capacity to map to nothing without the overhead of flatMap):
Mono<String> emptyIfNotPing = Mono.just("ping")
.handle((t, sink) -> {
if (t.equals("ping")) sink.next("pong");
else sink.complete();
});
If you never care about the elements and just want to propagate terminal signals (onComplete and onError), you can either use ignoreElement (which maintains the generic type) or then() (which turns into a Mono<Void>):
Mono<String> source = Mono.just("foo");
Mono<Void> emptyWithTypeLoss = source.then();
Mono<String> emptyWithoutTypeLoss = source.ignoreElement();

Listing WorkItem State Reasons programmatically

We have a customised TFS workflow, I want to be able to access the Reasons I can close a Bug (change the state from Active to Closed) from TFS so that we don't have to update our code every time we want to tweak our process.
This is what I have so far:
WorkItemType wiType = this.GetWorkItemStore().Projects[this.ProjectName].WorkItemTypes["Bug"];
var reason = wiType.FieldDefinitions["Reason"];
var state = wiType.FieldDefinitions["State"];
var filterList = new FieldFilterList();
FieldFilter filter = new FieldFilter(wiType.FieldDefinitions[CoreField.State], "Active");
filterList.Add(filter);
var allowedReasons = reason.FilteredAllowedValues(filterList);
However I'm not getting any results. I'd like to get a list of all the reasons why I can close a bug (Not Reproduceable, Fixed etc)
There isn't any easy way to get the transition via API directly as I know since the API read the allowed values from database directly.
The alternative way would be export the workitemtype definition via WorkItemType.Export() method and then get the information from it. Vaccano's answer in this question provided the entire code sample you can use.
Edited to give an example of how I solved this using the above recommendation:
public static List<Transition> GetTransistions(this WorkItemType workItemType)
{
List<Transition> currentTransistions;
// See if this WorkItemType has already had it's transistions figured out.
_allTransistions.TryGetValue(workItemType, out currentTransistions);
if (currentTransistions != null)
return currentTransistions;
// Get this worktype type as xml
XmlDocument workItemTypeXml = workItemType.Export(false);
// Create a dictionary to allow us to look up the "to" state using a "from" state.
var newTransistions = new List<Transition>();
// get the transistions node.
XmlNodeList transitionsList = workItemTypeXml.GetElementsByTagName("TRANSITIONS");
// As there is only one transistions item we can just get the first
XmlNode transitions = transitionsList[0];
// Iterate all the transitions
foreach (XmlNode transition in transitions)
{
XmlElement defaultReasonNode = transition["REASONS"]["DEFAULTREASON"];
var defaultReason = defaultReasonNode.Attributes["value"].Value;
var otherReasons = new List<string>();
XmlNodeList otherReasonsNodes = transition["REASONS"].SelectNodes("REASON");
foreach (XmlNode reasonNode in otherReasonsNodes)
{
var reason = reasonNode.Attributes["value"].Value;
otherReasons.Add(reason);
}
// save off the transistion
newTransistions.Add(new Transition
{
From = transition.Attributes["from"].Value,
To = transition.Attributes["to"].Value,
DefaultReason = defaultReason,
OtherReasons = otherReasons
});
}
// Save off this transition so we don't do it again if it is needed.
_allTransistions.Add(workItemType, newTransistions);
return newTransistions;
}

Why isn't my updated observable List reflected in the template?

I've got:
my-app
community-list
On attached, my-app gets the user and loads the app.user. In the meantime, community-list is attached (even before app.user is loaded) and so I haven't been able to get the user's starred communities yet. Therefore, the solution I'm working on is as follows.
In community-list.attached():
app.changes.listen((List<ChangeRecord> records) {
if (app.user != null) {
getUserStarredCommunities();
}
});
Elsewhere in community-list is said metho:
// This is triggered by an app.changes.listen.
void getUserStarredCommunities() {
// Determine if this user has starred the community.
communities.forEach((community) {
var starredCommunityRef = new db.Firebase(firebaseLocation + '/users/' + app.user.username + '/communities/' + community['id']);
starredCommunityRef.onValue.listen((e) {
if (e.snapshot.val() == null) {
community['userStarred'] = false;
} else {
community['userStarred'] = true;
}
});
});
}
Note that communities is an observable list in community-list:
#observable List communities = toObservable([]);
Which is initially populated in community-list.attached():
getCommunities() {
var f = new db.Firebase(firebaseLocation + '/communities');
var communityRef = f.limit(20);
communityRef.onChildAdded.listen((e) {
var community = e.snapshot.val();
// If no updated date, use the created date.
if (community['updatedDate'] == null) {
community['updatedDate'] = DateTime.parse(community['createdDate']);
}
// snapshot.name is Firebase's ID, i.e. "the name of the Firebase location"
// So we'll add that to our local item list.
community['id'] = e.snapshot.name();
// Insert each new community into the list.
communities.add(community);
// Sort the list by the item's updatedDate, then reverse it.
communities.sort((m1, m2) => m1["updatedDate"].compareTo(m2["updatedDate"]));
communities = communities.reversed.toList();
});
}
In summary, I load the list of communities even before I have a user, but once I have a user I want to update each community (Map) in the list of communities with the userStarred = true/false, which I then use in my community-list template.
Alas, it doesn't seem like the List updates. How do I achieve this?
This whole app.changes.listen business is expensive. What's the proper practice in a case like this, where an element is loaded before I load objects (like app.user) that will modify it in some way.
1)
toList() creates a copy of the list. You need to apply toObservable again to get an observable list.
communities = toObservable(communities.reversed.toList());
This also assigns a new list to communities which is covered by #observable.
I think it should trigger anyway
2) You update your communities explicitly. It shouldn't be necessary to listen for changes. You can call a method containing
if (app.user != null) {
getUserStarredCommunities();
}
explicitly each time you change the list.
You also call Firebase for each community when a change in communities occurs. I don't know Firebase but it seems you send a request to a server each time which is of course expensive.
You should remember for what user+community combination you already made the call and use the remembered result instead.
With app.changes.listen you listen to any updated of any #observable field in your component. If you have other observable fields beside communities this method might be called too often.
If you are only interested in changes to communities you should put this code into a method like
communitiesChanged(oldVal, newVal) {
if (app.user != null) {
getUserStarredCommunities();
}
}
but the better option is to not listen to changes and another method name and call it explicitly as state above anyways if possible.

Monotouch - EASession output stream gives null error

Looking for some help over here.
When i run this code (below), it crashes when i process it for the second time.
it crashes with an object reference not set.. on the session.outputstream
var session= new EASession(accessory, "net.gebe");
session.OutputStream.Open();
the second time session.outputstream is null. Even when disposing session.
Richard
public void PrintIt()
{
var _accessoryList = EAAccessoryManager.SharedAccessoryManager.ConnectedAccessories;
accessory = null;
foreach(var obj in _accessoryList)
{
if(obj.ProtocolStrings.Contains("net.gebe"))
{
accessory = obj;
//break;
}
}
if(accessory == null)
{
//tmg.InfoAlert ("No Printer");
return;
}
var session= new EASession(accessory, "net.gebe");
session.OutputStream.Open();
string str2 = "HELLO THERE PRINTER 1 2 3 4 5";
byte[] printdata2;
ASCIIEncoding encoding2 = new ASCIIEncoding();
printdata2 = encoding2.GetBytes(str2);
uint nlen2 = Convert.ToUInt32 (str2.Length+1);
session.OutputStream.Write(printdata2,nlen2 );
session.OutputStream.Close ();
session.Dispose ();
}
I got mine working now. What I did:
Save the session as a variable in the class
Only create the session if session is null
You may not want to call session.OutputStream.Close() after every print. At least it's something to keep in mind while debugging for your situation.
This will allow for multiple print jobs on the same page without blowing up. session.OutputStream was not null in this case.
I also found that the ViewDidLoad/Unload events weren't great for detecting when the device becomes available/unavailable via the EAAccessoryDidConnectNotification and EAAccessory DidDisconnectNotification observers. Instead I used ViewDidAppear/ViewDidDisappear. In those methods, I tear down the session, and when I come back in to print a new job, the session gets created and OutputStream is assigned.
Lastly, I wasn't getting events fired for my device via session.OutputStream.OnEvent. Not sure if it's my device, a MonoTouch bug, or just a generic bug of mine yet.
UPDATE: Check out this nicely wrapped implementation of AccessoryAdapter
You need to list the external accessories you're going to use in your Info.plist.
There is some documentation on this on apple's site:
http://developer.apple.com/library/ios/#featuredarticles/ExternalAccessoryPT/Introduction/Introduction.html#//apple_ref/doc/uid/TP40009502
Here is a screenshot of how to set this value in Info.plist:
http://screencast.com/t/AYmOWjf8wkL
(This is from here: https://bugzilla.xamarin.com/show_bug.cgi?id=1635#c1)

Resources