mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-22 08:31:41 +03:00
Merge pull request #742 from filecoin-project/@jason-leyser/confirmation-modals
Confirmation popup components
This commit is contained in:
commit
a01c262f6b
@ -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();
|
||||
|
@ -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}
|
||||
|
@ -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 can’t 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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -932,4 +937,4 @@ export default withTheme(CarouselSidebar);
|
||||
: "This file is currently not visible to others unless they have the link."}
|
||||
</div>
|
||||
</> */
|
||||
}
|
||||
}
|
128
components/core/ConfirmationModal.js
Normal file
128
components/core/ConfirmationModal.js
Normal 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>
|
||||
);
|
||||
};
|
@ -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 can’t 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 });
|
||||
},
|
||||
},
|
||||
]}
|
||||
|
@ -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 can’t 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 can’t 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>
|
||||
|
@ -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 can’t undo this action.`}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -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} />;
|
||||
};
|
||||
|
@ -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 can’t undo this action.`}
|
||||
inputHeader={`Please type your username to confirm`}
|
||||
inputPlaceholder={`username`}
|
||||
/>
|
||||
)}
|
||||
</ScenePage>
|
||||
);
|
||||
}
|
||||
|
@ -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 });
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user