slate/components/core/CarouselSidebar.js

987 lines
29 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as React from "react";
import * as Constants from "~/common/constants";
import * as Strings from "~/common/strings";
import * as Validations from "~/common/validations";
import * as Actions from "~/common/actions";
import * as System from "~/components/system";
import * as UserBehaviors from "~/common/user-behaviors";
import * as SVG from "~/common/svg";
import * as Events from "~/common/custom-events";
import * as Window from "~/common/window";
import { css, withTheme } from "@emotion/react";
import { RadioGroup } from "~/components/system/components/RadioGroup";
import { LoaderSpinner } from "~/components/system/components/Loaders";
import { SlatePicker } from "~/components/core/SlatePicker";
import { Input } from "~/components/system/components/Input";
import { Toggle } from "~/components/system/components/Toggle";
import { Textarea } from "~/components/system/components/Textarea";
import { Tag } from "~/components/system/components/Tag";
import { Link } from "~/components/core/Link";
import LinkTag from "~/components/core/Link/LinkTag";
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";
const DEFAULT_DATA =
"https://slate.textile.io/ipfs/bafkreid6bnjxz6fq2deuhehtxkcesjnjsa2itcdgyn754fddc7u72oks2m";
const DEFAULT_DOCUMENT =
"https://slate.textile.io/ipfs/bafkreiecdiepww52i5q3luvp4ki2n34o6z3qkjmbk7pfhx4q654a4wxeam";
const DEFAULT_VIDEO =
"https://slate.textile.io/ipfs/bafkreibesdtut4j5arclrxd2hmkfrv4js4cile7ajnndn3dcn5va6wzoaa";
const DEFAULT_AUDIO =
"https://slate.textile.io/ipfs/bafkreig2hijckpamesp4nawrhd6vlfvrtzt7yau5wad4mzpm3kie5omv4e";
const STYLES_NO_VISIBLE_SCROLL = css`
overflow-y: scroll;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar;
::-webkit-scrollbar {
width: 0px;
display: none;
}
::-webkit-scrollbar-track {
background: ${Constants.semantic.bgLight};
}
::-webkit-scrollbar-thumb {
background: ${Constants.system.grayLight2};
}
`;
const STYLES_BODY = css`
font-size: 16px;
line-height: 1.225;
overflow-wrap: break-word;
white-space: pre-wrap;
margin-bottom: 32px;
`;
const STYLES_SIDEBAR_INPUT_LABEL = css`
font-size: 16px;
font-family: ${Constants.font.semiBold};
color: ${Constants.system.grayLight2};
margin-bottom: 8px;
`;
const STYLES_SIDEBAR = css`
width: 420px;
padding: 48px 24px 0px 24px;
flex-shrink: 0;
height: 100vh;
display: flex;
flex-direction: column;
align-items: flex-start;
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;
}
`;
const STYLES_DISMISS_BOX = css`
position: absolute;
top: 16px;
right: 16px;
color: ${Constants.system.grayLight2};
cursor: pointer;
:hover {
color: ${Constants.system.white};
}
`;
const STYLES_HEADING = css`
font-family: ${Constants.font.semiBold};
font-size: 20px;
font-weight: 400;
overflow-wrap: break-word;
white-space: pre-wrap;
margin-bottom: 32px;
`;
const STYLES_META = css`
text-align: start;
padding: 14px 0px 8px 0px;
overflow-wrap: break-word;
`;
const STYLES_META_TITLE = css`
font-family: ${Constants.font.semiBold};
color: ${Constants.system.white};
font-size: ${Constants.typescale.lvl2};
text-decoration: none;
word-break: break-all;
overflow-wrap: anywhere;
:hover {
color: ${Constants.system.blue};
}
`;
const STYLES_TAG = css`
margin-right: 24px;
padding: 0px 2px;
border-radius: 2px;
border: 1px solid ${Constants.system.grayLight2};
`;
const STYLES_OPTIONS_SECTION = css`
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 16px 0 16px 0;
`;
const STYLES_META_DETAILS = css`
color: ${Constants.system.grayLight2};
text-transform: uppercase;
margin: 24px 0px;
font-family: ${Constants.font.medium};
font-size: 0.9rem;
`;
const STYLES_SIDEBAR_SECTION = css`
flex-shrink: 0;
width: 100%;
margin-bottom: 16px;
`;
const STYLES_ACTIONS = css`
color: ${Constants.system.white};
border: 1px solid #3c3c3c;
border-radius: 4px;
background-color: transparent;
margin-bottom: 48px;
margin-top: 36px;
`;
const STYLES_ACTION = css`
cursor: pointer;
padding: 12px 16px;
border-bottom: 1px solid #3c3c3c;
display: flex;
align-items: center;
:hover {
color: ${Constants.system.blue};
}
:last-child {
border: none;
}
`;
const STYLES_SECTION_HEADER = css`
font-family: ${Constants.font.semiBold};
font-size: 1.1rem;
margin-top: 24px;
display: flex;
align-items: center;
`;
const STYLES_HIDDEN = css`
position: absolute;
opacity: 0;
pointer-events: none;
`;
const STYLES_IMAGE_BOX = css`
max-width: 100%;
max-height: 368px;
display: flex;
align-items: center;
justify-content: center;
background-color: ${Constants.system.black};
overflow: hidden;
${"" /* box-shadow: 0 0 0 1px ${Constants.semantic.borderGrayLight} inset; */}
border-radius: 4px;
`;
const STYLES_FILE_HIDDEN = css`
height: 1px;
width: 1px;
opacity: 0;
visibility: hidden;
position: fixed;
top: -1px;
left: -1px;
`;
const STYLES_TEXT = css`
color: ${Constants.system.grayLight2};
line-height: 1.5;
`;
const STYLES_INPUT = {
marginBottom: 8,
backgroundColor: "transparent",
boxShadow: "0 0 0 1px #3c3c3c inset",
color: Constants.system.white,
height: 48,
};
const STYLES_AUTOSAVE = css`
font-size: 12px;
line-height: 1.225;
display: flex;
justify-content: baseline;
color: ${Constants.system.yellow};
opacity: 0;
${"" /* margin: 26px 24px; */}
position: absolute;
top: 24px;
left: 16px;
@keyframes slate-animations-autosave {
0% {
opacity: 0;
transform: translateX(0);
}
10% {
opacity: 1;
transform: translateX(12px);
}
90% {
opacity: 1;
transform: translateX(12px);
}
100% {
opacity: 0;
}
}
animation: slate-animations-autosave 4000ms ease;
`;
const STYLES_SPINNER = css`
width: 24px;
height: 24px;
`;
export const FileTypeDefaultPreview = (props) => {
if (props.type) {
if (Validations.isVideoType(type)) {
return DEFAULT_VIDEO;
} else if (Validations.isAudioType(type)) {
return DEFAULT_AUDIO;
} else if (Validations.isPdfType(type)) {
return DEFAULT_DOCUMENT;
} else if (Validations.isEpubType(type)) {
return DEFAULT_BOOK;
}
}
return DEFAULT_DATA;
};
class CarouselSidebar extends React.Component {
state = {
name: this.props.file.data.name || this.props.file.filename || "",
body: this.props.file.data.body || "",
source: this.props.file.data.source || "",
author: this.props.file.data.author || "",
tags: this.props.file.data.tags || [],
suggestions: this.props.viewer?.tags || [],
selected: {},
isUploading: false,
isDownloading: false,
showSavedMessage: false,
showConnectedSection: false,
showFileSection: true,
modalShow: false,
};
componentDidMount = () => {
const editingAllowed = !this.props.external && this.props.isOwner && !this.props.isRepost;
if (editingAllowed) {
this.debounceInstance = Window.debounce(() => this._handleSave(), 3000);
this.calculateSelected();
}
};
componentDidUpdate = (prevProps, prevState) => {
if (!isEqual(prevState.tags, this.state.tags)) {
this.updateSuggestions();
}
};
updateSuggestions = () => {
let newSuggestions = new Set([...this.state.suggestions, ...this.state.tags]);
this.setState({ suggestions: Array.from(newSuggestions) });
};
calculateSelected = () => {
if (!this.props.viewer) {
this.setState({ selected: {} });
return;
}
let selected = {};
const id = this.props.file.id;
for (let slate of this.props.viewer.slates) {
if (slate.objects.some((obj) => obj.id === id)) {
selected[slate.id] = true;
}
}
this.setState({ selected });
};
_handleToggleAccordion = (tab) => {
this.setState({ [tab]: !this.state[tab] });
};
_handleDarkMode = async (e) => {
Events.dispatchCustomEvent({
name: "set-slate-theme",
detail: { darkmode: e.target.value },
});
};
_handleChange = (e) => {
if (this.props.external || !this.props.isOwner) return;
this.debounceInstance();
this.setState(
{
[e.target.name]: e.target.value,
showSavedMessage: false,
},
() => {
if (e.target.name === "Tags") {
this.updateSuggestions();
}
}
);
};
_handleCapitalization(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
_handleSave = async () => {
if (this.props.external || !this.props.isOwner) return;
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { tags: this.state.suggestions } });
const response = await Actions.updateFile({
id: this.props.file.id,
data: {
name: this.state.name,
body: this.state.body,
source: this.state.source,
author: this.state.author,
tags: this.state.tags,
},
});
Events.hasError(response);
this.setState({ showSavedMessage: true });
};
_handleSaveCopy = async (data) => {
if (!this.props.viewer) {
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
return;
}
this.setState({ loading: "savingCopy" }, async () => {
let response = await UserBehaviors.saveCopy({ files: [data] });
Events.hasError(response);
this.setState({ loading: false });
});
};
_handleUpload = async (e) => {
if (this.props.external || !this.props.isOwner || !this.props.viewer) return;
e.persist();
this.setState({ isUploading: true });
let previousCoverId = this.props.file.data.coverImage?.id;
if (!e || !e.target) {
this.setState({ isUploading: false });
return;
}
let file = await UserBehaviors.uploadImage(e.target.files[0], this.props.resources, true);
if (!file) {
this.setState({ isUploading: false });
return;
}
let coverImage = file;
//TODO(martina): create an endpoint specifically for cover images instead of this, which will delete original cover image etc
let updateReponse = await Actions.updateFile({
id: this.props.file.id,
data: {
coverImage,
},
});
if (previousCoverId) {
if (!this.props.viewer.library.some((obj) => obj.id === previousCoverId)) {
await UserBehaviors.deleteFiles(previousCoverId, true);
}
}
Events.hasError(updateReponse);
this.setState({ isUploading: false });
};
_handleDownload = () => {
if (!this.props.viewer) {
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
return;
}
this.setState({ isDownloading: true }, async () => {
const response = await UserBehaviors.download(this.props.file);
this.setState({ isDownloading: false });
Events.hasError(response);
});
};
_handleCreateSlate = async () => {
if (this.props.external) return;
this.props.onClose();
this.props.onAction({
type: "SIDEBAR",
value: "SIDEBAR_CREATE_SLATE",
data: { files: [this.props.file] },
});
};
_handleDelete = (res) => {
if (this.props.external || !this.props.isOwner || !this.props.viewer) return;
if (!res) {
this.setState({ modalShow: false });
return;
}
const id = this.props.file.id;
let updatedLibrary = this.props.viewer.library.filter((obj) => obj.id !== id);
if (this.props.carouselType === "SLATE") {
const slateId = this.props.data.id;
let slates = this.props.viewer.slates;
for (let slate of slates) {
if (slate.id === slateId) {
slate.objects = slate.objects.filter((obj) => obj.id !== id);
break;
}
}
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { library: updatedLibrary, slates } });
} else {
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { library: updatedLibrary } });
}
UserBehaviors.deleteFiles(id);
this.props.onNext();
};
_handleAdd = async (slate) => {
if (this.state.selected[slate.id]) {
UserBehaviors.removeFromSlate({ slate, ids: [this.props.file.id] });
} else {
UserBehaviors.saveCopy({
slate,
files: [this.props.file],
});
}
this.setState({
selected: {
...this.state.selected,
[slate.id]: !this.state.selected[slate.id],
},
});
};
_handleRemove = async () => {
if (
!this.props.carouselType === "SLATE" ||
this.props.external ||
!this.props.isOwner ||
!this.props.viewer
) {
return;
}
const id = this.props.file.id;
const slateId = this.props.data.id;
let slates = this.props.viewer.slates;
for (let slate of slates) {
if (slate.id === slateId) {
slate.objects = slate.objects.filter((obj) => obj.id !== id);
break;
}
}
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { slates } });
UserBehaviors.removeFromSlate({ slate: this.props.data, ids: [this.props.file.id] });
this.props.onNext();
};
_handleToggleVisibility = async (e) => {
if (this.props.external || !this.props.isOwner || !this.props.viewer) return;
const isPublic = this.props.file.isPublic;
const slateIsPublic = this.props.data?.isPublic;
let selected = cloneDeep(this.state.selected);
const slateIds = Object.entries(this.state.selected)
.filter((entry) => entry[1])
.map((entry) => entry[0]);
const publicSlateIds = [];
const publicSlateNames = [];
for (let slate of this.props.viewer.slates) {
if (slate.isPublic && slateIds.includes(slate.id)) {
publicSlateNames.push(slate.data.name);
publicSlateIds.push(slate.id);
selected[slate.id] = false;
}
}
if (publicSlateNames.length) {
const slateNames = publicSlateNames.join(", ");
const message = `Making this file link-viewing only will remove it from the following public collections: ${slateNames}. Do you wish to continue?`;
if (!window.confirm(message)) {
return;
}
}
if (this.props.carouselType === "SLATE" && slateIsPublic) {
const slateId = this.props.data.id;
let slates = cloneDeep(this.props.viewer.slates);
for (let slate of slates) {
if (slate.id === slateId) {
slate.objects = slate.objects.filter((obj) => obj.id !== this.props.file.id);
break;
}
}
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { slates } });
}
let response = await Actions.toggleFilePrivacy({ ...this.props.file, isPublic: !isPublic });
Events.hasError(response);
if (isPublic) {
this.setState({ selected });
}
};
render() {
const isPublic = this.props.file.isPublic;
const file = this.props.file;
const { coverImage, type, size } = file.data;
const editingAllowed = this.props.isOwner && !this.props.isRepost && !this.props.external;
const isUnityGame = Validations.isUnityType(type);
const isLink = file.isLink;
const showPreviewImageSection =
editingAllowed && type && !isLink && !Validations.isPreviewableImage(type);
const elements = [];
if (editingAllowed && !isUnityGame) {
elements.push(
<div key="sidebar-media-object-info" style={{ marginTop: 8 }}>
{isLink && (
<LinkTag
fillWidth={true}
url={file.url}
containerStyle={{
backgroundColor: Constants.system.grayDark4,
padding: "8px 16px",
borderRadius: 8,
marginBottom: 16,
}}
/>
)}
<Input
full
value={this.state.name}
name="name"
onChange={this._handleChange}
autoHighlight
id={`sidebar-label-name`}
style={{
fontSize: Constants.typescale.lvl1,
...STYLES_INPUT,
}}
textStyle={{ color: Constants.system.white }}
/>
<Textarea
name="body"
placeholder="Add notes or a description..."
value={this.state.body}
onChange={this._handleChange}
style={STYLES_INPUT}
/>
<Input
full
value={this.state.source}
name="source"
placeholder="Source"
onChange={this._handleChange}
id={`sidebar-label-source`}
style={STYLES_INPUT}
textStyle={{ color: Constants.system.white }}
/>
<Input
full
value={this.state.author}
name="author"
placeholder="Author"
onChange={this._handleChange}
id={`sidebar-label-author`}
style={{ ...STYLES_INPUT, marginBottom: 12 }}
textStyle={{ color: Constants.system.white }}
/>
<div css={STYLES_OPTIONS_SECTION}>
<Tag
type="dark"
tags={this.state.tags}
suggestions={this.state.suggestions}
style={STYLES_INPUT}
textStyle={{ color: Constants.system.white }}
// dropdownStyles={{ top: "50px" }}
onChange={this._handleChange}
/>
</div>
</div>
);
} else {
const hasName = !Strings.isEmpty(file.data.name || file.filename);
const hasBody = !Strings.isEmpty(file.data.body);
const hasSource = !Strings.isEmpty(file.data.source);
const hasAuthor = !Strings.isEmpty(file.data.author);
if (hasName) {
elements.push(
<div key="sidebar-media-info-name" css={STYLES_SIDEBAR_SECTION}>
<div css={STYLES_HEADING}>
<ProcessedText dark text={file.data.name || file.filename} />
</div>
</div>
);
}
if (isLink) {
elements.push(
<LinkTag
url={file.url}
fillWidth={true}
containerStyle={{
backgroundColor: Constants.system.grayDark4,
padding: "8px 16px",
borderRadius: 8,
marginBottom: 24,
}}
/>
);
}
if (hasBody) {
elements.push(
<div key="sidebar-media-info-body" css={STYLES_SIDEBAR_SECTION}>
<div css={STYLES_BODY}>
<ProcessedText dark text={file.data.body} />
</div>
</div>
);
}
if (hasSource) {
elements.push(
<div key="sidebar-media-info-source" css={STYLES_SIDEBAR_SECTION}>
<div css={STYLES_SIDEBAR_INPUT_LABEL} style={{ position: "relative" }}>
Source:
</div>
<p css={STYLES_BODY} style={{ color: Constants.system.grayLight2 }}>
<ProcessedText dark text={file.data.source} />
</p>
</div>
);
}
if (hasAuthor) {
elements.push(
<div key="sidebar-media-info-author" css={STYLES_SIDEBAR_SECTION}>
<div css={STYLES_SIDEBAR_INPUT_LABEL} style={{ position: "relative" }}>
Author:
</div>
<p css={STYLES_BODY} style={{ color: Constants.system.grayLight2 }}>
<ProcessedText dark text={file.data.author} />
</p>
</div>
);
}
}
let actions = [];
{
this.props.carouselType === "ACTIVITY"
? actions.push(
<div key="go-to-slate" style={{ borderBottom: "1px solid #3c3c3c" }}>
<Link href={`/$/slate/${file.slateId}`} onAction={this.props.onAction}>
<div
key="go-to-slate"
css={STYLES_ACTION}
// onClick={() =>
// this.props.onAction({
// type: "NAVIGATE",
// value: "NAV_SLATE",
// data: file.slate,
// })
// }
>
<SVG.Slate height="24px" />
<span style={{ marginLeft: 16 }}>Go to collection</span>
</div>
</Link>
</div>
)
: null;
}
if (!isLink) {
actions.push(
<div key="download" css={STYLES_ACTION} onClick={this._handleDownload}>
{this.state.isDownloading ? (
<>
<LoaderSpinner css={STYLES_SPINNER} />
<span style={{ marginLeft: 16 }}>Downloading</span>
</>
) : (
<>
<SVG.Download height="24px" />
<span style={{ marginLeft: 16 }}>Download</span>
</>
)}
</div>
);
}
if (!this.props.isOwner || this.props.isRepost) {
actions.push(
<div key="save-copy" css={STYLES_ACTION} onClick={() => this._handleSaveCopy(file)}>
<SVG.Save height="24px" />
<span style={{ marginLeft: 16 }}>
{this.state.loading === "savingCopy" ? (
<LoaderSpinner style={{ height: 16, width: 16 }} />
) : (
<span>Save</span>
)}
</span>
</div>
);
}
if (this.props.carouselType === "SLATE" && !this.props.external && this.props.isOwner) {
actions.push(
<div key="remove" css={STYLES_ACTION} onClick={this._handleRemove}>
<SVG.DismissCircle height="24px" />
<span style={{ marginLeft: 16 }}>Remove from collection</span>
</div>
);
}
if (editingAllowed) {
actions.push(
<div key="delete" css={STYLES_ACTION} onClick={() => this.setState({ modalShow: true })}>
<SVG.Trash height="24px" />
<span style={{ marginLeft: 16 }}>Delete</span>
</div>
);
}
let uploadCoverImage;
if (showPreviewImageSection) {
uploadCoverImage = (
<div style={{ marginBottom: 48 }}>
<System.P1 css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
Preview image
</System.P1>
{coverImage ? (
<>
<System.P1 css={STYLES_TEXT}>This is the preview image of your file.</System.P1>
<div css={STYLES_IMAGE_BOX} style={{ marginTop: 24 }}>
<img
src={Strings.getURLfromCID(coverImage.cid)}
alt=""
style={{ maxWidth: "368px", maxHeight: "368px" }}
/>
</div>
</>
) : (
<System.P1 css={STYLES_TEXT}>Add a preview image for your file.</System.P1>
)}
<div style={{ marginTop: 16 }}>
<input css={STYLES_FILE_HIDDEN} type="file" id="file" onChange={this._handleUpload} />
<System.ButtonPrimary full type="label" htmlFor="file" loading={this.state.isUploading}>
Upload preview image
</System.ButtonPrimary>
</div>
</div>
);
}
let privacy;
if (editingAllowed) {
privacy = (
<div>
<System.P1 css={STYLES_SECTION_HEADER} style={{ marginBottom: 12 }}>
Visibility
</System.P1>
<System.P1
css={STYLES_TEXT}
style={{
marginTop: 12,
}}
>
{isPublic
? `This ${
isLink ? "link" : "file"
} is currently visible to everyone and searchable within Slate. It may appear in activity feeds and explore.`
: isLink
? "This link is only visible to you"
: "This file is only visible to those with the link."}
</System.P1>
<RadioGroup
name="isPublic"
options={[
{
value: true,
label: (
<div style={{ display: "flex", alignItems: "center" }}>
<SVG.Globe height="16px" style={{ marginRight: 8 }} />
Public
</div>
),
},
{
value: false,
label: (
<div style={{ display: "flex", alignItems: "center" }}>
<SVG.SecurityLock height="16px" style={{ marginRight: 8 }} />
Link-viewing only
</div>
),
},
]}
dark={true}
style={{ marginTop: 12 }}
labelStyle={{ fontFamily: Constants.font.medium }}
selected={isPublic}
onChange={this._handleToggleVisibility}
/>
{!isPublic && !isLink && (
<Input
full
value={isLink ? file.data.link.url : Strings.getURLfromCID(file.cid)}
name="copyLink"
readOnly
copyable
style={{
fontSize: Constants.typescale.lvl1,
...STYLES_INPUT,
marginTop: 12,
}}
textStyle={{ color: Constants.system.white }}
/>
)}
</div>
);
}
return (
<>
{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 css={STYLES_SIDEBAR} style={{ display: this.props.display, paddingBottom: 96 }}>
{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>
{elements}
<div css={STYLES_ACTIONS}>{actions}</div>
{privacy}
{uploadCoverImage}
{!this.props.external && this.props.viewer && (
<>
<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.file]}
selected={this.state.selected}
onAdd={this._handleAdd}
/>
</div>
)}
</>
)}
{this.props.file.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>
</>
);
}
}
export default withTheme(CarouselSidebar);
{
/* <>
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
Visibility
</div>
<div css={STYLES_OPTIONS_SECTION}>
<div css={STYLES_TEXT}>{isVisible ? "Everyone" : "Link only"}</div>
<Toggle dark active={isVisible} onChange={this._handleToggleVisibility} />
</div>
<div style={{ color: Constants.system.grayLight2, marginTop: 8 }}>
{isVisible
? "This file is currently visible to everyone and searchable within Slate. It may appear in activity feeds and explore."
: "This file is currently not visible to others unless they have the link."}
</div>
</> */
}