mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-24 17:44:50 +03:00
media object: adds PDF support, validation check to restrict certain uploads
This commit is contained in:
parent
e85bd9b16a
commit
5d4f58eddc
@ -26,3 +26,15 @@ export const password = (text) => {
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export const isFileTypeAllowed = (type = "") => {
|
||||
if (type.startsWith("application/pdf")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.startsWith("image/")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
@ -3,6 +3,7 @@ import * as NavigationData from "~/common/navigation-data";
|
||||
import * as Actions from "~/common/actions";
|
||||
import * as State from "~/common/state";
|
||||
import * as Credentials from "~/common/credentials";
|
||||
import * as Validations from "~/common/validations";
|
||||
|
||||
// NOTE(jim):
|
||||
// Scenes each have an ID and can be navigated to with _handleAction
|
||||
@ -11,7 +12,6 @@ import SceneEditAccount from "~/scenes/SceneEditAccount";
|
||||
import SceneFile from "~/scenes/SceneFile";
|
||||
import SceneFilesFolder from "~/scenes/SceneFilesFolder";
|
||||
import SceneHome from "~/scenes/SceneHome";
|
||||
import SceneMiners from "~/scenes/SceneMiners";
|
||||
import SceneSettings from "~/scenes/SceneSettings";
|
||||
import SceneWallet from "~/scenes/SceneWallet";
|
||||
import SceneSlates from "~/scenes/SceneSlates";
|
||||
@ -26,7 +26,6 @@ import SidebarCreateSlate from "~/components/sidebars/SidebarCreateSlate";
|
||||
import SidebarCreateWalletAddress from "~/components/sidebars/SidebarCreateWalletAddress";
|
||||
import SidebarWalletSendFunds from "~/components/sidebars/SidebarWalletSendFunds";
|
||||
import SidebarFileStorageDeal from "~/components/sidebars/SidebarFileStorageDeal";
|
||||
import SidebarCreatePaymentChannel from "~/components/sidebars/SidebarCreatePaymentChannel";
|
||||
import SidebarAddFileToBucket from "~/components/sidebars/SidebarAddFileToBucket";
|
||||
|
||||
// NOTE(jim):
|
||||
@ -68,7 +67,7 @@ export default class ApplicationPage extends React.Component {
|
||||
this.setState({ fileLoading: true });
|
||||
|
||||
let data = new FormData();
|
||||
data.append("image", file);
|
||||
data.append("data", file);
|
||||
|
||||
const options = {
|
||||
method: "POST",
|
||||
@ -156,7 +155,9 @@ export default class ApplicationPage extends React.Component {
|
||||
if (e.dataTransfer.items[i].kind === "file") {
|
||||
var file = e.dataTransfer.items[i].getAsFile();
|
||||
|
||||
if (Validations.isFileTypeAllowed(file.type)) {
|
||||
await this._handleSetFile({ file, slate });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
69
components/core/MediaObject.js
Normal file
69
components/core/MediaObject.js
Normal file
@ -0,0 +1,69 @@
|
||||
import * as React from "react";
|
||||
import * as Constants from "~/common/constants";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
const STYLES_FAILURE = css`
|
||||
background-color: ${Constants.system.pitchBlack};
|
||||
color: ${Constants.system.white};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 88px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
min-height: 10%;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const STYLES_OBJECT = css`
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
min-height: 10%;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const STYLES_ASSET = css`
|
||||
background-color: ${Constants.system.pitchBlack};
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 10%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const STYLES_IMAGE = css`
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
`;
|
||||
|
||||
export default class MediaObject extends React.Component {
|
||||
render() {
|
||||
const name = `${this.props.data.name}`;
|
||||
const url = this.props.data.url ? this.props.data.url : `https://hub.textile.io${this.props.data.ipfs}`;
|
||||
|
||||
let mediaElement = <div css={STYLES_FAILURE}>No Preview</div>;
|
||||
|
||||
if (this.props.data.type.startsWith("application/pdf")) {
|
||||
mediaElement = <object css={STYLES_OBJECT} data={url} type={this.props.data.type} />;
|
||||
}
|
||||
|
||||
if (this.props.data.type.startsWith("image/")) {
|
||||
mediaElement = (
|
||||
<div css={STYLES_ASSET}>
|
||||
<img css={STYLES_IMAGE} src={url} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return mediaElement;
|
||||
}
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as SVG from "~/components/system/svg";
|
||||
import * as System from "~/components/system";
|
||||
import * as Validations from "~/common/validations";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
@ -49,36 +48,31 @@ export default class SidebarAddFileToBucket extends React.Component {
|
||||
let file = e.target.files[0];
|
||||
|
||||
if (!file) {
|
||||
alert("Something went wrong");
|
||||
alert("TODO: Something went wrong");
|
||||
return;
|
||||
}
|
||||
|
||||
const isAllowed = Validations.isFileTypeAllowed(file.type);
|
||||
if (!isAllowed) {
|
||||
alert("TODO: File type is not allowed, yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
await this.props.onSetFile({
|
||||
file,
|
||||
slate:
|
||||
this.props.data && this.props.data.slateId
|
||||
? { id: this.props.data.slateId }
|
||||
: null,
|
||||
slate: this.props.data && this.props.data.slateId ? { id: this.props.data.slateId } : null,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<System.P style={{ fontFamily: Constants.font.semiBold }}>
|
||||
Upload a file to Slate
|
||||
</System.P>
|
||||
<input
|
||||
css={STYLES_FILE_HIDDEN}
|
||||
type="file"
|
||||
id="file"
|
||||
onChange={this._handleUpload}
|
||||
/>
|
||||
<System.P style={{ fontFamily: Constants.font.semiBold }}>Upload a file to Slate</System.P>
|
||||
<input css={STYLES_FILE_HIDDEN} type="file" id="file" onChange={this._handleUpload} />
|
||||
|
||||
{this.props.data && this.props.data.decorator === "SLATE" ? (
|
||||
<System.P style={{ marginTop: 24 }}>
|
||||
This will add an image to your Slate named{" "}
|
||||
<strong>{this.props.data.slatename}</strong>.
|
||||
This will add an image to your Slate named <strong>{this.props.data.slatename}</strong>.
|
||||
</System.P>
|
||||
) : null}
|
||||
|
||||
@ -86,16 +80,12 @@ export default class SidebarAddFileToBucket extends React.Component {
|
||||
type="label"
|
||||
htmlFor="file"
|
||||
style={{ marginTop: 24 }}
|
||||
loading={this.props.fileLoading}
|
||||
>
|
||||
loading={this.props.fileLoading}>
|
||||
Add file
|
||||
</System.ButtonPrimaryFull>
|
||||
|
||||
{!this.props.fileLoading ? (
|
||||
<System.ButtonSecondaryFull
|
||||
style={{ marginTop: 16 }}
|
||||
onClick={this.props.onCancel}
|
||||
>
|
||||
<System.ButtonSecondaryFull style={{ marginTop: 16 }} onClick={this.props.onCancel}>
|
||||
Cancel
|
||||
</System.ButtonSecondaryFull>
|
||||
) : null}
|
||||
|
@ -19,32 +19,30 @@ export const formMultipart = (req, res, { user }) =>
|
||||
});
|
||||
}
|
||||
|
||||
if (!files.image) {
|
||||
console.log(files);
|
||||
|
||||
if (!files.data) {
|
||||
return reject({
|
||||
decorator: "SERVER_UPLOAD_NOT_IMAGE_TYPE",
|
||||
decorator: "SERVER_UPLOAD_ERROR",
|
||||
error: true,
|
||||
message: files,
|
||||
});
|
||||
}
|
||||
|
||||
const path = files.image._writeStream.path;
|
||||
const path = files.data._writeStream.path;
|
||||
const localPath = `./${path}`;
|
||||
const data = LibraryManager.createLocalDataIncomplete(files.image);
|
||||
const data = LibraryManager.createLocalDataIncomplete(files.data);
|
||||
|
||||
const {
|
||||
buckets,
|
||||
bucketKey,
|
||||
bucketName,
|
||||
} = await Utilities.getBucketAPIFromUserToken(user.data.tokens.api);
|
||||
const { buckets, bucketKey, bucketName } = await Utilities.getBucketAPIFromUserToken(user.data.tokens.api);
|
||||
|
||||
let readFile;
|
||||
let push;
|
||||
// TODO(jim): Send this file to buckets.
|
||||
try {
|
||||
// NOTE(jim): Push pathPath to your bucket.
|
||||
readFile = await FS.readFileSync(path).buffer;
|
||||
push = await buckets.pushPath(bucketKey, data.name, readFile);
|
||||
} catch (e) {
|
||||
await FS.unlinkSync(localPath);
|
||||
|
||||
return reject({
|
||||
decorator: "SERVER_BUCKETS_PUSH_ISSUE",
|
||||
error: true,
|
||||
|
@ -1,8 +1,6 @@
|
||||
import * as MW from "~/node_common/middleware";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Powergate from "~/node_common/powergate";
|
||||
|
||||
const initCORS = MW.init(MW.CORS);
|
||||
const initAuth = MW.init(MW.RequireCookieAuthentication);
|
||||
@ -13,9 +11,7 @@ export default async (req, res) => {
|
||||
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
if (!id) {
|
||||
return res
|
||||
.status(403)
|
||||
.json({ decorator: "SERVER_ADD_TO_SLATE_USER_NOT_FOUND", error: true });
|
||||
return res.status(403).json({ decorator: "SERVER_ADD_TO_SLATE_USER_NOT_FOUND", error: true });
|
||||
}
|
||||
|
||||
const user = await Data.getUserById({
|
||||
@ -62,6 +58,7 @@ export default async (req, res) => {
|
||||
id: req.body.data.id,
|
||||
ownerId: user.id,
|
||||
name: req.body.data.name,
|
||||
type: req.body.data.type,
|
||||
url: `https://hub.textile.io${req.body.data.ipfs}`,
|
||||
},
|
||||
...slate.data.objects,
|
||||
@ -83,7 +80,5 @@ export default async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
return res
|
||||
.status(200)
|
||||
.json({ decorator: "SERVER_SLATE_ADD_TO_SLATE", slate });
|
||||
return res.status(200).json({ decorator: "SERVER_SLATE_ADD_TO_SLATE", slate });
|
||||
};
|
||||
|
@ -42,7 +42,13 @@ export default class SceneEditAccount extends React.Component {
|
||||
let file = e.target.files[0];
|
||||
|
||||
if (!file) {
|
||||
alert("Something went wrong");
|
||||
alert("TODO: Something went wrong");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.type.startsWith("image/")) {
|
||||
alert("TODO: Error message for not an image.");
|
||||
return;
|
||||
}
|
||||
|
||||
let data = new FormData();
|
||||
@ -144,25 +150,15 @@ export default class SceneEditAccount extends React.Component {
|
||||
description="This image will appear in various lists."
|
||||
/>
|
||||
|
||||
<Avatar
|
||||
style={{ marginTop: 24 }}
|
||||
size={256}
|
||||
url={this.props.viewer.data.photo}
|
||||
/>
|
||||
<Avatar style={{ marginTop: 24 }} size={256} url={this.props.viewer.data.photo} />
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<input
|
||||
css={STYLES_FILE_HIDDEN}
|
||||
type="file"
|
||||
id="file"
|
||||
onChange={this._handleUpload}
|
||||
/>
|
||||
<input css={STYLES_FILE_HIDDEN} type="file" id="file" onChange={this._handleUpload} />
|
||||
<System.ButtonPrimary
|
||||
style={{ margin: "0 16px 16px 0" }}
|
||||
type="label"
|
||||
htmlFor="file"
|
||||
loading={this.state.changingAvatar}
|
||||
>
|
||||
loading={this.state.changingAvatar}>
|
||||
Pick avatar
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
@ -172,8 +168,7 @@ export default class SceneEditAccount extends React.Component {
|
||||
label="Username"
|
||||
description={
|
||||
<React.Fragment>
|
||||
This is your username on Slate. Your username is used for your
|
||||
profile URL{" "}
|
||||
This is your username on Slate. Your username is used for your profile URL{" "}
|
||||
<a href={profileURL} target="_blank">
|
||||
{profileURL}
|
||||
</a>
|
||||
@ -186,10 +181,7 @@ export default class SceneEditAccount extends React.Component {
|
||||
/>
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<System.ButtonPrimary
|
||||
onClick={this._handleSave}
|
||||
loading={this.state.changingUsername}
|
||||
>
|
||||
<System.ButtonPrimary onClick={this._handleSave} loading={this.state.changingUsername}>
|
||||
Change username
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
@ -221,10 +213,7 @@ export default class SceneEditAccount extends React.Component {
|
||||
/>
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<System.ButtonPrimary
|
||||
onClick={this._handleChangePassword}
|
||||
loading={this.state.changingPassword}
|
||||
>
|
||||
<System.ButtonPrimary onClick={this._handleChangePassword} loading={this.state.changingPassword}>
|
||||
Change password
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
@ -236,10 +225,7 @@ export default class SceneEditAccount extends React.Component {
|
||||
/>
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<System.ButtonPrimary
|
||||
onClick={this._handleDelete}
|
||||
loading={this.state.deleting}
|
||||
>
|
||||
<System.ButtonPrimary onClick={this._handleDelete} loading={this.state.deleting}>
|
||||
Delete my account
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
|
@ -1,14 +1,10 @@
|
||||
import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Fixtures from "~/common/fixtures";
|
||||
import * as System from "~/components/system";
|
||||
import * as SVG from "~/components/system/svg";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
import Section from "~/components/core/Section";
|
||||
import ScenePage from "~/components/core/ScenePage";
|
||||
import MediaObject from "~/components/core/MediaObject";
|
||||
|
||||
const STYLES_FLEX = css`
|
||||
display: flex;
|
||||
@ -47,19 +43,6 @@ const STYLES_RIGHT = css`
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_ASSET = css`
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 10%;
|
||||
height: 100%;
|
||||
background-color: ${Constants.system.pitchBlack};
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% 50%;
|
||||
`;
|
||||
|
||||
const STYLES_BOTTOM = css`
|
||||
background: ${Constants.system.pitchBlack};
|
||||
color: ${Constants.system.white};
|
||||
@ -79,62 +62,21 @@ const STYLES_PATH = css`
|
||||
overflow-wrap: break-word;
|
||||
`;
|
||||
|
||||
const STYLES_ITEM = css`
|
||||
border-radius: 4px;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
min-height: 32px;
|
||||
padding: 6px 16px 6px 16px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.2px;
|
||||
font-family: ${Constants.font.semiBold};
|
||||
transition: 200ms ease all;
|
||||
cursor: pointer;
|
||||
background-color: ${Constants.system.brand};
|
||||
color: ${Constants.system.white};
|
||||
margin-left: 16px;
|
||||
|
||||
:hover {
|
||||
background-color: ${Constants.system.green};
|
||||
}
|
||||
|
||||
:focus {
|
||||
box-shadow: inset 0 0 5px 2px rgba(0, 0, 0, 0.3);
|
||||
outline: 0;
|
||||
border: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default class SceneFile extends React.Component {
|
||||
state = {};
|
||||
|
||||
_handleChange = (e) => {
|
||||
this.setState({ [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
render() {
|
||||
const file = this.props.data;
|
||||
|
||||
const fileName = `${file.name}`;
|
||||
const fileURL = file.url ? file.url : `https://hub.textile.io${file.ipfs}`;
|
||||
const fileURL = this.props.data.url ? this.props.data.url : `https://hub.textile.io${this.props.data.ipfs}`;
|
||||
|
||||
return (
|
||||
<div css={STYLES_FLEX}>
|
||||
<div css={STYLES_TOP}>
|
||||
<div css={STYLES_LEFT}>
|
||||
<span css={STYLES_PATH}>{fileName}</span>
|
||||
<span css={STYLES_PATH}>{fileURL}</span>
|
||||
</div>
|
||||
<div css={STYLES_RIGHT} onClick={() => this.props.onBack()}>
|
||||
<SVG.Dismiss height="24px" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
css={STYLES_ASSET}
|
||||
style={{ backgroundImage: `url('${fileURL}')` }}
|
||||
/>
|
||||
<MediaObject data={this.props.data} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Actions from "~/common/actions";
|
||||
import * as System from "~/components/system";
|
||||
|
||||
@ -30,7 +28,7 @@ export default class SceneFilesFolder extends React.Component {
|
||||
});
|
||||
|
||||
console.log({ jobs });
|
||||
|
||||
if (jobs.length) {
|
||||
const response = await Actions.checkCIDStatus(jobs);
|
||||
|
||||
console.log(response);
|
||||
@ -38,6 +36,7 @@ export default class SceneFilesFolder extends React.Component {
|
||||
if (response && response.update) {
|
||||
await this.props.onRehydrate();
|
||||
}
|
||||
}
|
||||
|
||||
if (this._interval) {
|
||||
this._interval = window.setTimeout(this.loop, POLLING_INTERVAL);
|
||||
@ -63,7 +62,8 @@ export default class SceneFilesFolder extends React.Component {
|
||||
|
||||
const data = {
|
||||
columns: [
|
||||
{ key: "name", name: "File", type: "FILE_LINK" },
|
||||
{ key: "name", name: "File", type: "FILE_LINK", width: "100%" },
|
||||
{ key: "type", name: "Type" },
|
||||
{
|
||||
key: "size",
|
||||
name: "Size",
|
||||
@ -105,7 +105,7 @@ export default class SceneFilesFolder extends React.Component {
|
||||
title={this.props.current.name}
|
||||
buttons={[
|
||||
{
|
||||
name: "Upload to IPFS",
|
||||
name: "Upload data",
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
|
||||
},
|
||||
|
@ -78,14 +78,13 @@ export default class SceneHome extends React.Component {
|
||||
};
|
||||
|
||||
// TODO(jim): Refactor later.
|
||||
const slateButtons = [
|
||||
{ name: "Create slate", type: "SIDEBAR", value: "SIDEBAR_CREATE_SLATE" },
|
||||
];
|
||||
const slateButtons = [{ name: "Create slate", type: "SIDEBAR", value: "SIDEBAR_CREATE_SLATE" }];
|
||||
|
||||
// TODO(jim): Refactor later.
|
||||
const data = {
|
||||
columns: [
|
||||
{ key: "name", name: "Data", type: "FILE_LINK" },
|
||||
{ key: "name", name: "Data", type: "FILE_LINK", width: "100%" },
|
||||
{ key: "type", name: "Type" },
|
||||
{
|
||||
key: "size",
|
||||
name: "Size",
|
||||
@ -97,14 +96,13 @@ export default class SceneHome extends React.Component {
|
||||
name: "Date uploaded",
|
||||
width: "160px",
|
||||
type: "FILE_DATE",
|
||||
tooltip:
|
||||
"This date represents when the file was first uploaded to IPFS.",
|
||||
},
|
||||
{
|
||||
key: "networks",
|
||||
name: "Network",
|
||||
type: "NETWORK_TYPE",
|
||||
width: "188px",
|
||||
tooltip: "This data is publicly available to share on the internet!",
|
||||
},
|
||||
],
|
||||
rows: this.props.viewer.library[0].children.map((each) => {
|
||||
@ -123,7 +121,7 @@ export default class SceneHome extends React.Component {
|
||||
value: this.props.viewer.library[0].id,
|
||||
},
|
||||
{
|
||||
name: "Upload to IPFS",
|
||||
name: "Upload data",
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
|
||||
},
|
||||
@ -152,11 +150,7 @@ export default class SceneHome extends React.Component {
|
||||
<ScenePage>
|
||||
<System.H1>Home</System.H1>
|
||||
{this.props.viewer.addresses[0] ? (
|
||||
<Section
|
||||
title="Wallet addresses"
|
||||
buttons={walletButtons}
|
||||
onAction={this.props.onAction}
|
||||
>
|
||||
<Section title="Wallet addresses" buttons={walletButtons} onAction={this.props.onAction}>
|
||||
<System.Table
|
||||
data={wallet}
|
||||
name="transaction"
|
||||
@ -166,11 +160,7 @@ export default class SceneHome extends React.Component {
|
||||
</Section>
|
||||
) : null}
|
||||
|
||||
<Section
|
||||
title="Slates"
|
||||
buttons={slateButtons}
|
||||
onAction={this.props.onAction}
|
||||
>
|
||||
<Section title="Slates" buttons={slateButtons} onAction={this.props.onAction}>
|
||||
<System.Table
|
||||
data={slates}
|
||||
name="slate"
|
||||
@ -180,11 +170,7 @@ export default class SceneHome extends React.Component {
|
||||
</Section>
|
||||
|
||||
{this.props.viewer.library[0] ? (
|
||||
<Section
|
||||
title="Recent data"
|
||||
buttons={dataButtons}
|
||||
onAction={this.props.onAction}
|
||||
>
|
||||
<Section title="Recent data" buttons={dataButtons} onAction={this.props.onAction}>
|
||||
<System.Table
|
||||
data={data}
|
||||
name="data"
|
||||
|
@ -15,6 +15,7 @@ export default class SceneSlate extends React.Component {
|
||||
const slates = {
|
||||
columns: [
|
||||
{ key: "name", name: "Data", type: "FILE_LINK", width: "288px" },
|
||||
{ key: "type", name: "Data type" },
|
||||
{ key: "url", name: "Asset URL", width: "100%" },
|
||||
],
|
||||
rows: images,
|
||||
@ -45,11 +46,7 @@ export default class SceneSlate extends React.Component {
|
||||
<System.H1>
|
||||
https://slate.host/@{this.props.viewer.username}/{slatename}
|
||||
</System.H1>
|
||||
<Section
|
||||
title="Images"
|
||||
buttons={slateButtons}
|
||||
onAction={this.props.onAction}
|
||||
>
|
||||
<Section title="Images" buttons={slateButtons} onAction={this.props.onAction}>
|
||||
<System.Table
|
||||
data={slates}
|
||||
name={`/@${this.props.viewer.username}/${slatename}`}
|
||||
|
Loading…
Reference in New Issue
Block a user