In my MAUI app I have a first page, let's call it Landing, showing a list of accounting objects. Among them, ExpenseReports.
Each ExpenseReport contains 1...N Expense objects.
Each Expense contains 1..N Receipts.
Each Receipt has three main fields in the database: ID, Image (blob) and Thumbnail Image (blob)
I start by selecting an ExpenseReport on the Landing page.
When navigating to the ExpenseReportEdit page, I send a few params, among them the ExpenseReport object just selected, and a deep copy of it, ExpenseReportOriginal, which will not be altered at all, serving only for field comparison (you will see why below).
On the ExpenseReportEdit page I see the list of the 1...N expenses.
I select one Expense for editing.
Since I will leave this page, I will pass the following params to the ExpenseEdit page:
ExpenseReport, ExpenseReportOriginal, Expense, and ExpenseOriginal.
The ExpenseEdit page gets these four objects and shows the details of this particular expense.
On this page I can take pictures of my receipts. I have noticed that my
[RelayCommand]
private async Task ScanReceipt()
method navigates away from my ExpenseEdit page because I've noticed that, if I set a breakpoint on the OnAppearing method, it gets hit after taking the photo.
So. When taking a photo, I populate a Receipt object. Then I say
Expense.Receipts.Add(receipt);
Expense.Receipts is a list of Receipt objects.
Let's say I have taken 6 photos. The Expense.Receipts list will now contain 6 big (about 7MB each) objects of type Receipt.
After each image taken the code goes again thru the OnAppearing() of the ExpenseEdit page and I can see that the app is already very slow.
I still do not save the Expense object at this point, the intention is to do whatever more edits are necessary and navigate back to the ExpenseReportEdit page (of course, passing the whole Expense object back to it, with all the receipts, and a flag saying "This expense is dirty"). Then after probably some more edits on the ExpenseReportEdit page, the user can finally click on the SaveExpenseReport button, at which point the whole tree will be saved to the SQLite DB.
The "Original" objects are used like so: after each edit on a page, let's say I change field "ExpenseDescription" on some expense, the code in the xaml.cs TextChanged() event will compare the Expense and the ExpenseOriginal objects and if they are in any way different (amy field of these objects), a "changesWereMade" flag is set, thus enabling the "Save" button and also the question "You have made changes on this object etc etc" on the "Back" button if the object was not saved.
If the user clicks on Save on the ExpenseEdit page, I REPLACE the incoming Expense objcet by the just-changed Expense in the Expenses collection of the incoming ExpenseReport I have received, and pass that back to the ExpenseReportEdit page.
If on the ExpenseEdit page the user clicks on Back without saving, I will just send the original ExpenseReport back to the ExpenseReportEdit page.
So let me try to explain a bit better: The code goes like this:
On the ExpenseReportEdit page I can change the ExpenseReport directly.
If I want to change an "Expense" leaf, I click on it, navigate to the ExpenseEdite page. Here I can change the details of the expense. I can also click on Add Receipt to add some image to the Expense object
Any changes in this Expense will be returned to the ExpenseReportEdit page for saving the whole tree (unless the user cancels the changes on this ExpenseEdit page, case in which I replace the Expense with the ExpenseOriginal object and then again navigate back to the ExpenseReportEdit page).
This works. But my problem is now with the heavy load I have to carry along - I have to carry my ExpenseReport with all the Expenses with all the Receipts objects thru memory,
because I do NOT save the images to the DB on the ExpenseEdit page and then populate the list of receipts only with the IDs of those images, I actually carry the whole huge bytearrays with me....
I would love to have this in the Expense model
public List<int> ReceiptIDs { get; set; }
instead of
public List<Receipt> Receipts { get; set; }
With a good smartphone with a powerful camera the Receipts are quite big, and the app is very slow.
So now I see a few solutions - none seems to be okay, however.
a) I save the receipt to the database as a Tentative image, carry only the IDs in the Expense object instead of the ByteArray, and then, once back in the ExpenseReportEdit page, if the user decides NOT to save the ExpenseReport, go to the Db and delete those records. But this is quite ugly, you'll agree.
b) I save the receipt to some static class (image of the tree ExpenseReport -> Expenses -> Receipts) and when saving in the ExpenseReportEdit page, taking the whole object from there instead of carrying it among pages like I am doing now.
None of these "solutions" seems okay to me. So, short of asking my manager for a change of the app so that the user saves the Expense directly on the ExpenseEdit page and does not save the whole tree all at once (you'll agree that this is less than elegant !), what would by my best option ?
I am sure that my object circulation is less than optimal, so then.... where have I gone wrong ?
I really hope I have explained my situation as well as I could, and that I have made myself understood. I'm quite tired right now and I am not sure whether this is the case.
Thank you for reading !
Related
I'm creating a progress bar for my product pages that show how much you have to spent until you get free shipping. Everything works great, except that I can't get the shipping rates with the rate for which the client will get free shipping. Currently my request looks like this:
if (cart.items.some(i => i.requires_shipping === true)) {
const url = '/cart/shipping_rates.json?' + encodeURI('shipping_address[zip]={$zip}&shipping_address[country]={$country}');
jQuery.getJSON(url, function(shipping_rates) {
console.log(shipping_rates);
});
}
The If statement checks if a Product is in the Cart that requires shipping. With this setup I'm getting the shipping method based on the current cart value. Expl: If the cart value is below the 25€ free shipping threshold the shipping method shows that shipping costs 3.5€ and once the cart value is above the threshold, it show that the shipping is free.
I want to get the threshold based on the country the client lives in. Is there a way to get the Threshold with a request without making complicated logic based around the shipping_rates?
The problem with this approach is that your request is retrieving currently available shipping rates for the given order but they are not showing the next level.
There are a few problems with this approach:
You won't get next-tier details (e.g. how much $ away you are from the next shipping). It would be best if you stored that info somewhere and JS is not a good place for that.
I am not sure if Script Editor / Shopify Functions shipping discount will be calculated at this point. It will be in the checkout step, but I have never heard of that happening on the cart request level. If my concerns are true then this endpoint is not the best choice for being the source of truth.
You are making a javascript request which takes time and assuming you are showing that info somewhere, you are experiencing reflow and CLS issue
3.1 Such a request could fail.
Since you need to define business logic somewhere and you can't get rid of that requirement, I would highly recommend keeping that logic in meta fields/theme customizer (tiers, discount values) and calculating everything using liquid code. On refresh, you can use Section Rendering API to reload a given part of the page. This is how it solves the above problems:
Your tiers setup is stored in fields/theme customizer which can be accessed using liquid.
You don't need to worry about falsy result from API (shipping rate set up in admin panel vs possible discount) because this is defined by you. The downside is you need to keep discount logic in two places - backend and frontend (liquid) and keep it in sync.
Because you calculate everything using liquid you don't get reflows, issues with loading fetch data and content layout shift.
I am building a Workshop App which will be used for data entry by a large number of operational staff members (> 200).
I would like to implement the following set-up:
The staff will enter the data on existing Ontology Objects. To facilitate this, I embedded a Foundry Form into the Object View, and set-up a corresponding write-back data set.
The Ontology Objects in question will be displayed in an Object Table in Workshop.
The staff member will select an Object from the Object Table.
The selected Object will be opened in an Object View.
The staff member will enter data on the Object View (Foundry Form being displayed here).
I need to make sure that no concurrent data entry can/will happen. How can I achieve this?
I am thinking about removing any Object which is opened in the Object View from the Object Table, but I am not sure if this is the best solution to the problem or how to achieve the removal from the table.
I am assuming that this must be a common problem and there would be a Design Pattern/Standard Functionality to solve this.
You'll have the best behavior here if you replace your Foundry Form with Foundry Actions. These actions are defined in the Ontology Management App and provide a more robust security model for managing object edits and are more tightly integrated into the various object-layer tools in Foundry.
Then in your Object View, rather than using the Foundry Forms widget, choose to create a new "Workshop-backed" Object View tab - this option is under the dropdown next to the New Tab button - and within the Workshop module use the Inline Action Form to embed the action form that you've configured in the Ontology Management App, supplying the variable representing the current object as the default for the object parameter in the Action.
With regards to simultaneous edits, in Actions, when the form is populated (i.e. when that tab is opened), the version of the underlying object(s) are registered. When the edit request is submitted, the current version of the object is checked, and if the version is different (i.e. there have been edits applied since the form was loaded) the user will be presented with message to the effect that the object has been updated and the edits will not be applied.
This protects against the case of a user overwriting a near-simultaneous edit without reviewing the changes first and does so at the writeback layer, rather than with logic in your application front-end.
There is not currently an approach to reflect real-time user interaction between two Workshop sessions. For instance, there is no way for the session of User A to "know" that User B has opened Object X in their session and is making a change.
If you wanted to do something for a bit of convenience or display signaling, you could create a small object type related to your main object - something like "Edit Lock" that looks like:
| id | primary_object_id | user_id | timestamp | timeout
And then in your Workshop app, there's a button above the table that says "Edit Selected Object".
When you click this button, you trigger an Action to create a new "edit lock" object populated with the current user and the current timestamp and say a timeout of 15 minutes from now.
Then in the object view for the primary object in question, on the same tab where you have the edit form embedded, you can create a variable that gets the edit locks associated with that object via a search around and use a Function to create a boolean variable if there is an "active" edit lock. And use this to conditionally show a banner to the user or otherwise give them a visual indication that another user has indicated they're making changes.
This won't push to the other session, and it'd be just for display purposes, the real protection still comes from the underlying object versioning that Actions enforces, but it might be a nice user affordance to layer on top if you really expect to commonly run into this situation.
I have an Html page with a load of javascript that changes between views.
Some views require the person to be logged in, and consequently prompt for it.
How can I note the person has successfully logged in, using the javascript, that will not be a security issue, but will mean the person does not have to repeatedly log in for each view. I do not want to keep on going back to the server each time.
Edit:::
To explain more. Here are the problems I see.
Lets say I have the following in my javascript:
var isLoggedIn = true;
var userEmail = "myemail#mysite.com";
Anyone can hack my code to change these values and then get another person's info. That is not good. So instead of isLoggedIn do I need something like a hashed password stored in the javascript:
var userHashedPassword = "shfasjfhajshfalshfla";
But every where I read, they say you should not keep any password stuff in memory for any length of time.
So what variables do I keep and where? The user will be constantly flicking between non-user specific divs and user-based divs, and I do not want them to have to constantly log in each time.
****Edit 2:*****
This is what I am presently doing, but am not happy with.
There is a page/div with 3 radio buttons. Vacant games (does not require user information), My Game (requires knowledge of user and must be signed in), My Old Games (also requires logged in status).
When first going on the page it defaults on vacant games, and gets the info from the server, which does not require login.
In two variables in the javascript I have
var g_Email = "";
var g_PasswordEncrypted = "";
Note these are both 0 length strings.
If the user wants to view their games, they click the My Games radio button. The code checks to see if the g_Email and PasswordEncrypted are 0 length strings, if they are it goes to a div where they need to login.
When the user submits their loging info, it goes to the server, checks their details, and sends back an ok message, and all the info (My Games) that the user was requesting.
So if the login was a success, then
g_Email = "myemail#mysite.com";
g_PasswordEncrypted = "this is and encrypted version of the password";
If there is any failure in login, these two are instead set to "".
Then when the user navigates to any page that requires login, it checks to see if these two strings are filled. If they are, it will not go to a login page when you request information like My Games.
Instead it just sends the info in these strings to the server, along with the My Games request. The server still checks these Email and encrypted password are valid before sending back the info, but at the client side, the user has not had to repeatedly input this info each time.
If there is any failure in the server request, it just sends back an error message (I am using ajax) in the callback function, which knows to set the g_Email and g_PasswordEncrypted to "" if there is anything wrong. (In the latter case, the client side knows it has to re-request the login details because these two strings are "").
The thing I do not like is I am keeping the Encryted password on the person's client machine. If they walk away from their machine, someone can open up the debugger in something like chrome and extract these details, and then hack it into their machine some time later.
If javascript loads content for each view from the server then it is for server to know if a current session belongs to logged user or not. In case the user is not logged, the server responses with prompt to login, otherwise it sends content of the view.
If javascript bulds content for the views deriving it from the data that was already received from the server then it should use some variable keeping state of the user (logged/not_logged). And depending on that value javascript will either show a prompt to login or display required content of the view.
I have a service in AngularJS that generates all the steps needed, the current state of each step (done, current, show, etc) and an associated directive that actually implements the service and displays the data of the service. But, there are 2 steps that are divided in 4 and 3 steps each:
Step one
Discounts
Activities
Duration
Payment Length
Step two
Identification
Personal data
Payment
How can I "save" the state of my form in case the person leaves the site and comes back later? Is it safe to use localStorage? I'm no providing support for IE6 or 7. I thought of using cookies, but that can end up being weak (or not)
Either local storage or cookies should be fine. I doubt this will be an issue, but keep in mind that both have a size limit. Also, it goes without saying that the form state will only be restored if the user returns on the same browser, and without having deleted cookies / local storage.
Another option could be to save the information server side. If the user is signed in, you can make periodic AJAX calls with the data and store the state on the server. When the user finishes all steps, you can make an AJAX call telling the server to delete any saved data it might have. This allows you to restore state even if the user returns on a different browser, as long as he is signed in.
Regardless of what direction you go with this, you can use jQuery's serialize method to serialize the form into a string and save it using your choice of storage.
Ok so here is what is happening:
I have a client that I am building an application for. My client has a flowchart that they would like posted on the front page of their application. Check. My client then wants this flowchart to be set up as an image map so that a user could click one of the boxes in this flowchart and be taken to a report in another part of the application. Check.
All of that is elementary and, in a technical sense, works. The issue is, and it is an issue I have encountered before with APEX, is that every time a user clicks one of these links it takes them to the login screen. It seems that linking directly to a page's URL breaks the session and requires you to login again, even if you are linking from one page in the application to another in the same application.
I have played with all of the authentication settings in a hopes of fixing this and tried to determine what is breaking the session exactly but with no luck.
Has anyone else had this problem and could share their method for fixing it? I really cant have users logging in every time they click a link and I also cannot simply remove the authentication on the pages. Thanks in advance.
You should pass on the session id in your links. If you don't, then apex will see this as a new session. You can tell from the url: take note of the session id in your url when you are on your image map. When you select an application, take another look at the session id part in the url. If they are different, then you are starting a new session each time.
/apex/f?p=190:90:1674713700462259:::::
190 -> application id
90 -> page id
1674713700462259 -> Session id
To pass on the session, it depends where you construct your links.
In PLSQL, you can find it through :SESSION or :APP_SESSION
For example, in a plsql dynamic region: htp.p('the session id is '||:SESSION);
In javascript code you can use $v("pInstance") to retrieve the value dynamically, or use &APP_SESSION. which will have the value substituted at runtime.
Small example:
function printsome(){
var d = $("<div></div>");
d.text('&APP_SESSION. = ' + $v("pInstance"));
$("body").append(d);
};
So you probably just need to alter the construction of your link somewhat to include the session!
I was assuming the binding variables will do the job. But they were helpless.
Best way is to pass the current session id to an item then use the item value in the link.
f?p=&APP_ID.:32:&P31_SESSION.:::P32_CUSTOMER_ID:#CUSTOMER_ID#