removes onboarding activity and associated handling code (#4307)

This commit is contained in:
Dimitri Mitropoulos 2021-12-15 16:15:25 -05:00 committed by GitHub
parent 0abbefaf9e
commit 2edfdee345
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 26 additions and 330 deletions

View File

@ -3,7 +3,6 @@ import {
ACTIVITY_DEBUG,
ACTIVITY_HOME,
ACTIVITY_MIGRATION,
ACTIVITY_ONBOARDING,
ACTIVITY_SPEC,
ACTIVITY_UNIT_TEST,
FLEXIBLE_URL_REGEX,
@ -45,7 +44,6 @@ describe('isWorkspaceActivity', () => {
it('should return false', () => {
expect(isWorkspaceActivity(ACTIVITY_HOME)).toBe(false);
expect(isWorkspaceActivity(ACTIVITY_ONBOARDING)).toBe(false);
expect(isWorkspaceActivity(ACTIVITY_MIGRATION)).toBe(false);
expect(isWorkspaceActivity(ACTIVITY_ANALYTICS)).toBe(false);
});
@ -57,7 +55,6 @@ describe('isValidActivity', () => {
expect(isValidActivity(ACTIVITY_DEBUG)).toBe(true);
expect(isValidActivity(ACTIVITY_UNIT_TEST)).toBe(true);
expect(isValidActivity(ACTIVITY_HOME)).toBe(true);
expect(isValidActivity(ACTIVITY_ONBOARDING)).toBe(true);
expect(isValidActivity(ACTIVITY_MIGRATION)).toBe(true);
expect(isValidActivity(ACTIVITY_ANALYTICS)).toBe(true);
});

View File

@ -167,7 +167,6 @@ export const ACTIVITY_SPEC: GlobalActivity = 'spec';
export const ACTIVITY_DEBUG: GlobalActivity = 'debug';
export const ACTIVITY_UNIT_TEST: GlobalActivity = 'unittest';
export const ACTIVITY_HOME: GlobalActivity = 'home';
export const ACTIVITY_ONBOARDING: GlobalActivity = 'onboarding';
export const ACTIVITY_MIGRATION: GlobalActivity = 'migration';
export const ACTIVITY_ANALYTICS: GlobalActivity = 'analytics';
export const DEPRECATED_ACTIVITY_INSOMNIA = 'insomnia';
@ -183,7 +182,6 @@ export const isDesignActivity = (activity?: string): activity is GlobalActivity
return true;
case ACTIVITY_HOME:
case ACTIVITY_ONBOARDING:
case ACTIVITY_MIGRATION:
case ACTIVITY_ANALYTICS:
default:
@ -199,7 +197,6 @@ export const isCollectionActivity = (activity?: string): activity is GlobalActiv
case ACTIVITY_SPEC:
case ACTIVITY_UNIT_TEST:
case ACTIVITY_HOME:
case ACTIVITY_ONBOARDING:
case ACTIVITY_MIGRATION:
case ACTIVITY_ANALYTICS:
default:
@ -213,7 +210,6 @@ export const isValidActivity = (activity: string): activity is GlobalActivity =>
case ACTIVITY_DEBUG:
case ACTIVITY_UNIT_TEST:
case ACTIVITY_HOME:
case ACTIVITY_ONBOARDING:
case ACTIVITY_MIGRATION:
case ACTIVITY_ANALYTICS:
return true;

View File

@ -189,7 +189,6 @@ export default async function migrateFromDesigner({
const coreSettings = await models.settings.getOrCreate();
const propertiesToPersist = [
'_id',
'hasPromptedOnboarding',
'hasPromptedToMigrateFromDesigner',
];
propertiesToPersist.forEach(s => {

View File

@ -189,13 +189,8 @@ async function _createModelInstances() {
async function _updateFlags({ launches }: Stats) {
const firstLaunch = launches === 1;
if (firstLaunch) {
await models.settings.patch({
hasPromptedOnboarding: false,
// Don't show the analytics preferences prompt as it is part of the onboarding flow
hasPromptedAnalytics: true,
});
await models.settings.patch({ hasPromptedAnalytics: false });
}
}

View File

@ -52,12 +52,11 @@ export function init(): BaseSettings {
fontVariantLigatures: false,
forceVerticalLayout: false,
// Only existing users updating from an older version should see the analytics prompt.
// So by default this flag is set to false, and is toggled to true during initialization for new users.
/**
* Only existing users updating from an older version should see the analytics prompt.
* So by default this flag is set to false, and is toggled to true during initialization for new users.
*/
hasPromptedAnalytics: false,
// Users should only see onboarding during first launch, and anybody updating from an older version should not see it, so by default this flag is set to true, and is toggled to false during initialization
hasPromptedOnboarding: true,
hasPromptedToMigrateFromDesigner: false,
hotKeyRegistry: hotkeys.newDefaultRegistry(),
httpProxy: '',

View File

@ -450,12 +450,6 @@ class General extends PureComponent<Props> {
setting="hasPromptedToMigrateFromDesigner"
/>
</div>
<div className="form-row pad-top-sm">
<BooleanSetting
label="Has seen onboarding experience"
setting="hasPromptedOnboarding"
/>
</div>
<div className="form-row pad-top-sm">
<BooleanSetting
label="Has seen analytics prompt"

View File

@ -1,189 +0,0 @@
import 'swagger-ui-react/swagger-ui.css';
import { autoBindMethodsForReact } from 'class-autobind-decorator';
import React, { Fragment, PureComponent } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { AUTOBIND_CFG, getAppLongName, getAppName, getAppSynopsis } from '../../common/constants';
import { database as db } from '../../common/database';
import type { BaseModel } from '../../models';
import { isWorkspace, WorkspaceScopeKeys } from '../../models/workspace';
import { ForceToWorkspace } from '../redux/modules/helpers';
import { importFile, importUri } from '../redux/modules/import';
import { Analytics } from './analytics';
import { showPrompt } from './modals';
import { OnboardingContainer } from './onboarding-container';
import type { WrapperProps } from './wrapper';
type ReduxProps = ReturnType<typeof mapDispatchToProps>;
interface Props extends ReduxProps {
wrapperProps: WrapperProps;
}
interface State {
step: number;
}
@autoBindMethodsForReact(AUTOBIND_CFG)
class WrapperOnboarding extends PureComponent<Props, State> {
state: State = {
step: 1,
};
componentDidMount() {
db.onChange(this._handleDbChange);
}
componentWillUnmount() {
// Unsubscribe DB listener
db.offChange(this._handleDbChange);
}
_handleDbChange(changes: [string, BaseModel, boolean][]) {
for (const change of changes) {
if (isWorkspace(change[1])) {
setTimeout(() => {
this._handleDone();
}, 400);
}
}
}
_handleDone() {
this._nextActivity();
}
_handleBackStep(e: React.SyntheticEvent<HTMLAnchorElement>) {
e.preventDefault();
this.setState(state => ({
step: state.step - 1,
}));
}
async _handleCompleteAnalyticsStep() {
this.setState(state => ({
step: state.step + 1,
}));
}
_handleImportFile() {
this.props.handleImportFile({
forceToWorkspace: ForceToWorkspace.new,
forceToScope: WorkspaceScopeKeys.design,
});
}
_handleImportUri(defaultValue: string) {
const { handleImportUri } = this.props;
showPrompt({
title: 'Import From URI',
defaultValue: typeof defaultValue === 'string' ? defaultValue : '',
placeholder: 'https://example.com/openapi-spec.yaml',
label: 'URI to Import',
onComplete: value => {
handleImportUri(value, {
forceToWorkspace: ForceToWorkspace.new,
forceToScope: WorkspaceScopeKeys.design,
});
},
});
}
_handleImportPetstore() {
this._handleImportUri(
'https://gist.githubusercontent.com/gschier/4e2278d5a50b4bbf1110755d9b48a9f9' +
'/raw/801c05266ae102bcb9288ab92c60f52d45557425/petstore-spec.yaml',
);
}
_handleSkipImport() {
this._nextActivity();
}
_nextActivity() {
this.props.wrapperProps.handleGoToNextActivity();
}
hasStep1() {
return this.props.wrapperProps.settings.incognitoMode === false;
}
renderStep1() {
return (
<Analytics
wrapperProps={this.props.wrapperProps}
handleDone={this._handleCompleteAnalyticsStep}
/>
);
}
renderStep2() {
const {
settings: { enableAnalytics },
} = this.props.wrapperProps;
const lastStep = this.hasStep1() ? (
<p className="notice success text-left margin-top margin-bottom">
<a href="#" className="pull-right" onClick={this._handleBackStep}>
Back
</a>
{enableAnalytics
? `Thanks for helping make ${getAppName()} better!`
: 'Opted out of analytics'}
</p>
) : null;
return (
<Fragment>
{lastStep}
<p>Import an OpenAPI spec to get started:</p>
<button key="file" className="btn btn--clicky" onClick={this._handleImportFile}>
From File
</button>
<button key="petstore" className="btn btn--clicky" onClick={this._handleImportPetstore}>
Petstore Example
</button>
<button key="skip" className="btn btn--super-compact" onClick={this._handleSkipImport}>
Skip
</button>
</Fragment>
);
}
render() {
const { step } = this.state;
let stepBody;
if (step === 1 && this.hasStep1()) {
stepBody = this.renderStep1();
} else {
stepBody = this.renderStep2();
}
return (
<OnboardingContainer
wrapperProps={this.props.wrapperProps}
header={'Welcome to ' + getAppLongName()}
subHeader={getAppSynopsis()}
>
{stepBody}
</OnboardingContainer>
);
}
}
const mapDispatchToProps = dispatch => {
const bound = bindActionCreators({
importFile,
importUri,
}, dispatch);
return ({
handleImportFile: bound.importFile,
handleImportUri: bound.importUri,
});
};
export default connect(null, mapDispatchToProps)(WrapperOnboarding);

View File

@ -9,7 +9,6 @@ import {
ACTIVITY_DEBUG,
ACTIVITY_HOME,
ACTIVITY_MIGRATION,
ACTIVITY_ONBOARDING,
ACTIVITY_SPEC,
ACTIVITY_UNIT_TEST,
AUTOBIND_CFG,
@ -83,7 +82,6 @@ import { WrapperDebug } from './wrapper-debug';
import { WrapperDesign } from './wrapper-design';
import WrapperHome from './wrapper-home';
import { WrapperMigration } from './wrapper-migration';
import WrapperOnboarding from './wrapper-onboarding';
import { WrapperUnitTest } from './wrapper-unit-test';
const spectral = initializeSpectral();
@ -728,10 +726,8 @@ export class Wrapper extends PureComponent<WrapperProps, State> {
{activity === ACTIVITY_MIGRATION && <WrapperMigration wrapperProps={this.props} />}
{activity === ACTIVITY_ANALYTICS && <WrapperAnalytics wrapperProps={this.props} />}
{(activity === ACTIVITY_ONBOARDING || activity === null) && (
<WrapperOnboarding wrapperProps={this.props} />
{(activity === ACTIVITY_ANALYTICS || activity === null) && (
<WrapperAnalytics wrapperProps={this.props} />
)}
</Fragment>
</Fragment>

View File

@ -9,7 +9,6 @@ import {
ACTIVITY_DEBUG,
ACTIVITY_HOME,
ACTIVITY_MIGRATION,
ACTIVITY_ONBOARDING,
ACTIVITY_SPEC,
ACTIVITY_UNIT_TEST,
DEPRECATED_ACTIVITY_INSOMNIA,
@ -43,11 +42,9 @@ const mockStore = configureMockStore(middlewares);
const createSettings = (
hasPromptedMigration: boolean,
hasPromptedOnboarding: boolean,
hasPromptedAnalytics: boolean,
) => {
const settings = models.settings.init();
settings.hasPromptedOnboarding = hasPromptedOnboarding;
settings.hasPromptedToMigrateFromDesigner = hasPromptedMigration;
settings.hasPromptedAnalytics = hasPromptedAnalytics;
return settings;
@ -68,7 +65,6 @@ describe('global', () => {
ACTIVITY_DEBUG,
ACTIVITY_UNIT_TEST,
ACTIVITY_HOME,
ACTIVITY_ONBOARDING,
ACTIVITY_MIGRATION,
])('should update local storage and track event: %s', (activity: GlobalActivity) => {
const expectedEvent = {
@ -82,36 +78,14 @@ describe('global', () => {
);
});
it('should update flag for onboarding prompted', async done => {
await models.settings.patch({
hasPromptedToMigrateFromDesigner: false,
hasPromptedOnboarding: false,
});
async function onDatabaseChange() {
const settings = await models.settings.getOrCreate();
expect(settings.hasPromptedToMigrateFromDesigner).toBe(false);
expect(settings.hasPromptedOnboarding).toBe(true);
database.offChange(onDatabaseChange);
done();
}
database.onChange(onDatabaseChange);
setActiveActivity(ACTIVITY_ONBOARDING);
});
it('should update flag for migration prompted', async done => {
await models.settings.patch({
hasPromptedToMigrateFromDesigner: false,
hasPromptedOnboarding: false,
});
async function onDatabaseChange() {
const settings = await models.settings.getOrCreate();
expect(settings.hasPromptedToMigrateFromDesigner).toBe(true);
expect(settings.hasPromptedOnboarding).toBe(false);
database.offChange(onDatabaseChange);
done();
}
@ -151,9 +125,9 @@ describe('global', () => {
});
describe('goToNextActivity', () => {
it('should go from onboarding to home', async () => {
const settings = createSettings(false, false, true);
const activeActivity = ACTIVITY_ONBOARDING;
it('should go from analytics to home', async () => {
const settings = createSettings(false, true);
const activeActivity = ACTIVITY_ANALYTICS;
const store = mockStore({
global: {
activeActivity,
@ -171,28 +145,8 @@ describe('global', () => {
]);
});
it('should go from migration to onboarding', async () => {
const settings = createSettings(false, false, true);
const activeActivity = ACTIVITY_MIGRATION;
const store = mockStore({
global: {
activeActivity,
},
entities: {
settings: [settings],
},
});
await store.dispatch(goToNextActivity());
expect(store.getActions()).toEqual([
{
type: SET_ACTIVE_ACTIVITY,
activity: ACTIVITY_ONBOARDING,
},
]);
});
it('should go from migration to analytics', async () => {
const settings = createSettings(false, true, false);
const settings = createSettings(false, false);
const activeActivity = ACTIVITY_MIGRATION;
const store = mockStore({
global: {
@ -212,7 +166,7 @@ describe('global', () => {
});
it('should go from migration to home', async () => {
const settings = createSettings(true, true, true);
const settings = createSettings(true, true);
const activeActivity = ACTIVITY_MIGRATION;
const store = mockStore({
global: {
@ -234,7 +188,7 @@ describe('global', () => {
it.each([ACTIVITY_SPEC, ACTIVITY_DEBUG, ACTIVITY_UNIT_TEST, ACTIVITY_HOME])(
'should not change activity from: %s',
async (activeActivity: GlobalActivity) => {
const settings = createSettings(false, true, true);
const settings = createSettings(false, true);
const store = mockStore({
global: {
activeActivity,
@ -324,10 +278,9 @@ describe('global', () => {
ACTIVITY_DEBUG,
ACTIVITY_UNIT_TEST,
ACTIVITY_HOME,
ACTIVITY_ONBOARDING,
ACTIVITY_ANALYTICS,
])('should initialize %s from local storage', async activity => {
const settings = createSettings(true, true, true);
const settings = createSettings(true, true);
const store = mockStore({
global: {},
entities: {
@ -344,7 +297,7 @@ describe('global', () => {
});
it('should initialize from local storage and migrate deprecated activity', async () => {
const settings = createSettings(true, true, true);
const settings = createSettings(true, true);
const store = mockStore({
global: {},
entities: {
@ -361,8 +314,8 @@ describe('global', () => {
expect(store.getActions()).toEqual([expectedEvent]);
});
it('should go to onboarding if initialized at migration', async () => {
const settings = createSettings(true, false, true);
it('should go to home if initialized at migration', async () => {
const settings = createSettings(true, true);
const store = mockStore({
global: {},
entities: {
@ -373,7 +326,7 @@ describe('global', () => {
global.localStorage.setItem(`${LOCALSTORAGE_PREFIX}::activity`, JSON.stringify(activity));
const expectedEvent = {
type: SET_ACTIVE_ACTIVITY,
activity: ACTIVITY_ONBOARDING,
activity: ACTIVITY_HOME,
};
await store.dispatch(initActiveActivity());
expect(store.getActions()).toEqual([expectedEvent]);
@ -384,7 +337,7 @@ describe('global', () => {
null,
undefined,
)('should go to home if initialized with an unsupported value: %s', async activity => {
const settings = createSettings(true, true, true);
const settings = createSettings(true, true);
const store = mockStore({
global: {},
entities: {
@ -401,7 +354,7 @@ describe('global', () => {
});
it('should go to home if local storage key not found', async () => {
const settings = createSettings(true, true, true);
const settings = createSettings(true, true);
const store = mockStore({
global: {},
entities: {
@ -417,7 +370,7 @@ describe('global', () => {
});
it('should go to home if initialized at migration and onboarding seen', async () => {
const settings = createSettings(true, true, true);
const settings = createSettings(true, true);
const store = mockStore({
global: {},
entities: {
@ -435,7 +388,7 @@ describe('global', () => {
});
it('should prompt to migrate', async () => {
const settings = createSettings(false, true, true);
const settings = createSettings(false, true);
fsExistsSyncSpy.mockReturnValue(true);
const store = mockStore({
global: {},
@ -455,7 +408,7 @@ describe('global', () => {
});
it('should not prompt to migrate if default directory not found', async () => {
const settings = createSettings(false, true, true);
const settings = createSettings(false, true);
fsExistsSyncSpy.mockReturnValue(false);
const store = mockStore({
global: {},
@ -474,26 +427,8 @@ describe('global', () => {
expect(fsExistsSyncSpy).toHaveBeenCalledWith(getDesignerDataDir());
});
it('should prompt to onboard', async () => {
const settings = createSettings(true, false, true);
const store = mockStore({
global: {},
entities: {
settings: [settings],
},
});
const activity = ACTIVITY_HOME;
global.localStorage.setItem(`${LOCALSTORAGE_PREFIX}::activity`, JSON.stringify(activity));
const expectedEvent = {
type: SET_ACTIVE_ACTIVITY,
activity: ACTIVITY_ONBOARDING,
};
await store.dispatch(initActiveActivity());
expect(store.getActions()).toEqual([expectedEvent]);
});
it('should prompt to change analytics settings', async () => {
const settings = createSettings(true, true, false);
const settings = createSettings(true, false);
const store = mockStore({
global: {},
entities: {

View File

@ -13,7 +13,6 @@ import {
ACTIVITY_DEBUG,
ACTIVITY_HOME,
ACTIVITY_MIGRATION,
ACTIVITY_ONBOARDING,
DEPRECATED_ACTIVITY_INSOMNIA,
isValidActivity,
} from '../../../common/constants';
@ -299,14 +298,9 @@ export const loadRequestStop = (requestId: string) => ({
requestId,
});
function _getNextActivity(settings: Settings, currentActivity: GlobalActivity): GlobalActivity {
function _getNextActivity(settings: Settings, currentActivity: GlobalActivity) {
switch (currentActivity) {
case ACTIVITY_MIGRATION:
// Has not seen the onboarding step? Go to onboarding
if (!settings.hasPromptedOnboarding) {
return ACTIVITY_ONBOARDING;
}
// Has not seen the analytics prompt? Go to it
if (!settings.hasPromptedAnalytics) {
return ACTIVITY_ANALYTICS;
@ -315,8 +309,7 @@ function _getNextActivity(settings: Settings, currentActivity: GlobalActivity):
// Otherwise, go to home
return ACTIVITY_HOME;
case ACTIVITY_ONBOARDING:
// Always go to home after onboarding
case ACTIVITY_ANALYTICS:
return ACTIVITY_HOME;
default:
@ -354,14 +347,6 @@ export const setActiveActivity = (activity: GlobalActivity) => {
});
break;
case ACTIVITY_ONBOARDING:
models.settings.patch({
hasPromptedOnboarding: true,
// Don't show the analytics preferences prompt as it is part of the onboarding flow
hasPromptedAnalytics: true,
});
break;
case ACTIVITY_ANALYTICS:
models.settings.patch({
hasPromptedAnalytics: true,
@ -756,8 +741,6 @@ export const initActiveActivity = () => (dispatch, getState) => {
if (!settings.hasPromptedToMigrateFromDesigner && fs.existsSync(getDesignerDataDir())) {
trackEvent('Data', 'Migration', 'Auto');
overrideActivity = ACTIVITY_MIGRATION;
} else if (!settings.hasPromptedOnboarding) {
overrideActivity = ACTIVITY_ONBOARDING;
} else if (!settings.hasPromptedAnalytics) {
overrideActivity = ACTIVITY_ANALYTICS;
}

View File

@ -56,7 +56,6 @@ export interface Settings {
fontVariantLigatures: boolean;
forceVerticalLayout: boolean;
hasPromptedAnalytics: boolean;
hasPromptedOnboarding: boolean;
hasPromptedToMigrateFromDesigner: boolean;
hotKeyRegistry: HotKeyRegistry;
httpProxy: string;

View File

@ -12,15 +12,7 @@ const clickDontShare = async app => {
.then(e => e.click());
};
const clickSkipImport = async app => {
await app.client
.$('.onboarding__content__body')
.then(e => e.$('button=Skip'))
.then(e => e.click());
};
export const skipOnboardingFlow = async app => {
await analyticsMessageShown(app);
await clickDontShare(app);
await clickSkipImport(app);
};