Merge pull request #742 from filecoin-project/@jason-leyser/confirmation-modals

Confirmation popup components
This commit is contained in:
martinalong 2021-05-13 14:48:56 -07:00 committed by GitHub
commit a01c262f6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 392 additions and 153 deletions

View File

@ -69,11 +69,6 @@ export const signOut = async ({ viewer }) => {
// NOTE(jim): Permanently deletes you, forever.
export const deleteMe = async ({ viewer }) => {
const message = "Do you really want to delete your account? It will be permanently removed";
if (!window.confirm(message)) {
return false;
}
await Actions.updateSearch("delete-user");
let response = await Actions.deleteViewer();

View File

@ -51,13 +51,11 @@ const STYLES_CONTENT = css`
}
`;
const STYLES_SIDEBAR = css`
z-index: ${Constants.zindex.sidebar};
const STYLES_SIDEBAR_ELEMENTS = css`
height: 100vh;
width: ${Constants.sizes.sidebar}px;
padding: 0;
flex-shrink: 0;
position: fixed;
background-color: rgba(195, 195, 196, 1);
top: 0;
right: 0;
@ -66,12 +64,20 @@ const STYLES_SIDEBAR = css`
@media (max-width: ${Constants.sizes.mobile}px) {
width: 100%;
}
/*
@supports ((-webkit-backdrop-filter: blur(25px)) or (backdrop-filter: blur(25px))) {
-webkit-backdrop-filter: blur(25px);
backdrop-filter: blur(25px);
background-color: rgba(195, 195, 196, 0.6);
}
*/
`;
const STYLES_SIDEBAR = css`
position: fixed;
top: 0; right: 0;
margin: auto;
z-index: ${Constants.zindex.sidebar};
`;
const STYLES_SIDEBAR_HEADER = css`
@ -202,13 +208,15 @@ export default class ApplicationLayout extends React.Component {
enabled
onOutsideRectEvent={this._handleDismiss}
>
<div
css={STYLES_SIDEBAR}
ref={(c) => {
this._sidebar = c;
}}
>
{sidebarElements}
<div css={STYLES_SIDEBAR}>
<div
css={STYLES_SIDEBAR_ELEMENTS}
ref={(c) => {
this._sidebar = c;
}}
>
{sidebarElements}
</div>
</div>
</Boundary>
) : null}

View File

@ -21,6 +21,7 @@ import { Tag } from "~/components/system/components/Tag";
import isEqual from "lodash/isEqual";
import cloneDeep from "lodash/cloneDeep";
import ProcessedText from "~/components/core/ProcessedText";
import { ConfirmationModal } from "~/components/core/ConfirmationModal";
const DEFAULT_BOOK =
"https://slate.textile.io/ipfs/bafkreibk32sw7arspy5kw3p5gkuidfcwjbwqyjdktd5wkqqxahvkm2qlyi";
@ -38,7 +39,6 @@ const STYLES_NO_VISIBLE_SCROLL = css`
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar;
::-webkit-scrollbar {
width: 0px;
display: none;
@ -77,13 +77,11 @@ const STYLES_SIDEBAR = css`
justify-content: space-between;
background-color: rgba(20, 20, 20, 0.8);
${STYLES_NO_VISIBLE_SCROLL}
@supports ((-webkit-backdrop-filter: blur(75px)) or (backdrop-filter: blur(75px))) {
-webkit-backdrop-filter: blur(75px);
backdrop-filter: blur(75px);
background-color: rgba(150, 150, 150, 0.2);
}
@media (max-width: ${Constants.sizes.mobile}px) {
display: none;
}
@ -95,7 +93,6 @@ const STYLES_DISMISS_BOX = css`
right: 16px;
color: ${Constants.system.darkGray};
cursor: pointer;
:hover {
color: ${Constants.system.white};
}
@ -123,7 +120,6 @@ const STYLES_META_TITLE = css`
text-decoration: none;
word-break: break-all;
overflow-wrap: anywhere;
:hover {
color: ${Constants.system.blue};
}
@ -173,11 +169,9 @@ const STYLES_ACTION = css`
border-bottom: 1px solid #3c3c3c;
display: flex;
align-items: center;
:hover {
color: ${Constants.system.brand};
}
:last-child {
border: none;
}
@ -243,7 +237,6 @@ const STYLES_AUTOSAVE = css`
position: absolute;
top: 24px;
left: 16px;
@keyframes slate-animations-autosave {
0% {
opacity: 0;
@ -300,6 +293,7 @@ class CarouselSidebar extends React.Component {
showSavedMessage: false,
showConnectedSection: false,
showFileSection: true,
modalShow: false,
};
componentDidMount = () => {
@ -449,11 +443,11 @@ class CarouselSidebar extends React.Component {
});
};
_handleDelete = () => {
_handleDelete = (res) => {
if (this.props.external || !this.props.isOwner) return;
const message =
"Are you sure you want to delete this? It will be removed from your collections as well";
if (!window.confirm(message)) {
if (!res) {
this.setState({ modalShow: false });
return;
}
const id = this.props.data.id;
@ -745,7 +739,7 @@ class CarouselSidebar extends React.Component {
if (editingAllowed) {
actions.push(
<div key="delete" css={STYLES_ACTION} onClick={this._handleDelete}>
<div key="delete" css={STYLES_ACTION} onClick={() => this.setState({ modalShow: true })}>
<SVG.Trash height="24px" />
<span style={{ marginLeft: 16 }}>Delete</span>
</div>
@ -847,70 +841,81 @@ class CarouselSidebar extends React.Component {
}
return (
<div css={STYLES_SIDEBAR} style={{ display: this.props.display }}>
{this.state.showSavedMessage && (
<div css={STYLES_AUTOSAVE}>
<SVG.Check height="14px" style={{ marginRight: 4 }} />
Changes saved
</div>
<>
{this.state.modalShow && (
<ConfirmationModal
type={"DELETE"}
withValidation={false}
callback={this._handleDelete}
header={`Are you sure you want to delete the file “${this.state.name}”?`}
subHeader={`This file will be deleted from all connected collections and your file library. You cant undo this action.`}
/>
)}
<div key="s-1" css={STYLES_DISMISS_BOX} onClick={this.props.onClose}>
<SVG.Dismiss height="24px" />
</div>
<div key="s-2" style={{ marginBottom: 80 }}>
{elements}
{!this.props.external && <div css={STYLES_ACTIONS}>{actions}</div>}
{privacy}
{uploadCoverImage}
{!this.props.external && (
<>
<div
css={STYLES_SECTION_HEADER}
style={{ cursor: "pointer", marginTop: 48 }}
onClick={() => this._handleToggleAccordion("showConnectedSection")}
>
<span
style={{
marginRight: 8,
transform: this.state.showConnectedSection ? "none" : "rotate(-90deg)",
transition: "100ms ease transform",
}}
>
<SVG.ChevronDown height="24px" display="block" />
</span>
<span>Add to collection</span>
</div>
{this.state.showConnectedSection && (
<div style={{ width: "100%", margin: "24px 0 44px 0" }}>
<SlatePicker
dark
slates={this.props.viewer.slates}
onCreateSlate={this._handleCreateSlate}
selectedColor={Constants.system.white}
files={[this.props.data]}
selected={this.state.selected}
onAdd={this._handleAdd}
/>
</div>
)}
</>
<div css={STYLES_SIDEBAR} style={{ display: this.props.display }}>
{this.state.showSavedMessage && (
<div css={STYLES_AUTOSAVE}>
<SVG.Check height="14px" style={{ marginRight: 4 }} />
Changes saved
</div>
)}
<div key="s-1" css={STYLES_DISMISS_BOX} onClick={this.props.onClose}>
<SVG.Dismiss height="24px" />
</div>
{this.props.data.filename.endsWith(".md") ? (
<>
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
Settings
</div>
<div css={STYLES_OPTIONS_SECTION}>
<div css={STYLES_TEXT}>Dark mode</div>
<Toggle dark active={this.props?.theme?.darkmode} onChange={this._handleDarkMode} />
</div>
</>
) : null}
<div key="s-2" style={{ marginBottom: 80 }}>
{elements}
{!this.props.external && <div css={STYLES_ACTIONS}>{actions}</div>}
{privacy}
{uploadCoverImage}
{!this.props.external && (
<>
<div
css={STYLES_SECTION_HEADER}
style={{ cursor: "pointer", marginTop: 48 }}
onClick={() => this._handleToggleAccordion("showConnectedSection")}
>
<span
style={{
marginRight: 8,
transform: this.state.showConnectedSection ? "none" : "rotate(-90deg)",
transition: "100ms ease transform",
}}
>
<SVG.ChevronDown height="24px" display="block" />
</span>
<span>Add to collection</span>
</div>
{this.state.showConnectedSection && (
<div style={{ width: "100%", margin: "24px 0 44px 0" }}>
<SlatePicker
dark
slates={this.props.viewer.slates}
onCreateSlate={this._handleCreateSlate}
selectedColor={Constants.system.white}
files={[this.props.data]}
selected={this.state.selected}
onAdd={this._handleAdd}
/>
</div>
)}
</>
)}
{this.props.data.filename.endsWith(".md") ? (
<>
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
Settings
</div>
<div css={STYLES_OPTIONS_SECTION}>
<div css={STYLES_TEXT}>Dark mode</div>
<Toggle dark active={this.props?.theme?.darkmode} onChange={this._handleDarkMode} />
</div>
</>
) : null}
</div>
</div>
</div>
</>
);
}
}

View File

@ -0,0 +1,128 @@
import * as React from "react";
import * as Constants from "~/common/constants";
import { css } from "@emotion/react";
import { useState } from "react";
import { ButtonPrimaryFull, ButtonSecondaryFull, ButtonWarningFull } from "~/components/system/components/Buttons.js";
import { Input } from "~/components/system/components/Input.js";
import { Boundary } from "~/components/system/components/fragments/Boundary.js";
const STYLES_TRANSPARENT_BG = css `
background-color: ${Constants.system.bgBlurGrayBlack};
z-index: ${Constants.zindex.modal};
width: 100vw;
height: 100vh;
position: fixed;
left: 0;
top: 0;
`;
const STYLES_MAIN_MODAL = css `
background-color: ${Constants.system.white};
width: 380px;
height: auto;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 8px;
padding: 24px;
text-align: left;
`;
const STYLES_HEADER = css `
color: ${Constants.system.black};
font-size: ${Constants.typescale.lvl1};
font-family: ${Constants.font.semiBold};
`;
const STYLES_SUB_HEADER = css `
color: ${Constants.system.textGray};
font-size: ${Constants.typescale.lvl0};
font-family: ${Constants.font.text};
margin-top: 16px;
`;
const STYLES_INPUT_HEADER = css `
color: ${Constants.system.black};
font-size: ${Constants.typescale.lvlN1};
font-family: ${Constants.font.semiBold};
font-weight: bold;
margin-top: 24px;
margin-bottom: 8px;
`;
export const ConfirmationModal = (props) => {
const [isEnabled, setIsEnabled] = useState(false);
const lang = {
deleteText: 'Delete',
confirmText: 'Confirm',
cancelText: 'Cancel'
}
const _handleChange = (e) => {
if (e.target.value === props.matchValue) {
setIsEnabled(true);
return;
}
setIsEnabled(false);
}
let deleteButton = <ButtonWarningFull disabled={true}>{props.buttonText || lang.deleteText}</ButtonWarningFull>;
if (isEnabled) {
deleteButton = <ButtonWarningFull onClick={() => props.callback(true)}>{props.buttonText || lang.deleteText}</ButtonWarningFull>;
}
let confirmButton = <ButtonPrimaryFull disabled={true}>{props.buttonText || lang.confirmText}</ButtonPrimaryFull>;
if (isEnabled) {
confirmButton = <ButtonPrimaryFull onClick={() => props.callback(true)}>{props.buttonText || lang.confirmText}</ButtonPrimaryFull>;
}
return (
<div css={STYLES_TRANSPARENT_BG}>
<Boundary enabled={true} onOutsideRectEvent={() => props.callback(false)}>
<div css={STYLES_MAIN_MODAL}>
<div css={STYLES_HEADER}>{props.header}</div>
<div css={STYLES_SUB_HEADER}>{props.subHeader}</div>
{props.type === "DELETE" &&
<>
{props.withValidation ? (
<>
<div css={STYLES_INPUT_HEADER}>{props.inputHeader}</div>
<Input placeholder={props.inputPlaceholder} onChange={_handleChange} />
<ButtonSecondaryFull onClick={() => props.callback(false)} style={{margin: '24px 0px 8px'}}>{lang.cancelText}</ButtonSecondaryFull>
{deleteButton}
</>
) : (
<>
<ButtonSecondaryFull onClick={() => props.callback(false)} style={{ margin: '24px 0px 8px' }}>{lang.cancelText}</ButtonSecondaryFull>
<ButtonWarningFull onClick={() => props.callback(true)}>{props.buttonText || lang.deleteText}</ButtonWarningFull>
</>
)}
</>
}
{props.type === "CONFIRM" &&
<>
{props.withValidation ? (
<>
<div css={STYLES_INPUT_HEADER}>{props.inputHeader}</div>
<Input placeholder={props.inputPlaceholder} onChange={_handleChange} />
<ButtonSecondaryFull onClick={() => props.callback(false)} style={{ margin: '24px 0px 8px' }}>{lang.cancelText}</ButtonSecondaryFull>
{confirmButton}
</>
) : (
<>
<ButtonSecondaryFull onClick={() => props.callback(false)} style={{ margin: '24px 0px 8px' }}>{lang.cancelText}</ButtonSecondaryFull>
<ButtonPrimaryFull onClick={() => props.callback(true)}>{props.buttonText || lang.confirmText}</ButtonPrimaryFull>
</>
)}
</>
}
</div>
</Boundary>
</div>
);
};

View File

@ -20,10 +20,10 @@ import { GroupSelectable, Selectable } from "~/components/core/Selectable/";
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
import FilePreviewBubble from "~/components/core/FilePreviewBubble";
import isEqual from "lodash/isEqual";
import { ConfirmationModal } from "~/components/core/ConfirmationModal";
const STYLES_CONTAINER_HOVER = css`
display: flex;
:hover {
color: ${Constants.system.brand};
}
@ -67,7 +67,6 @@ const STYLES_LINK = css`
text-overflow: ellipsis;
white-space: nowrap;
max-width: 320px;
@media (max-width: ${Constants.sizes.tablet}px) {
max-width: 120px;
}
@ -86,7 +85,6 @@ const STYLES_ICON_BOX_HOVER = css`
align-items: center;
padding: 8px;
cursor: pointer;
:hover {
color: ${Constants.system.brand};
}
@ -117,7 +115,6 @@ const STYLES_ACTION_BAR = css`
width: 90vw;
max-width: 878px;
height: 48px;
@media (max-width: ${Constants.sizes.mobile}px) {
display: none;
}
@ -131,7 +128,6 @@ const STYLES_ACTION_BAR_CONTAINER = css`
display: flex;
justify-content: center;
z-index: ${Constants.zindex.header};
@media (max-width: ${Constants.sizes.mobile}px) {
display: none;
}
@ -153,7 +149,6 @@ const STYLES_LEFT = css`
const STYLES_FILES_SELECTED = css`
font-family: ${Constants.font.semiBold};
color: ${Constants.system.white};
@media (max-width: ${Constants.sizes.mobile}px) {
display: none;
}
@ -171,7 +166,6 @@ const STYLES_IMAGE_GRID = css`
grid-column-gap: 20px;
grid-row-gap: 20px;
width: 100%;
@media (max-width: ${Constants.sizes.mobile}px) {
grid-template-columns: repeat(2, 1fr);
}
@ -185,11 +179,9 @@ const STYLES_IMAGE_BOX = css`
justify-content: center;
cursor: pointer;
position: relative;
@media (max-width: ${Constants.sizes.mobile}px) {
margin: 12px auto;
}
:hover {
box-shadow: 0px 0px 0px 1px ${Constants.system.lightBorder} inset,
0 0 40px 0 ${Constants.system.shadow};
@ -223,12 +215,10 @@ const STYLES_TAG = css`
font-family: ${Constants.font.text};
padding: 2px 8px;
margin: 8px 8px 0 0;
span {
line-height: 1.5;
font-size: 14px;
}
&:hover {
background: ${Constants.system.gray30};
}
@ -307,6 +297,7 @@ export default class DataView extends React.Component {
viewLimit: 40,
scrollDebounce: false,
imageSize: 100,
modalShow: false,
};
isShiftDown = false;
@ -474,9 +465,9 @@ export default class DataView extends React.Component {
this.setState({ checked: {} });
};
_handleDelete = (id) => {
const message = `Are you sure you want to delete these files? They will be deleted from your collections as well`;
if (!window.confirm(message)) {
_handleDelete = (res, id) => {
if (!res) {
this.setState({ modalShow: false });
return;
}
@ -495,7 +486,7 @@ export default class DataView extends React.Component {
this.props.onUpdateViewer({ library });
UserBehaviors.deleteFiles(ids);
this.setState({ checked: {} });
this.setState({ checked: {}, modalShow: false });
};
_handleSelect = (index) => {
@ -600,7 +591,6 @@ export default class DataView extends React.Component {
return commonTags;
};
render() {
let numChecked = Object.keys(this.state.checked).length || 0;
// const header = (
@ -637,6 +627,7 @@ export default class DataView extends React.Component {
// </span>
// </div>
// );
const footer = (
<React.Fragment>
{numChecked ? (
@ -684,11 +675,20 @@ export default class DataView extends React.Component {
<ButtonWarning
transparent
style={{ marginLeft: 8, color: Constants.system.white }}
onClick={() => this._handleDelete()}
onClick={() => this.setState({ modalShow: true })}
>
{Strings.pluralize("Delete file", numChecked)}
</ButtonWarning>
)}
{this.state.modalShow && (
<ConfirmationModal
type={"DELETE"}
withValidation={false}
callback={this._handleDelete}
header={`Are you sure you want to delete the selected files?`}
subHeader={`These files will be deleted from all connected collections and your file library. You cant undo this action.`}
/>
)}
<div
css={STYLES_ICON_BOX}
onClick={() => {
@ -969,7 +969,7 @@ export default class DataView extends React.Component {
text: "Delete",
onClick: (e) => {
e.stopPropagation();
this.setState({ menu: null }, () => this._handleDelete(each.id));
this.setState({ menu: null, modalShow: true });
},
},
]}

View File

@ -24,6 +24,7 @@ import {
ButtonWarning,
} from "~/components/system/components/Buttons";
import { GroupSelectable, Selectable } from "~/components/core/Selectable/";
import { ConfirmationModal } from "~/components/core/ConfirmationModal";
//NOTE(martina): sets 200px as the standard width for a 1080px wide layout with 20px margin btwn images.
//If the container is larger or smaller, it scales accordingly by that factor
@ -328,6 +329,7 @@ export class SlateLayout extends React.Component {
tooltip: null,
keyboardTooltip: false,
signInModal: false,
modalShowDeleteFiles: false,
};
componentDidMount = async () => {
@ -887,10 +889,9 @@ export class SlateLayout extends React.Component {
this.setState(state);
};
_handleResetLayout = async () => {
if (
!window.confirm("Are you sure you want to reset your layout to the default column layout?")
) {
_handleResetLayout = async (res) => {
if (!res) {
this.setState({ modalShowResetLayout: false });
return;
}
let prevLayout = this.cloneLayout(this.state.layout);
@ -907,6 +908,7 @@ export class SlateLayout extends React.Component {
],
layout,
zIndexMax: 1,
modalShowResetLayout: false,
});
};
@ -1049,14 +1051,15 @@ export class SlateLayout extends React.Component {
e.dataTransfer.setData("DownloadURL", `${type}:${title}:${url}`);
};
_handleDeleteFiles = async (e, i) => {
const message = `Are you sure you want to delete these files? They will be deleted from your data and collections.`;
if (!window.confirm(message)) {
_handleDeleteModal = () => {
this.setState({ modalShowDeleteFiles: true })
}
_handleDeleteFiles = async (res, i) => {
if (!res) {
this.setState({ modalShowDeleteFiles: false });
return;
}
e.stopPropagation();
e.preventDefault();
let ids = [];
if (i !== undefined) {
ids = [this.state.items[i].id.replace("data-", "")];
@ -1078,7 +1081,7 @@ export class SlateLayout extends React.Component {
}
await UserBehaviors.deleteFiles(ids);
this.setState({ checked: {} });
this.setState({ checked: {}, modalShowDeleteFiles: false });
};
_stopProp = (e) => {
@ -1134,10 +1137,19 @@ export class SlateLayout extends React.Component {
Reset layout
</ButtonDisabled>
) : (
<ButtonSecondary onClick={this._handleResetLayout} style={{ marginRight: 16 }}>
<ButtonSecondary onClick={() => { this.setState({ modalShowResetLayout: true }) }} style={{ marginRight: 16 }}>
Reset layout
</ButtonSecondary>
)}
{this.state.modalShowResetLayout && (
<ConfirmationModal
type={"CONFIRM"}
withValidation={false}
callback={this._handleResetLayout}
header={`Are you sure you want to reset your layout to the default column layout?`}
subHeader={`You cant undo this action.`}
/>
)}
{this.state.prevLayouts.length ? (
<ButtonSecondary style={{ marginRight: 16 }} onClick={this._handleUndo}>
Undo
@ -1453,8 +1465,8 @@ export class SlateLayout extends React.Component {
onMouseLeave={() => this.setState({ tooltip: null })}
onClick={
this.state.items[i].ownerId === this.props.viewer.id
? (e) => {
this._handleDeleteFiles(e, i);
? () => {
this.setState({ modalShowDeleteFiles: true })
}
: () => {}
}
@ -1475,6 +1487,7 @@ export class SlateLayout extends React.Component {
}}
/>
</div>
</div>
</React.Fragment>
) : (
@ -1674,6 +1687,15 @@ export class SlateLayout extends React.Component {
)}
</div>
</div>
{this.state.modalShowDeleteFiles && (
<ConfirmationModal
type={"DELETE"}
withValidation={false}
callback={this._handleDeleteFiles}
header={`Are you sure you want to delete the selected files?`}
subHeader={`These files will be deleted from all connected collections and your file library. You cant undo this action.`}
/>
)}
{numChecked ? (
<div css={STYLES_ACTION_BAR_CONTAINER}>
<div css={STYLES_ACTION_BAR}>
@ -1710,7 +1732,7 @@ export class SlateLayout extends React.Component {
<ButtonWarning
transparent
style={{ marginLeft: 8, color: Constants.system.white }}
onClick={this._handleDeleteFiles}
onClick={this._handleDeleteModal}
>
{Strings.pluralize("Delete file", numChecked)}
</ButtonWarning>

View File

@ -10,6 +10,7 @@ import * as UserBehaviors from "~/common/user-behaviors";
import { RadioGroup } from "~/components/system/components/RadioGroup";
import { css } from "@emotion/react";
import { ConfirmationModal } from "~/components/core/ConfirmationModal";
const SIZE_LIMIT = 1000000;
const DEFAULT_IMAGE =
@ -52,6 +53,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
name: this.props.data.data.name,
tags: this.props.data.data?.tags || [],
suggestions: this.props.viewer?.tags || [],
modalShow: false,
};
componentDidMount = () => {
@ -107,12 +109,9 @@ export default class SidebarSingleSlateSettings extends React.Component {
});
};
_handleDelete = async (e) => {
if (
!window.confirm(
"Are you sure you want to delete this Collection? This action is irreversible."
)
) {
_handleDelete = async (res) => {
if (!res) {
this.setState({ modalShow: false })
return;
}
@ -130,6 +129,8 @@ export default class SidebarSingleSlateSettings extends React.Component {
if (Events.hasError(response)) {
return;
}
this.setState({ modalShow: false })
};
render() {
@ -303,11 +304,20 @@ export default class SidebarSingleSlateSettings extends React.Component {
</System.ButtonPrimary>
<div style={{ marginTop: 16 }}>
<System.ButtonWarning full onClick={this._handleDelete} style={{ overflow: "hidden" }}>
<System.ButtonWarning full onClick={() => this.setState({ modalShow: true })} style={{ overflow: "hidden" }}>
Delete collection
</System.ButtonWarning>
</div>
</div>
{this.state.modalShow && (
<ConfirmationModal
type={"DELETE"}
withValidation={false}
callback={this._handleDelete}
header={`Are you sure you want to delete the collection “${this.state.slatename}”?`}
subHeader={`This collection will be deleted but all your files will remain in your file library. You cant undo this action.`}
/>
)}
</React.Fragment>
);
}

View File

@ -39,6 +39,13 @@ const STYLES_BUTTON_PRIMARY = css`
}
`;
const STYLES_BUTTON_PRIMARY_DISABLED = css`
${STYLES_BUTTON}
cursor: not-allowed;
background-color: ${Constants.system.bgBlue};
color: ${Constants.system.white};
`;
const STYLES_BUTTON_PRIMARY_TRANSPARENT = css`
${STYLES_BUTTON}
cursor: pointer;
@ -71,6 +78,17 @@ export const ButtonPrimary = (props) => {
);
}
if (props.disabled) {
return (
<button
css={STYLES_BUTTON_PRIMARY_DISABLED}
style={{ width: props.full ? "100%" : "auto", ...props.style }}
onClick={props.onClick}
children={props.children}
/>
);
}
return (
<button
css={props.transparent ? STYLES_BUTTON_PRIMARY_TRANSPARENT : STYLES_BUTTON_PRIMARY}
@ -88,12 +106,12 @@ export const ButtonPrimaryFull = (props) => {
const STYLES_BUTTON_SECONDARY = css`
${STYLES_BUTTON}
cursor: pointer;
color: ${Constants.system.brand};
background-color: ${Constants.system.white};
color: ${Constants.system.black};
background-color: ${Constants.system.gray20};
box-shadow: 0 0 0 1px ${Constants.system.bgGray} inset;
:hover {
background-color: #fcfcfc;
background-color: ${Constants.system.gray30};
}
:focus {
@ -251,12 +269,11 @@ export const ButtonDisabledFull = (props) => {
const STYLES_BUTTON_WARNING = css`
${STYLES_BUTTON}
cursor: pointer;
color: ${Constants.system.red};
background-color: ${Constants.system.white};
box-shadow: 0 0 0 1px ${Constants.system.bgGray} inset;
color: ${Constants.system.white};
background-color: ${Constants.system.red};
:hover {
background-color: #fcfcfc;
background-color: #b51111;
}
:focus {
@ -265,6 +282,14 @@ const STYLES_BUTTON_WARNING = css`
}
`;
const STYLES_BUTTON_WARNING_DISABLED = css`
${STYLES_BUTTON}
cursor: not-allowed;
color: ${Constants.system.white};
background-color: ${Constants.system.bgRed};
box-shadow: 0 0 0 1px ${Constants.system.bgGray} inset;
`;
const STYLES_BUTTON_WARNING_TRANSPARENT = css`
${STYLES_BUTTON}
cursor: pointer;
@ -297,6 +322,17 @@ export const ButtonWarning = (props) => {
);
}
if (props.disabled) {
return (
<button
css={STYLES_BUTTON_WARNING_DISABLED}
style={{ width: props.full ? "100%" : "auto", ...props.style }}
onClick={props.onClick}
children={props.children}
/>
);
}
return (
<button
css={props.transparent ? STYLES_BUTTON_WARNING_TRANSPARENT : STYLES_BUTTON_WARNING}
@ -306,3 +342,7 @@ export const ButtonWarning = (props) => {
/>
);
};
export const ButtonWarningFull = (props) => {
return <ButtonWarning full {...props} />;
};

View File

@ -15,6 +15,7 @@ import { SecondaryTabGroup } from "~/components/core/TabGroup";
import ScenePage from "~/components/core/ScenePage";
import ScenePageHeader from "~/components/core/ScenePageHeader";
import Avatar from "~/components/core/Avatar";
import { ConfirmationModal } from "~/components/core/ConfirmationModal";
const STYLES_FILE_HIDDEN = css`
height: 1px;
@ -56,6 +57,7 @@ export default class SceneEditAccount extends React.Component {
savingNameBio: false,
changingFilecoin: false,
tab: 0,
modalShow: false,
};
_handleUpload = async (e) => {
@ -151,13 +153,19 @@ export default class SceneEditAccount extends React.Component {
this.setState({ changingPassword: false, password: "", confirm: "" });
};
_handleDelete = async (e) => {
_handleDelete = async (res) => {
if (!res) {
this.setState({ modalShow: false });
return;
}
this.setState({ deleting: true });
this.setState({ modalShow: false });
await Window.delay(100);
await UserBehaviors.deleteMe({ viewer: this.props.viewer });
this.setState({ deleting: false });
};
_handleChange = (e) => {
@ -333,7 +341,7 @@ export default class SceneEditAccount extends React.Component {
<div style={{ marginTop: 24 }}>
<System.ButtonWarning
onClick={this._handleDelete}
onClick={() => this.setState({ modalShow: true })}
loading={this.state.deleting}
style={{ width: "200px" }}
>
@ -351,6 +359,19 @@ export default class SceneEditAccount extends React.Component {
tabIndex="-1"
css={STYLES_COPY_INPUT}
/>{" "}
{this.state.modalShow && (
<ConfirmationModal
type={"DELETE"}
withValidation={true}
matchValue={this.state.username}
callback={this._handleDelete}
header={`Are you sure you want to delete your account @${this.state.username}?`}
subHeader={`You will lose all your files and collections. You cant undo this action.`}
inputHeader={`Please type your username to confirm`}
inputPlaceholder={`username`}
/>
)}
</ScenePage>
);
}

View File

@ -25,6 +25,8 @@ import APIDocsUpdateSlateV2 from "~/components/api-docs/v2/update-slate.js";
import APIDocsUpdateFileV2 from "~/components/api-docs/v2/update-file.js";
import APIDocsUploadToSlateV2 from "~/components/api-docs/v2/upload.js";
import { ConfirmationModal } from "~/components/core/ConfirmationModal";
const STYLES_API_KEY = css`
height: 40px;
border-radius: 4px;
@ -49,10 +51,15 @@ const STYLES_KEY_CONTAINER = css`
class Key extends React.Component {
_input;
state = { visible: false, copying: false };
state = { visible: false, copying: false, modalShow: false };
_handleDelete = async (id) => {
_handleDelete = async (res, id) => {
if (!res) {
this.setState({ modalShow: false });
return;
}
await this.props.onDelete(id);
this.setState({ modalShow: false });
};
_handleCopy = async () => {
@ -79,13 +86,24 @@ class Key extends React.Component {
onMouseLeave={() => this.setState({ visible: false })}
/>
<SquareButtonGray
onClick={() => this._handleDelete(this.props.data.id)}
onClick={() => this.setState({ modalShow: true })}
style={{
marginLeft: 8,
}}
>
<SVG.Trash height="16px" />
</SquareButtonGray>
{this.state.modalShow && (
<ConfirmationModal
type={"DELETE"}
withValidation={false}
callback={(e) => this._handleDelete(e, this.props.data.id)}
header={`Are you sure you want to revoke this API key?`}
subHeader={`Any services using it will no longer be able to access your Slate account.`}
/>
)}
</div>
);
}
@ -100,6 +118,7 @@ export default class SceneSettingsDeveloper extends React.Component {
docs: "GET",
copying: false,
tab: 0,
modalShow: false,
};
_handleCopy = async () => {
@ -121,16 +140,7 @@ export default class SceneSettingsDeveloper extends React.Component {
};
_handleDelete = async (id) => {
this.setState({ loading: true });
if (
!window.confirm(
"Are you sure you want to revoke this API key? Any services using it will no longer be able to access your Slate account"
)
) {
this.setState({ loading: false });
return;
}
this.setState({ loading: true, modalShow: false });
const response = await Actions.deleteAPIKey({ id });