You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gristlabs_grist-core/app/client/ui/SupportGristPage.ts

209 lines
6.4 KiB

import {makeT} from 'app/client/lib/localization';
import {AppModel} from 'app/client/models/AppModel';
import {TelemetryModel, TelemetryModelImpl} from 'app/client/models/TelemetryModel';
import {basicButtonLink, bigBasicButton, bigBasicButtonLink, bigPrimaryButton} from 'app/client/ui2018/buttons';
import {theme} from 'app/client/ui2018/cssVars';
import {icon} from 'app/client/ui2018/icons';
import {cssLink} from 'app/client/ui2018/links';
import {loadingSpinner} from 'app/client/ui2018/loaders';
import {commonUrls} from 'app/common/gristUrls';
import {TelemetryPrefsWithSources} from 'app/common/InstallAPI';
import {Computed, Disposable, dom, makeTestId, styled} from 'grainjs';
const testId = makeTestId('test-support-grist-page-');
const t = makeT('SupportGristPage');
export class SupportGristPage extends Disposable {
private readonly _model: TelemetryModel = new TelemetryModelImpl(this._appModel);
private readonly _optInToTelemetry = Computed.create(this, this._model.prefs,
(_use, prefs) => {
if (!prefs) { return null; }
return prefs.telemetryLevel.value !== 'off';
})
.onWrite(async (optIn) => {
const telemetryLevel = optIn ? 'limited' : 'off';
await this._model.updateTelemetryPrefs({telemetryLevel});
});
constructor(private _appModel: AppModel) {
super();
this._model.fetchTelemetryPrefs().catch(reportError);
}
public buildTelemetrySection() {
return cssSection(
dom.domComputed(this._model.prefs, prefs => {
if (prefs === null) {
return cssSpinnerBox(loadingSpinner());
}
if (!this._appModel.isInstallAdmin()) {
// TODO: We are no longer serving this page to non-admin users, so this branch should no
// longer match, and this version perhaps should be removed.
if (prefs.telemetryLevel.value === 'limited') {
return [
cssParagraph(t(
'This instance is opted in to telemetry. Only the site administrator has permission to change this.',
))
];
} else {
return [
cssParagraph(t(
'This instance is opted out of telemetry. Only the site administrator has permission to change this.',
))
];
}
} else {
return [
cssParagraph(t(
'Support Grist by opting in to telemetry, which helps us understand how the product ' +
'is used, so that we can prioritize future improvements.'
)),
cssParagraph(
t('We only collect usage statistics, as detailed in our {{link}}, never document contents.', {
link: telemetryHelpCenterLink(),
}),
),
cssParagraph(t('You can opt out of telemetry at any time from this page.')),
this._buildTelemetrySectionButtons(prefs),
];
}
}),
testId('telemetry-section'),
);
}
public getTelemetryOptInObservable() { return this._optInToTelemetry; }
public _buildTelemetrySectionButtons(prefs: TelemetryPrefsWithSources) {
const {telemetryLevel: {value, source}} = prefs;
if (source === 'preferences') {
return dom.domComputed(this._optInToTelemetry, (optedIn) => {
if (optedIn) {
return [
cssOptInOutMessage(
t('You have opted in to telemetry. Thank you!'), ' 🙏',
testId('telemetry-section-message'),
),
cssOptOutButton(t('Opt out of Telemetry'),
dom.on('click', () => this._optInToTelemetry.set(false)),
),
];
} else {
return [
cssOptInButton(t('Opt in to Telemetry'),
dom.on('click', () => this._optInToTelemetry.set(true)),
),
];
}
});
} else {
return cssOptInOutMessage(
value !== 'off'
? [t('You have opted in to telemetry. Thank you!'), ' 🙏']
: t('You have opted out of telemetry.'),
testId('telemetry-section-message'),
);
}
}
public buildSponsorshipSection() {
return cssSection(
cssParagraph(
t(
'Grist software is developed by Grist Labs, which offers free and paid ' +
'hosted plans. We also make Grist code available under a standard free ' +
'and open OSS license (Apache 2.0) on {{link}}.',
{link: gristCoreLink()},
),
),
cssParagraph(
t(
'You can support Grist open-source development by sponsoring ' +
'us on our {{link}}.',
{link: sponsorGristLink()},
),
),
cssParagraph(t(
'We are a small and determined team. Your support matters a lot to us. ' +
'It also shows to others that there is a determined community behind this product.'
)),
cssSponsorButton(
cssButtonIconAndText(icon('Heart'), cssButtonText(t('Manage Sponsorship'))),
{href: commonUrls.githubSponsorGristLabs, target: '_blank'},
),
testId('sponsorship-section'),
);
}
public buildSponsorshipSmallButton() {
return basicButtonLink('💛 ', t('Sponsor'),
{href: commonUrls.githubSponsorGristLabs, target: '_blank'});
}
}
function telemetryHelpCenterLink() {
return cssLink(
t('Help Center'),
{href: commonUrls.helpTelemetryLimited, target: '_blank'},
);
}
function sponsorGristLink() {
return cssLink(
t('GitHub Sponsors page'),
{href: commonUrls.githubSponsorGristLabs, target: '_blank'},
);
}
function gristCoreLink() {
return cssLink(
t('GitHub'),
{href: commonUrls.githubGristCore, target: '_blank'},
);
}
const cssSection = styled('div', ``);
const cssParagraph = styled('div', `
color: ${theme.text};
font-size: 14px;
line-height: 20px;
margin-bottom: 12px;
`);
const cssOptInOutMessage = styled(cssParagraph, `
line-height: 40px;
font-weight: 600;
margin-top: 24px;
margin-bottom: 0px;
`);
const cssOptInButton = styled(bigPrimaryButton, `
margin-top: 24px;
`);
const cssOptOutButton = styled(bigBasicButton, `
margin-top: 24px;
`);
const cssSponsorButton = styled(bigBasicButtonLink, `
margin-top: 24px;
`);
const cssButtonIconAndText = styled('div', `
display: flex;
align-items: center;
`);
const cssButtonText = styled('span', `
margin-left: 8px;
`);
const cssSpinnerBox = styled('div', `
margin-top: 24px;
text-align: center;
`);