mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Delete my account button
Summary: Adding new "Delete my account" button to the profile page that allows users to remove completely their accounts as long as they don't own any team site. Test Plan: Added Reviewers: georgegevoian, paulfitz Reviewed By: georgegevoian, paulfitz Subscribers: paulfitz Differential Revision: https://phab.getgrist.com/D4037
This commit is contained in:
@@ -87,6 +87,11 @@ export function getLogoutUrl(): string {
|
||||
return _getLoginLogoutUrl('logout');
|
||||
}
|
||||
|
||||
// Get the URL that users are redirect to after deleting their account.
|
||||
export function getAccountDeletedUrl(): string {
|
||||
return _getLoginLogoutUrl('account-deleted', {nextUrl: ''});
|
||||
}
|
||||
|
||||
// Get URL for the signin page.
|
||||
export function getLoginOrSignupUrl(options: GetLoginOrSignupUrlOptions = {}): string {
|
||||
return _getLoginLogoutUrl('signin', options);
|
||||
@@ -96,19 +101,21 @@ export function getWelcomeHomeUrl() {
|
||||
return _buildUrl('welcome/home').href;
|
||||
}
|
||||
|
||||
const FINAL_PATHS = ['/signed-out', '/account-deleted'];
|
||||
|
||||
// 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 ("/").
|
||||
// "/signed-out" page or "/account-deleted", in which case it returns the home page ("/").
|
||||
// This is a good URL to use for a post-login redirect.
|
||||
function _getCurrentUrl(): string {
|
||||
const {hash, pathname, search} = new URL(window.location.href);
|
||||
if (pathname.endsWith('/signed-out')) { return '/'; }
|
||||
if (FINAL_PATHS.some(final => pathname.endsWith(final))) { return '/'; }
|
||||
|
||||
return parseFirstUrlPart('o', pathname).path + search + hash;
|
||||
}
|
||||
|
||||
// Returns the URL for the given login page.
|
||||
function _getLoginLogoutUrl(
|
||||
page: 'login'|'logout'|'signin'|'signup',
|
||||
page: 'login'|'logout'|'signin'|'signup'|'account-deleted',
|
||||
options: GetLoginOrSignupUrlOptions = {}
|
||||
): string {
|
||||
const {srcDocId, nextUrl = _getCurrentUrl()} = options;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import {detectCurrentLang, makeT} from 'app/client/lib/localization';
|
||||
import {AppModel, reportError} from 'app/client/models/AppModel';
|
||||
import {urlState} from 'app/client/models/gristUrlState';
|
||||
import * as css from 'app/client/ui/AccountPageCss';
|
||||
import {ApiKey} from 'app/client/ui/ApiKey';
|
||||
import {AppHeader} from 'app/client/ui/AppHeader';
|
||||
import {buildChangePasswordDialog} from 'app/client/ui/ChangePasswordDialog';
|
||||
import {DeleteAccountDialog} from 'app/client/ui/DeleteAccountDialog';
|
||||
import {translateLocale} from 'app/client/ui/LanguageMenu';
|
||||
import {leftPanelBasic} from 'app/client/ui/LeftPanelCommon';
|
||||
import {MFAConfig} from 'app/client/ui/MFAConfig';
|
||||
import {pagePanels} from 'app/client/ui/PagePanels';
|
||||
@@ -14,11 +17,9 @@ import {cssBreadcrumbs, separator} from 'app/client/ui2018/breadcrumbs';
|
||||
import {labeledSquareCheckbox} from 'app/client/ui2018/checkbox';
|
||||
import {cssLink} from 'app/client/ui2018/links';
|
||||
import {select} from 'app/client/ui2018/menus';
|
||||
import {getPageTitleSuffix} from 'app/common/gristUrls';
|
||||
import {getGristConfig} from 'app/common/urlUtils';
|
||||
import {FullUser} from 'app/common/UserAPI';
|
||||
import {detectCurrentLang, makeT} from 'app/client/lib/localization';
|
||||
import {translateLocale} from 'app/client/ui/LanguageMenu';
|
||||
import {getPageTitleSuffix} from 'app/common/gristUrls';
|
||||
import {Computed, Disposable, dom, domComputed, makeTestId, Observable, styled, subscribe} from 'grainjs';
|
||||
|
||||
const testId = makeTestId('test-account-page-');
|
||||
@@ -161,7 +162,10 @@ designed to ensure that you're the only person who can access your account, even
|
||||
inputArgs: [{size: '5'}], // Lower size so that input can shrink below ~152px.
|
||||
})
|
||||
)),
|
||||
),
|
||||
!getGristConfig().canCloseAccount ? null : [
|
||||
dom.create(DeleteAccountDialog, user),
|
||||
],
|
||||
),
|
||||
testId('body'),
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {makeT} from 'app/client/lib/localization';
|
||||
import {AppModel} from 'app/client/models/AppModel';
|
||||
import {getLoginUrl, getMainOrgUrl, urlState} from 'app/client/models/gristUrlState';
|
||||
import {getLoginUrl, getMainOrgUrl, getSignupUrl, urlState} from 'app/client/models/gristUrlState';
|
||||
import {AppHeader} from 'app/client/ui/AppHeader';
|
||||
import {leftPanelBasic} from 'app/client/ui/LeftPanelCommon';
|
||||
import {pagePanels} from 'app/client/ui/PagePanels';
|
||||
@@ -21,7 +21,8 @@ export function createErrPage(appModel: AppModel) {
|
||||
return gristConfig.errPage === 'signed-out' ? createSignedOutPage(appModel) :
|
||||
gristConfig.errPage === 'not-found' ? createNotFoundPage(appModel, message) :
|
||||
gristConfig.errPage === 'access-denied' ? createForbiddenPage(appModel, message) :
|
||||
createOtherErrorPage(appModel, message);
|
||||
gristConfig.errPage === 'account-deleted' ? createAccountDeletedPage(appModel) :
|
||||
createOtherErrorPage(appModel, message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,6 +68,20 @@ export function createSignedOutPage(appModel: AppModel) {
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a page that shows the user is logged out.
|
||||
*/
|
||||
export function createAccountDeletedPage(appModel: AppModel) {
|
||||
document.title = t("Account deleted{{suffix}}", {suffix: getPageTitleSuffix(getGristConfig())});
|
||||
|
||||
return pagePanelsError(appModel, t("Account deleted{{suffix}}", {suffix: ''}), [
|
||||
cssErrorText(t("Your account has been deleted.")),
|
||||
cssButtonWrap(bigPrimaryButtonLink(
|
||||
t("Sign up"), {href: getSignupUrl()}, testId('error-signin')
|
||||
))
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a "Page not found" page.
|
||||
*/
|
||||
|
||||
@@ -168,6 +168,7 @@ export const theme = {
|
||||
lightText: new CustomProp('theme-text-light', undefined, colors.slate),
|
||||
darkText: new CustomProp('theme-text-dark', undefined, 'black'),
|
||||
errorText: new CustomProp('theme-text-error', undefined, colors.error),
|
||||
errorTextHover: new CustomProp('theme-text-error-hover', undefined, '#BF0A31'),
|
||||
dangerText: new CustomProp('theme-text-danger', undefined, '#FFA500'),
|
||||
disabledText: new CustomProp('theme-text-disabled', undefined, colors.slate),
|
||||
|
||||
|
||||
@@ -341,6 +341,8 @@ export interface ConfirmModalOptions {
|
||||
hideCancel?: boolean;
|
||||
extraButtons?: DomContents;
|
||||
modalOptions?: IModalOptions;
|
||||
saveDisabled?: Observable<boolean>;
|
||||
width?: ModalWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,7 +354,7 @@ export function confirmModal(
|
||||
title: DomElementArg,
|
||||
btnText: DomElementArg,
|
||||
onConfirm: () => Promise<void>,
|
||||
{explanation, hideCancel, extraButtons, modalOptions}: ConfirmModalOptions = {},
|
||||
{explanation, hideCancel, extraButtons, modalOptions, saveDisabled, width}: ConfirmModalOptions = {},
|
||||
): void {
|
||||
return saveModal((ctl, owner): ISaveModalOptions => ({
|
||||
title,
|
||||
@@ -360,8 +362,9 @@ export function confirmModal(
|
||||
saveLabel: btnText,
|
||||
saveFunc: onConfirm,
|
||||
hideCancel,
|
||||
width: 'normal',
|
||||
width: width ?? 'normal',
|
||||
extraButtons,
|
||||
saveDisabled,
|
||||
}), modalOptions);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user