How to conditionally PopAsync based on what page is currently show to user - c#

Within the same page of my app the user can send off two websocket requests. That I then get two websocket responses. In each of those responses I pop the page from the nav stack. This is in their profile page where they can edit some basic info or change their password. Hopefully they usually would only do one but in the chance they do both and click save then it pops the page twice.
I have been trying to check the current page and if they are still on the profile page to go ahead and pop it but if they are not then skip the popping.
It seems like popasync is not actually removing the page from the stack because I will call the nav stack to a list afterwards to check and it's still there. I tried doing a check if any pages in the stack were the profile pages to then pop it. That still could be sloppy incase there were multiple instances of it in the stack but it would atleast get me in the right direction.
I also tried using MessagingCenter to bring it back to the code behind but that didn't do the trick either.
The main problem here is I only want to pop the page if that is the page the user is currently looking at.
If anyone could point me in the right direction that would be much appreciated.
else if (root.payload?.data?.updateUserFields != null)
{
DabGraphQlUpdateUserFields fields = root.payload.data.updateUserFields;
dbSettings.StoreSetting("Email", fields.email);
dbSettings.StoreSetting("FirstName", fields.firstName);
dbSettings.StoreSetting("LastName", fields.lastName);
GlobalResources.WaitStop();
var UserName = GlobalResources.GetUserName().Split(' ');
GuestStatus.Current.UserName = GlobalResources.GetUserName();
Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.DisplayAlert("Success", "User profile information has been updated", "OK"); ; });
DabProfileManagementPage profilePage = new DabProfileManagementPage();
Device.BeginInvokeOnMainThread(() =>
{
if (Application.Current.MainPage.Navigation.NavigationStack.Any(p => p is DabProfileManagementPage))
{
var existingPagess = Application.Current.MainPage.Navigation.NavigationStack.ToList();
Application.Current.MainPage.Navigation.PopAsync();
var _lastPage = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault();
Application.Current.MainPage.Navigation.RemovePage(_lastPage);
var existingPages = Application.Current.MainPage.Navigation.NavigationStack.ToList();
}
});
//Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.Navigation.PopAsync(); });
}
else if (root.payload?.data?.updatePassword != null)
{
GlobalResources.WaitStop();
if (root.payload.data.updatePassword == true)
{
Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.DisplayAlert("Success", "Your password has been updated", "OK"); ; });
Device.BeginInvokeOnMainThread(() =>
{
if (Application.Current.MainPage.Navigation.NavigationStack.Any(p => p is DabProfileManagementPage))
{
var existingPagess = Application.Current.MainPage.Navigation.NavigationStack.ToList();
Application.Current.MainPage.Navigation.PopAsync();
var _lastPage = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault();
Application.Current.MainPage.Navigation.RemovePage(_lastPage);
var existingPages = Application.Current.MainPage.Navigation.NavigationStack.ToList();
}
});
//Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.Navigation.PopAsync(); });
}
}

As Jason pointed out I just added variable of type int and increased it after a pop happened and reset it once it tries to go past 2. I also reset it to 0 once the user clicks to open the page again. The only issue, which shouldn't happen often is if the user tries to go and change info for a third time without leaving the page. No pop will happen. I thought about adding a timer but I have had apps get messy with timers before so I opted to keep it as is for now. Hoping I do find a way to pop once every time no matter how many times the user decides to hit that function.
else if (root.payload?.data?.updateUserFields != null)
{
DabGraphQlUpdateUserFields fields = root.payload.data.updateUserFields;
dbSettings.StoreSetting("Email", fields.email);
dbSettings.StoreSetting("FirstName", fields.firstName);
dbSettings.StoreSetting("LastName", fields.lastName);
GlobalResources.WaitStop();
var UserName = GlobalResources.GetUserName().Split(' ');
GuestStatus.Current.UserName = GlobalResources.GetUserName();
Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.DisplayAlert("Success", "User profile information has been updated", "OK"); ; });
if (popRequests < 1)
{
popRequests = popRequests + 1;
Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.Navigation.PopAsync(); });
}
else
popRequests = 0;
}
else if (root.payload?.data?.updatePassword != null)
{
GlobalResources.WaitStop();
if (root.payload.data.updatePassword == true)
{
Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.DisplayAlert("Success", "Your password has been updated", "OK"); ; });
if (popRequests < 2)
{
popRequests = popRequests + 1;
Device.BeginInvokeOnMainThread(() => { Application.Current.MainPage.Navigation.PopAsync(); });
}
else
popRequests = 1;
}
}

Related

Xamarin forms what is the best of search query with API

I am learning xamarin forms.
I using Azure search API to query news but sometimes the result of search is not good because of my search code. I can tap for instance "cat" but because of the delay "ca" is sent to the API.
What I woud like please :
I would like to launch automatically my search function each time the user is not tapping on search bar.
For instance lets say a user is willing to tap "tennis is a sport":
So he taps quikly on search bar and make a pause on "tennis is" => launch function on "tennis is"
Then he continues and finishs the sentence : "tennis is a sport" => launch function on "tennis is a sport"
If there is a better way of doing it, I will take.
Here is my code :
Task tasksearch = null;
int timeDelaySearch = 1000;
void OnSearcMyNews(System.Object sender, Xamarin.Forms.TextChangedEventArgs e)
{
try {
if (tasksearch == null || tasksearch.IsCompleted)
{
tasksearch = Task.Run(async () =>
{
await Task.Delay(timeDelaySearch);
Device.BeginInvokeOnMainThread( () =>
{
searchTerm = searchTerm.ToLower();
_ = SearchNewsDataAsync(searchTerm, UserConnected.NewsLanguage);
tasksearch = null;
});
});
}
}
catch (Exception ex)
{
}
}

Hide "No Preference" button only in the change prompt

I would like to remove the "No Preference" only in the "Change prompt" in the formflow or at least change it text only for the confirmation prompt leaving the of the form with "No Preference" option.
I was able to change its text, but it changed the whole form and didn't work for me.
public static IForm<PromoBot> BuildForm()
var form = new FormBuilder<PromoBot>()
.Message("Hi......!")
.Field(nameof(Nome), validate: async (state, value) =>
{
..........
result.IsValid = true;
return result;
}, active: state => true)
.Message("xxxxxx")
.Field(nameof(CEP)
.Field(nameof(Estados))
.Confirm(async (state) =>
{
return new PromptAttribute("You have selected the following: \n {*} "Is this correct?{||}");
})
.Message("Excelente! Agora vou precisar de alguns segundos para encontrar o melhor plano para sua empresa… já volto!")
.OnCompletion(OnComplete);
var noPreferenceStrings = new string[] { "New Text" };
form.Configuration.Templates.Single(t => t.Usage == TemplateUsage.NoPreference).Patterns = noPreferenceStrings;
form.Configuration.NoPreference = noPreferenceStrings;
return form.Build();
}
Ordinarily you can use a template attribute to modify FormFlow's behavior, but the navigation step is sort of finicky. I think the best thing to do in this situation is provide your form with a custom prompter.
In your case, the code could look something like this:
public static IForm<MyClass> BuildForm()
{
var formBuilder = new FormBuilder<MyClass>()
.Field(nameof(FirstName))
.Field(nameof(LastName))
.Confirm("Is this okay? {*}")
.Prompter(PromptAsync)
;
return formBuilder.Build();
}
/// <summary>
/// Here is the method we're using for the PromptAsyncDelgate.
/// </summary>
private static async Task<FormPrompt> PromptAsync(IDialogContext context, FormPrompt prompt,
MyClass state, IField<MyClass> field)
{
var preamble = context.MakeMessage();
var promptMessage = context.MakeMessage();
// Check to see if the form is on the navigation step
if (field.Name.Contains("navigate") && prompt.Buttons.Any())
{
// If it's on the navigation step,
// we want to change or remove the No Preference line
if (you_want_to_change_it)
{
var noPreferenceButton = prompt.Buttons.Last();
// Make sure the Message stays the same or else
// FormFlow won't know what to do when this button is clicked
noPreferenceButton.Message = noPreferenceButton.Description;
noPreferenceButton.Description = "Back";
}
else if(you_want_to_remove_it)
{
prompt.Buttons.RemoveAt(prompt.Buttons.Count - 1);
}
}
if (prompt.GenerateMessages(preamble, promptMessage))
{
await context.PostAsync(preamble);
}
await context.PostAsync(promptMessage);
return prompt;
}
One additional note: "Back" is actually a special command in FormFlow. Whereas "No Preference" will take you back to the confirmation step, "Back" will take you to the last field in the form. If you want to actually put a back button in your navigation step, you can leave out this line:
noPreferenceButton.Message = noPreferenceButton.Description;

Refresh sectionTree on new user login

I have a sectiontree which varies based on the current logged in users UserType.
The thing is that if i log-out from the backoffice, and logs in with a new user with a lower UserType, the tree is not refreshed - The code is not rerun to generate the tree.
This means that the user with a non administrative UserType can access administrative areas in the section, as long as an administrator have been logged in earlier on the same solution.
How would i make the SectionTree refresh on new users login?
Update
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
var sectionApi = new SectionApiController();
// Root handler
if (id == Constants.System.Root.ToInvariantString())
{
this.RootHandler();
}
else if(id.Contains("COUNTRY_") || id.Contains("LEVEL_") )
{
var section = new IdConvert(id);
if ( section.Area.Equals("country") )
{
this.FirstLevelHandler(section.Id);
}
else if (section.Area.Equals("level"))
{
this.GetLevels(section.Id);
}
// Render clubs.
this.ClubHandler();
// Render levels
this.LevelHandler();
} else if(id.Contains("CLUB_")) {
}
else if(id.Contains("SPORTS_")) {
var Country = new IdConvert(id);
this.SportsHandler(Country.Id);
}
else if (id.Contains("QUESTIONS_"))
{
var Country = new IdConvert(id);
this.QuestionsHandler(Country.Id);
}
return this._nodes;
}
The Tree works fine, it renders what it should render. But It doesent refresh upon new user login.
I'm using the following to check wether or not a person is "admin"
public static bool IsAdministrator()
{
try
{
if (_curNewUser == null)
{
GetCurrentUser();
}
if (_curNewUser.UserType.Alias == "admin")
{
return true;
}
}
catch (Exception e) { }
return false;
}
Based on a comment you are not clearing _curNewUser when user logs out and that's why you are seeing this issue.
Instead of keeping the reference to _curNewUser you should use umbraco built in UmbracoContext.Current.Security.CurrentUser directly in your UserProvider and that will fix it, something like this:
public static bool IsAdministrator()
{
var user = UmbracoContext.Current.Security.CurrentUser;
return user != null && user.UserType.Alias == "admin";
}
No need for you to hook up to logout events or anything like that.

How to clear/change the query string in aspx page?

I have two pages
1. a.aspx and
2. b.aspx
I pass query string from "b.aspx?save=success" to a.aspx.
In Page Load of a.aspx I have the following code:
Page_Load()
{
if(!Postback)
{
if (Request.QueryString["save"] != null)
{
noDataFound.InnerHtml = "operation success";
}
}
}
Problem: On load of a.aspx page I get the message "operation success". This is Ok.But When I refresh the page again I get the same message as "operation success". How not to display again the same message on page refresh(pressing F5or reload).
function invokeMeMaster() {
var isPostBack = <%= Page.IsPostBack ? "true" : "false" %> ;
if (!isPostBack) {
/* START */
var query = getQueryParams(document.location.search);
var p = query.save;
if (sessionStorage.hits) {
sessionStorage.hits = Number(sessionStorage.hits) + 1;
} else {
sessionStorage.hits = 1;
}
if (p == "success" && (sessionStorage.hits) % 2 == 0) {
document.getElementById("<%=noDataFound.ClientID %>").innerText = "Testing...........";
}
function getQueryParams(qs) {
qs = qs.split("+").join(" ");
var params = {}, tokens,
re = /[?&]?([^=]+)=([^&]*)/g;
while (tokens = re.exec(qs)) {
params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
}
return params;
}
/* END */
} else {
document.getElementById("<%=noDataFound.ClientID %>").innerText = "";
}
}
window.onload = function () {
invokeMeMaster();
};
untested solution (Keeping F5 or Reload of Page in mind), may be you have do something like below:
if(!IsPostBack)
{
if (Request.QueryString["save"] != null && Session["CheckSuccess"] == null)
{
noDataFound.InnerHtml = "operation success";
Session["CheckSuccess"] = "true";
}
else
noDataFound.InnerHtml = string.Empty;
}
The best I can think of is using the IsPostback property to check that.
if (!this.IsPostback)
{
// first try
if (Request.QueryString["save"] != null)
{noDataFound.InnerHtml = "operation success";}
}
NOTE: IsPostback is not set on refresh, only if clicking a button or something alike triggers the ASP.NET postback action.
The other thing you could do is set a Session variable then the 'operation succesful' must be shown (probably you determine this in another Page.
// other page
Session["showSaveMessage"] = true;
// this page
if (Session["showSaveMessage"] == true)
{
// show message
Session["showSaveMessage"] = false;
}
A third option is to move this client side. Create a javascript action on load of the page. When a specific part is added to the query string (#showmessage), you can catch that and show the message (How to get the value from the GET parameters?).
Then redirect to the parameterless version (#) by setting the url to the stripped version. Set window.location.href or window.location.search for that (this won't cause a call to the webserver, since it is all client side).
This circumvents the drawbacks of the first solution, but introduces more code client side. Luckily, ASP.NET MVC has some mechanisms for this. Unfortunately ASP.NET Web Forms doesn't have those.

Checking individual fields for changes in an EntityState

In our database we have a user. A user can have certain metadata (Like their location, age, etc) associated with them. In addition to that they have a profile image.
A user can edit any of these things at any time.
The problem we run into is that when a user goes to edit their information, but they don't choose an image -- the previous image gets erased (Null).
I figure that when we go to save the modified user, we should be able to say if(user.profileImage == null) don't save it
I figure that would come into play in our user repository which uses the following code:
public void SaveUser(Domain.Entities.User user)
{
if (user.UserId == 0)
{
context.Users.Add(user);
}
else
{
context.Entry(user).State = EntityState.Modified;
//The logic would be here
}
context.SaveChanges();
}
However I seems that no matter what we try it doesn't work.
So my question is: Is there a way to check changes to individual fields instead of the entire EntityState?
I ended up storing image data in the Session and then if the image upload was null I simply inserted the session attribute into my image, effectively keeping the old image if left empty.
Session["image"] = productService.GetProduct(id).Image;
Then in the post I said...
if (image != null)
{
product.ImageType = image.ContentType;
product.Image = new byte[image.ContentLength];
image.InputStream.Read(product.Image, 0, image.ContentLength);
}
else
{
product.Image = (byte[])Session["image"];
}
Yes, there is a way:
context.Users.Attach(user);
context.Entry(user).Property(u => u.Location).IsModified = true;
context.Entry(user).Property(u => u.Age).IsModified = true;
// etc.
Be aware that you cannot set IsModified to false once the property is marked as modified. So, something like this ...
context.Entry(user).State = EntityState.Modified;
context.Entry(user).Property(u => u.Image).IsModified = false;
... does not work.
Edit Alternative solution:
public void SaveUser(Domain.Entities.User user)
{
if (user.UserId == 0)
{
context.Users.Add(user);
}
else
{
var userInDb = context.Users.Single(u => u.UserId == user.UserId);
if (user.profileImage == null)
user.profileImage = userInDb.profileImage;
context.Entry(userInDb).CurrentValues.SetValues(user);
}
context.SaveChanges();
}
This keeps the value of profileImage as it is in the database if the user didn't post a new image (= user.profileImage == null), otherwise it saves the new image.

Categories