(core) Welcome intro for viewers on a team site.

Summary:
Adding intro for a viewer on a teamsite.
Showing upgrade button for owners only.

Test Plan: new test

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D3557
pull/242/head
Jarosław Sadziński 2 years ago
parent b7686fa664
commit c359547f6b

@ -150,10 +150,9 @@ export class HomeModelImpl extends Disposable implements HomeModel, ViewSettings
return destWS && roles.canEdit(destWS.access) ? destWS : null;
});
// Whether to show intro: no docs (other than examples) and user may create docs.
// Whether to show intro: no docs (other than examples).
public readonly showIntro = Computed.create(this, this.workspaces, (use, wss) => (
wss.every((ws) => ws.isSupportWorkspace || ws.docs.length === 0) &&
Boolean(use(this.newDocWorkspace))));
wss.every((ws) => ws.isSupportWorkspace || ws.docs.length === 0)));
private _userOrgPrefs = Observable.create<UserOrgPrefs|undefined>(this, this._app.currentOrg?.userOrgPrefs);

@ -11,30 +11,69 @@ import {cssLink} from 'app/client/ui2018/links';
import {commonUrls, shouldHideUiElement} from 'app/common/gristUrls';
import {FullUser} from 'app/common/LoginSessionAPI';
import * as roles from 'app/common/roles';
import {dom, DomContents, styled} from 'grainjs';
import {Computed, dom, DomContents, styled} from 'grainjs';
export function buildHomeIntro(homeModel: HomeModel): DomContents {
const isViewer = homeModel.app.currentOrg?.access === roles.VIEWER;
const user = homeModel.app.currentValidUser;
if (user) {
return homeModel.app.isTeamSite ? makeTeamSiteIntro(homeModel) : makePersonalIntro(homeModel, user);
} else {
const isAnonym = !user;
const isPersonal = !homeModel.app.isTeamSite;
if (isAnonym) {
return makeAnonIntro(homeModel);
} else if (isPersonal) {
return makePersonalIntro(homeModel, user);
} else { // isTeamSite
if (isViewer) {
return makeViewerTeamSiteIntro(homeModel);
} else {
return makeTeamSiteIntro(homeModel);
}
}
}
function makeViewerTeamSiteIntro(homeModel: HomeModel) {
const personalOrg = Computed.create(null, use => use(homeModel.app.topAppModel.orgs).find(o => o.owner));
const docLink = (dom.maybe(personalOrg, org => {
return cssLink(
urlState().setLinkUrl({org: org.domain ?? undefined}),
'free, personal site',
testId('welcome-personal-url'));
}));
return [
css.docListHeader(
dom.autoDispose(personalOrg),
`Welcome to ${homeModel.app.currentOrgName}`,
productPill(homeModel.app.currentOrg, {large: true}),
testId('welcome-title')
),
cssIntroLine(
testId('welcome-info'),
"You have read-only access to this site. Currently there are no documents.", dom('br'),
"Any documents created in this site will appear here."),
cssIntroLine(
'Interested in using Grist outside of your team? Visit your ', docLink, '.',
testId('welcome-text')
)
];
}
function makeTeamSiteIntro(homeModel: HomeModel) {
const sproutsProgram = cssLink({href: commonUrls.sproutsProgram, target: '_blank'}, 'Sprouts Program');
return [
css.docListHeader(`Welcome to ${homeModel.app.currentOrgName}`,
css.docListHeader(
`Welcome to ${homeModel.app.currentOrgName}`,
productPill(homeModel.app.currentOrg, {large: true}),
testId('welcome-title')),
testId('welcome-title')
),
cssIntroLine('Get started by inviting your team and creating your first Grist document.'),
(shouldHideUiElement('helpCenter') ? null :
cssIntroLine('Learn more in our ', helpCenterLink(), ', or find an expert via our ', sproutsProgram, '.',
testId('welcome-text'))
cssIntroLine(
'Learn more in our ', helpCenterLink(), ', or find an expert via our ', sproutsProgram, '.',
testId('welcome-text')
)
),
makeCreateButtons(homeModel),
makeCreateButtons(homeModel)
];
}

@ -3,7 +3,7 @@
* page, both for anonymous and logged-in users.
*/
import {assert, driver, stackWrapFunc, WebElement} from 'mocha-webdriver';
import {assert, driver, Key, stackWrapFunc, WebElement} from 'mocha-webdriver';
import * as gu from 'test/nbrowser/gristUtils';
import {server, setupTestSuite} from 'test/nbrowser/testUtils';
@ -42,6 +42,42 @@ describe('HomeIntro', function() {
it('should render selected Examples workspace specially', testSelectedExamplesPage);
});
describe("Viewer on a team site", function() {
it('should show welcome for viewers', async function() {
// Sign in as to a team that has no docs.
await server.simulateLogin("Chimpy", "chimpy@getgrist.com", "FreeTeam");
await driver.get(server.getUrl('freeteam', ''));
await gu.editOrgAcls();
const orgInput = await driver.find('.test-um-member-new input');
await orgInput.sendKeys('charon@getgrist.com', Key.ENTER);
await gu.saveAcls();
await gu.removeLogin();
await server.simulateLogin("Charon", "charon@getgrist.com", "abyss");
await driver.get(server.getUrl('freeteam', ''));
// Check message specific to logged-in user and an empty team site.
assert.match(await driver.findWait('.test-welcome-title', 1000).getText(), new RegExp(`Welcome.* FreeTeam`));
assert.match(await driver.find('.test-welcome-info').getText(),
/You have read-only access to this site.*/);
assert.match(await driver.find('.test-welcome-text').getText(),
/Interested in using Grist outside of your team\? Visit your free, personal site\./);
assert.notMatch(await driver.find('.test-welcome-text').getText(), /sign up/);
await driver.find(".test-welcome-personal-url").click();
await gu.waitForDocMenuToLoad();
assert.equal(
await driver.find('.test-dm-other-sites-message').getText(),
'You are on your personal site. You also have access to the following sites:'
);
await driver.get(server.getUrl('freeteam', ''));
await gu.waitForDocMenuToLoad();
});
it('should not show Other Sites section', testOtherSitesSection);
it('should not show welcome buttons', testNoButtonsOnHome);
it('should show examples workspace with the intro', testExamplesSection);
it('should render selected Examples workspace specially', testSelectedExamplesPage);
});
describe("Logged-in on merged-org", function() {
it('should show welcome for logged-in user', async function() {
// Sign in as a new user who has no docs.
@ -117,6 +153,13 @@ describe('HomeIntro', function() {
}
}
async function testNoButtonsOnHome() {
const buttons = ['test-intro-templates', 'test-intro-import-doc', 'test-intro-create-doc'];
for (const button of buttons) {
assert.isFalse(await driver.find(`.${button}`).isPresent());
}
}
async function testCreateImport(isLoggedIn: boolean) {
// Create doc from intro button
await driver.find('.test-intro-create-doc').click();
@ -160,7 +203,7 @@ describe('HomeIntro', function() {
// Wait for doc to load, check it, then return to home page, and remove the doc so that we
// can see the intro again.
const checkDocAndRestore = stackWrapFunc(async function(isLoggedIn: boolean, docChecker: () => Promise<void>,
const checkDocAndRestore = async function(isLoggedIn: boolean, docChecker: () => Promise<void>,
stepsBackToDocMenu: number = 1) {
await gu.waitForDocToLoad();
await gu.dismissWelcomeTourIfNeeded();
@ -180,7 +223,7 @@ describe('HomeIntro', function() {
await driver.wait(async () => !(await driver.find('.test-modal-dialog').isPresent()), 3000);
}
assert.equal(await driver.find('.test-dm-doc').isPresent(), false);
});
};
async function testExamplesCollapsing() {
assert.equal(await driver.find('.test-dm-pinned-doc-name').isDisplayed(), true);

Loading…
Cancel
Save