Displaying form on first login - google-app-maker

I'm trying to work out how I can display a form to a user upon their first login to my app ( to fill in profile information) after which they can proceed to the regular site.
Could anyone point me in the right direction?
Thanks

You can make the trick using app startup script:
https://devsite.googleplex.com/appmaker/settings#app_start
Assuming that you have Profile model/datasource, code in your startup script will look similar to this:
loader.suspendLoad();
var profileDs = app.datasources.Profile;
// It would be more secure to move this filtering to the server side
profileDs.query.filters.UserEmail._equals = app.user.email;
profileDs.load({
success: function() {
if (profileDs.item === null) {
app.showPage(app.pages.CreateProfile);
} else {
app.showPage(app.pages.HomePage);
}
loader.resumeLoad();
},
failure: function() {
loader.resumeLoad();
// your fallback code goes here
}
});
If profile is absolute must, I would also recommend to enforce the check in onAttach event for every page but CreateProfile (to prevent navigation by direct link):
// Profile datasource should be already loaded by startup script
// when onAttach event is fired
if (app.datasources.Profile.item === null) {
throw new Error('Invalid operation!');
}

I suggest checking the user profile upon login. If the profile is not present, display the profile form, otherwise, proceed to the regular site.

Related

Protect the unauthenticated route in next js

I'm newly start on next js project , we have added a middlewate in next js to protect the route like below
useEffect(() => {
if (typeof window !== undefined) {
if (router.pathname == "/reset-password") {
// allow before login
}else if (!loginUser.authenticated) {
router.push('./login')
}
else if (loginUser.authenticated && !loginUser.selectedCustomer) {
router.push('./search-customer')
} else if (loginUser.authenticated && loginUser.selectedCustomer) {
if (router.pathname == "/") {
router.push("/stock-items/categories");
}
}
}
}, []);
return <>{props.children}</>;
But the issue is when any one direclty hit the specific route the controller goes to specific page and then nevigate to login screen if user is not login
i'm trying to stop that type of process , if user is not login then then any route should not be nevigate
please help us
useEffect runs on the client-side after the DOM has rendered (the typeof window check is therefore unnecessary).
This means any checks you place in the useEffect will be ran after the user see's the page (even just for a split second).
There are 2 options how you can handle this. Both are mentioned in the authentication guide in NextJS docs. I recommend taking a look.
Option 1
Set loading state default state as true, rendering a loading indicator while you do your authentication checks...
After you finish authenticating, toggle the loading state to false, allowing to render the page.
Option 2 (Recommended)
What you want to do here is take advantage of Next's core features such as getServerSideProps.
The code you put into getServerSideProps runs BEFORE the client receives any HTML payload.
In the getServerSideProps you can do your authentication, check for authentication cookie or Authorization header, depends what authentication method you use, and either redirect the user to /login page.

How to handover item._key from a listPage to a first editPage and second editPage

I have a created a page that contains a list of reviews. If the user clicks on a name, he will be directed to a page where he can edit the review details if needed.
In order to achieve this I have adapted the methods from the travel approval template.
The names in the list are link widgets that contain the onClick event which I simply adapted from the template to get a quick result. The client script is integrated in the onAttach event of the page.
//button method
if (event.ctrlKey === false && event.metaKey === false) {
event.preventDefault();
app.showPage(app.pages.ReviewDetails);
replaceUrlForEditRequest(widget.datasource.item._key);
}
//client script
function startLoadingEditRequestPage() {
google.script.url.getLocation(function(location) {
var requestId = location.parameter.requestId;
var requestDs = app.datasources.Reviews;
if (requestDs.creating) {
return;
}
if (!requestId) {
app.showPage(app.pages.Dashboard);
return;
}
if (requestDs.loaded && requestDs.query.filters._key._equals === requestId) {
return;
}
requestDs.unload();
requestDs.query.filters._key._equals = requestId;
requestDs.load();
});
}
The handover to the edit page works perfectly. The user will see the review details of the person he has clicked on (ex: Mary Poppins) and not the one who has the active index in the list. If the user clicks on a link "personal information" in the review details page he will be directed to another edit page where he can see other information of the person. For this I have simply amended the method from the template by adding another target page to the history.
function replaceUrlForEditRequest(requestId) {
var params = {
requestId: requestId
};
google.script.history.replace(null, params, app.pages.EditReview.name);
google.script.history.replace(null, params, app.pages.EditReviewDetails.name);
}
But when I duplicate the button method in the link on the review details page, it is not working. I always see the first name in my list and not the one that I had clicked on. How can I fix that?
The issue has been resolved. Although both pages were on the same model, they were not on the same datasource. After setting them to the same datasource, everything works fine.

manage views with css or regions in Backbone Marionette

I am working on a page having lot of input-controls and related divs. There are use-cases on this page where I am suppossed to show/hide the divs depending on the order of user clicking on input-controls in various follow-up screens.
Now the divs are all there in first load itself and by showing/hiding, the screen changes for the user. Now to show/hide I can use css and add view* class to .main content div depending on business logic.
ex.:
.main div{
display: none;
}
.main.view1 div.a,.main.view1 div.b,.main.view1 div.f{
display:block;
}
.main.view2 div.c,.main.view2 div.f {
display:block;
}
.main.view3 div.c,.main.view3 div.f {
display:block;
}
....etc
But this way the no. of css classes are getting unmanageable.
Please suggest if there is a better method I can use wherein it becomes easy to manage the user-flows. I think there are regions in marionette which can help me manage this. Please suggest the best way and elaborate if the answer is marionette.regions
You can model the application as a state machine to model complicated workflows.
To define a state machine:
Define all the states that your application can be in.
Define the set of actions that are allowed in each state. Each action will transition the state of the application from one state to another.
Write the business logic for each action which includes both persisting changes to the server and also changing the state of the views accordingly.
This design is similar to creating a DFA, but you can add extra behaviour according to your needs.
If this sounds too abstract, here's an example of a simple state machine.
Let's say you're building a simple login application.
Design the States and Actions
INITIAL_STATE: The user visits the page for the first time and both fields are empty. Let's say you only want to make the username visible, but not the password in this state. (Similar to the new Gmail workflow)
USERNAME_ENTRY_STATE: When the user types in the username and hits return, in this state, you want to display the username and hide the password. You can have onUsernameEntered as an action in this state.
PASSWORD_ENTRY_STATE: Now, the username view will be hidden and the password view will be shown. When the user hits return, you have to check if the usernames and passwords match. Let's call this action onPasswordEntered
AUTHENTICATED_STATE: When the server validates the username/password combination, let's say you want to show the home page. Let's call this action onAuthenticated
I have omitted handling the Authentication Failed case for now.
Design the Views:
In this case, we have the UsernameView and the PasswordView
Design the Models:
A single Auth model suffices for our example.
Design the Routes:
Check out the best practices for handling routes with Marionette. The state machine should be initialized in the login route.
Sample Pseudo-Code:
I've only shown the code relevant to managing the state machine. Rendering and event handling can be handled as usual;
var UsernameView = Backbone.View.extend({
initialize: function(options) {
this.stateMachine = options.stateMachine;
},
onUserNameEntered: function() {
username = //get username from DOM;
this.stateMachine.handleAction('onUserNameEntered', username)
},
show: function() {
//write logic to show the view
},
hide: function() {
//write logic to hide the view
}
});
var PasswordView = Backbone.View.extend({
initialize: function(options) {
this.stateMachine = options.stateMachine;
},
onPasswordEntered: function() {
password = //get password from DOM;
this.stateMachine.handleAction('onPasswordEntered', password)
},
show: function() {
//write logic to show the view
},
hide: function() {
//write logic to hide the view
}
});
Each state will have an entry function which will initialize the views and and exit function which will cleanup the views. Each state will also have functions corresponding to the valid actions in that state. For example:
var BaseState = function(options) {
this.stateMachine = options.stateMachine;
this.authModel = options.authModel;
};
var InitialState = BaseState.extend({
entry: function() {
//show the username view
// hide the password view
},
exit: function() {
//hide the username view
},
onUsernameEntered: function(attrs) {
this.authModel.set('username', attrs.username');
this.stateMachine.setState('PASSWORD_ENTRY_STATE');
}
});
Similarly, you can write code for other states.
Finally, the State Machine:
var StateMachine = function() {
this.authModel = new AuthModel;
this.usernameView = new UserNameView({stateMachine: this});
//and all the views
this.initialState = new InitialState({authModel: this.authModel, usernameView: this.usernameView});
//and similarly, all the states
this.currentState = this.initialState;
};
StateMachine.prototype = {
setState: function(stateCode) {
this.currentState.exit(); //exit from currentState;
this.currentState = this.getStateFromStateCode(stateCode);
this.currentState.entry();
},
handleAction: function(action, attrs) {
//check if action is valid for current state
if(actionValid) {
//call appropriate event handler in currentState
}
}
};
StateMachine.prototype.constructor = StateMachine;
For a simple application this seems to be an overkill. For complicated business logic, it is worth the effort. This design pattern automatically prevents cases such as double-clicking on a button, since you would have already moved on to the next state and the new state does not recognise the previous state's action.
Once you have built the state machine, other members of your team can just plug in their states and views and also can see the big picture in a single place.
Libraries such as Redux do some of the heavy-lifting shown here. So you may want to consider React + Redux + Immutable.js as well.

First Sign-in after Signup Show Config Page Once Effectively

I'm trying to show a popup or a template page if user has signed in for the first time after sign up basically allowing them configure some stuff on that page before going to dashboard home, It's only needed for convenience and here is what I got (telescope code)
Router.onBeforeAction(hasCompletedChannels);
hasCompletedChannels: function() {
if(!this.ready()) return;
var user = Meteor.user();
if (user && ! userCompletedChannels(user)){
this.render('connectChannels');
} else {
this.next();
}
}
Which I don't really like because this will always run every time, I want it to run just once, And don't even execute the check function. Is it possible to detect first sign in? (After signup)
I think you could just tie it to the specific route. Right now you're tying it to the Router object (every render forces that check as you point out). So if you define your login function to send someone to a specific route after sign-in, you could just verify on that route.
The function Accounts.onLogin gives you a way to do stuff after the login.
Something like
Router.route('profile', {
path: '/profile',
onBeforeAction: function() {
// Check some stoof
// If first time logged in
// render first time template
// else
// this.next() will render the profile page
},
waitOn: function() {
return [
// some subs
];
},
data: function() {
// some data
}
});
I'm assuming that its going to get routed to a page called profile (seems to make sense). You could check for first time logged in by some attribute you use in the user object and the fields you want filled out and force a render of a different template, or a subtemplate. Check out the Iron Router guide for more ideas on ways to configure it.
Best of luck

Framework7 starter page "pageInit" NOT WORKING

anyone using framework7 to create mobile website? I found it was great and tried to learn it by myself, now I meet this problem, after I create my App, I want to do something on the starter page initialization, here, my starter page is index.html, and I set data-page="index", now I write this below:
$$(document).on('pageInit', function (e) {
var page = e.detail.page;
// in my browser console, no "index page" logged
if (page.name === 'index') {
console.log("index page");
});
// but I changed to any other page other than index, it works
// my browser logged "another page"
if(page.name === 'login') {
console.log('another page');
}
});
Anyone can help? Thank you so much.
I have also encountered with the same problem before.
PageInit event doesn't work for initial page, only for pages that you navigate to, it will only work for index page if you navigate to some other page and then go back to index page.
So I see two options here:
Just not use pageInit event for index page - make its initialization just once (just make sure you put this javascript after all its html is ready, or e.g. use jquery's on document ready event)
Leave index page empty initially and load it dynamically via Framework7's mainView.loadContent method, then pageInit event would work for it (that was a good option for me as I had different index page each time, and I already loaded all other pages dynamically from underscore templates)
I am facing same issue and tried all solutions in various forums.. nothing actually worked. But after lot of RnD i stumbled upon following solution ...
var $$ = Dom7;
$$(document).on('page:init', function (e) {
if(e.detail.page.name === "index"){
//do whatever.. remember "page" is now e.detail.page..
$$(e.detail.page.container).find('#latest').html("my html here..");
}
});
var me = new Framework7({material: true});
var mainview = me.addView('.view-main', {});
.... and whatever else JS here..
this works perfectly..
surprisingly you can use "me" before initializing it..
for using for first page u better use document ready event. and for reloading page event you better use Reinit event.
if jquery has used.
$(document).on('ready', function (e) {
// ... mainView.activePage.name = "index"
});
$(document).on('pageReinit', function (e) {
//... this event occur on reloading anypage.
});

Resources