mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Replace questionnaire for new users with a popup asking for just their primary use-case.
Summary: - WelcomeQuestions implements the new popup. - Popup shows up on any doc-list page, the first time the user visits one after signing up and setting their name. - Submits responses to the same "New User Questions" doc, which has been changed to accept two new columns (ChoiceList of use_cases, and Text for use_other). - Improve modals on mobile along the way. Test Plan: Added browser tests and tested manually Reviewers: alexmojaki Reviewed By: alexmojaki Subscribers: jarek Differential Revision: https://phab.getgrist.com/D3213
This commit is contained in:
@@ -7,7 +7,9 @@ import {Features} from 'app/common/Features';
|
||||
import {GristLoadConfig} from 'app/common/gristUrls';
|
||||
import {FullUser} from 'app/common/LoginSessionAPI';
|
||||
import {LocalPlugin} from 'app/common/plugin';
|
||||
import {UserPrefs} from 'app/common/Prefs';
|
||||
import {getOrgName, Organization, OrgError, UserAPI, UserAPIImpl} from 'app/common/UserAPI';
|
||||
import {getUserPrefsObs} from 'app/client/models/UserPrefs';
|
||||
import {bundleChanges, Computed, Disposable, Observable, subscribe} from 'grainjs';
|
||||
|
||||
export {reportError} from 'app/client/models/errors';
|
||||
@@ -58,6 +60,7 @@ export interface AppModel {
|
||||
orgError?: OrgError; // If currentOrg is null, the error that caused it.
|
||||
|
||||
currentFeatures: Features; // features of the current org's product.
|
||||
userPrefsObs: Observable<UserPrefs>;
|
||||
|
||||
pageType: Observable<PageType>;
|
||||
|
||||
@@ -177,6 +180,8 @@ export class AppModelImpl extends Disposable implements AppModel {
|
||||
public readonly currentFeatures = (this.currentOrg && this.currentOrg.billingAccount) ?
|
||||
this.currentOrg.billingAccount.product.features : {};
|
||||
|
||||
public readonly userPrefsObs = getUserPrefsObs(this);
|
||||
|
||||
// Get the current PageType from the URL.
|
||||
public readonly pageType: Observable<PageType> = Computed.create(this, urlState().state,
|
||||
(use, state) => (state.doc ? "doc" : (state.billing ? "billing" : (state.welcome ? "welcome" : "home"))));
|
||||
|
||||
@@ -1,40 +1,60 @@
|
||||
import {localStorageObs} from 'app/client/lib/localStorageObs';
|
||||
import {AppModel} from 'app/client/models/AppModel';
|
||||
import {UserOrgPrefs} from 'app/common/Prefs';
|
||||
import {UserOrgPrefs, UserPrefs} from 'app/common/Prefs';
|
||||
import {Computed, Observable} from 'grainjs';
|
||||
|
||||
/**
|
||||
* Creates an observable that returns UserOrgPrefs, and which stores them when set.
|
||||
*
|
||||
* For anon user, the prefs live in localStorage. Note that the observable isn't actually watching
|
||||
* for changes on the server, it will only change when set.
|
||||
*/
|
||||
export function getUserOrgPrefsObs(appModel: AppModel): Observable<UserOrgPrefs> {
|
||||
const savedPrefs = appModel.currentValidUser ? appModel.currentOrg?.userOrgPrefs : undefined;
|
||||
if (savedPrefs) {
|
||||
const prefsObs = Observable.create<UserOrgPrefs>(null, savedPrefs);
|
||||
return Computed.create(null, (use) => use(prefsObs))
|
||||
.onWrite(userOrgPrefs => {
|
||||
prefsObs.set(userOrgPrefs);
|
||||
return appModel.api.updateOrg('current', {userOrgPrefs});
|
||||
});
|
||||
} else {
|
||||
const userId = appModel.currentUser?.id || 0;
|
||||
const jsonPrefsObs = localStorageObs(`userOrgPrefs:u=${userId}`);
|
||||
return Computed.create(null, jsonPrefsObs, (use, p) => (p && JSON.parse(p) || {}) as UserOrgPrefs)
|
||||
.onWrite(userOrgPrefs => {
|
||||
jsonPrefsObs.set(JSON.stringify(userOrgPrefs));
|
||||
});
|
||||
}
|
||||
interface PrefsTypes {
|
||||
userOrgPrefs: UserOrgPrefs;
|
||||
userPrefs: UserPrefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an observable that returns a particular preference value from `prefsObs`, and which
|
||||
* stores it when set.
|
||||
*/
|
||||
export function getUserOrgPrefObs<Name extends keyof UserOrgPrefs>(
|
||||
prefsObs: Observable<UserOrgPrefs>, prefName: Name
|
||||
): Observable<UserOrgPrefs[Name]> {
|
||||
return Computed.create(null, (use) => use(prefsObs)[prefName])
|
||||
.onWrite(value => prefsObs.set({...prefsObs.get(), [prefName]: value}));
|
||||
function makePrefFunctions<P extends keyof PrefsTypes>(prefsTypeName: P) {
|
||||
type PrefsType = PrefsTypes[P];
|
||||
|
||||
/**
|
||||
* Creates an observable that returns UserOrgPrefs, and which stores them when set.
|
||||
*
|
||||
* For anon user, the prefs live in localStorage. Note that the observable isn't actually watching
|
||||
* for changes on the server, it will only change when set.
|
||||
*/
|
||||
function getPrefsObs(appModel: AppModel): Observable<PrefsType> {
|
||||
const savedPrefs = appModel.currentValidUser ? appModel.currentOrg?.[prefsTypeName] : undefined;
|
||||
if (savedPrefs) {
|
||||
const prefsObs = Observable.create<PrefsType>(null, savedPrefs!);
|
||||
return Computed.create(null, (use) => use(prefsObs))
|
||||
.onWrite(prefs => {
|
||||
prefsObs.set(prefs);
|
||||
return appModel.api.updateOrg('current', {[prefsTypeName]: prefs});
|
||||
});
|
||||
} else {
|
||||
const userId = appModel.currentUser?.id || 0;
|
||||
const jsonPrefsObs = localStorageObs(`${prefsTypeName}:u=${userId}`);
|
||||
return Computed.create(null, jsonPrefsObs, (use, p) => (p && JSON.parse(p) || {}) as PrefsType)
|
||||
.onWrite(prefs => {
|
||||
jsonPrefsObs.set(JSON.stringify(prefs));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an observable that returns a particular preference value from `prefsObs`, and which
|
||||
* stores it when set.
|
||||
*/
|
||||
function getPrefObs<Name extends keyof PrefsType>(
|
||||
prefsObs: Observable<PrefsType>, prefName: Name
|
||||
): Observable<PrefsType[Name]> {
|
||||
return Computed.create(null, (use) => use(prefsObs)[prefName])
|
||||
.onWrite(value => prefsObs.set({...prefsObs.get(), [prefName]: value}));
|
||||
}
|
||||
|
||||
return {getPrefsObs, getPrefObs};
|
||||
}
|
||||
|
||||
// Functions actually exported are:
|
||||
// - getUserOrgPrefsObs(appModel): Observsble<UserOrgPrefs>
|
||||
// - getUserOrgPrefObs(userOrgPrefsObs, prefName): Observsble<PrefType[prefName]>
|
||||
// - getUserPrefsObs(appModel): Observsble<UserPrefs>
|
||||
// - getUserPrefObs(userPrefsObs, prefName): Observsble<PrefType[prefName]>
|
||||
|
||||
export const {getPrefsObs: getUserOrgPrefsObs, getPrefObs: getUserOrgPrefObs} = makePrefFunctions('userOrgPrefs');
|
||||
export const {getPrefsObs: getUserPrefsObs, getPrefObs: getUserPrefObs} = makePrefFunctions('userPrefs');
|
||||
|
||||
Reference in New Issue
Block a user