(core) Direct users to last visited site when possible

Summary:
When clicking the logo in the top-left corner, or finishing a tutorial, we
now direct users to the site they last visited, if possible. If unknown, a
new redirect endpoint, /welcome/home, is used instead, which directs users
to a sensible location based on the number of sites they have.

Test Plan: Browser tests.

Reviewers: paulfitz

Reviewed By: paulfitz

Differential Revision: https://phab.getgrist.com/D3878
This commit is contained in:
George Gevoian
2023-05-01 11:24:23 -07:00
parent 65013331a3
commit 959f8a45c6
7 changed files with 109 additions and 28 deletions

View File

@@ -1,6 +1,7 @@
import {BehavioralPromptsManager} from 'app/client/components/BehavioralPromptsManager';
import {get as getBrowserGlobals} from 'app/client/lib/browserGlobals';
import {makeT} from 'app/client/lib/localization';
import {sessionStorageObs} from 'app/client/lib/localStorageObs';
import {error} from 'app/client/lib/log';
import {reportError, setErrorNotifier} from 'app/client/models/errors';
import {urlState} from 'app/client/models/gristUrlState';
@@ -83,6 +84,7 @@ export interface AppModel {
isTeamSite: boolean; // Is it a team site?
isLegacySite: boolean; // Is it a legacy site?
orgError?: OrgError; // If currentOrg is null, the error that caused it.
lastVisitedOrgDomain: Observable<string|null>;
currentProduct: Product|null; // The current org's product.
currentFeatures: Features; // Features of the current org's product.
@@ -227,6 +229,8 @@ export class AppModelImpl extends Disposable implements AppModel {
public readonly currentOrgUsage: Observable<OrgUsageSummary|null> = Observable.create(this, null);
public readonly lastVisitedOrgDomain = this.autoDispose(sessionStorageObs('grist-last-visited-org-domain'));
public readonly currentProduct = this.currentOrg?.billingAccount?.product ?? null;
public readonly currentFeatures = this.currentProduct?.features ?? {};
@@ -284,6 +288,14 @@ export class AppModelImpl extends Disposable implements AppModel {
this.dismissedPopups.set(seen ? DismissedPopup.values : []);
this.behavioralPromptsManager.reset();
};
this.autoDispose(subscribe(urlState().state, async (_use, {doc, org}) => {
// Keep track of the last valid org domain the user visited, ignoring those
// with a document id in the URL.
if (!this.currentOrg || doc) { return; }
this.lastVisitedOrgDomain.set(org ?? null);
}));
}
public get planName() {

View File

@@ -85,6 +85,10 @@ export function getLoginOrSignupUrl(nextUrl: string = _getCurrentUrl()): string
return _getLoginLogoutUrl('signin', nextUrl);
}
export function getWelcomeHomeUrl() {
return _buildUrl('welcome/home').href;
}
// Returns the relative URL (i.e. path) of the current page, except when it's the
// "/signed-out" page, in which case it returns the home page ("/").
// This is a good URL to use for a post-login redirect.
@@ -97,14 +101,19 @@ function _getCurrentUrl(): string {
// Returns the URL for the given login page, with 'next' param optionally set.
function _getLoginLogoutUrl(page: 'login'|'logout'|'signin'|'signup', nextUrl?: string | null): string {
const startUrl = new URL(window.location.href);
startUrl.pathname = addOrgToPath('', window.location.href, true) + '/' + page;
startUrl.search = '';
startUrl.hash = '';
const startUrl = _buildUrl(page);
if (nextUrl) { startUrl.searchParams.set('next', nextUrl); }
return startUrl.href;
}
function _buildUrl(page?: string): URL {
const startUrl = new URL(window.location.href);
startUrl.pathname = addOrgToPath('', window.location.href, true) + '/' + (page ?? '');
startUrl.search = '';
startUrl.hash = '';
return startUrl;
}
/**
* Implements the interface expected by UrlState. It is only exported for the sake of tests; the
* only public interface is the urlState() accessor.