mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-27 10:52:41 +03:00
added amount stored to support message
This commit is contained in:
parent
183affecad
commit
d489c95c0b
@ -219,3 +219,10 @@ export const getSerializedProfile = async (data) => {
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
};
|
||||
|
||||
export const createSupportMessage = async (data) => {
|
||||
return await returnJSON(`/api/support-message`, {
|
||||
...DEFAULT_OPTIONS,
|
||||
body: JSON.stringify({ data }),
|
||||
});
|
||||
};
|
||||
|
@ -53,6 +53,7 @@ const constructSlatesTreeForNavigation = (slates) => {
|
||||
name: s.data.name || s.slatename,
|
||||
pageTitle: `Viewing ${s.slatename}`,
|
||||
decorator: "SLATE",
|
||||
ignore: true,
|
||||
};
|
||||
});
|
||||
};
|
||||
@ -77,7 +78,7 @@ export const generate = ({ library = [], slates = [] }) => [
|
||||
decorator: "SLATES",
|
||||
name: "Slates",
|
||||
pageTitle: "Slates",
|
||||
children: constructSlatesTreeForNavigation(slates),
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
id: "V1_NAVIGATION_SLATE",
|
||||
@ -87,6 +88,7 @@ export const generate = ({ library = [], slates = [] }) => [
|
||||
children: null,
|
||||
ignore: true,
|
||||
},
|
||||
...constructSlatesTreeForNavigation(slates),
|
||||
constructFilesTreeForNavigation(library),
|
||||
/*
|
||||
{
|
||||
|
@ -200,10 +200,7 @@ export const createSlug = (text, base = "untitled") => {
|
||||
return base;
|
||||
}
|
||||
|
||||
text = text
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.trim();
|
||||
text = text.toString().toLowerCase().trim();
|
||||
|
||||
const sets = [
|
||||
{ to: "a", from: "[ÀÁÂÃÅÆĀĂĄẠẢẤẦẨẪẬẮẰẲẴẶ]" },
|
||||
|
@ -2,6 +2,7 @@ import * as Strings from "~/common/strings";
|
||||
|
||||
const USERNAME_REGEX = new RegExp("^[a-zA-Z0-9_]{0,}[a-zA-Z]+[0-9]*$");
|
||||
const MIN_PASSWORD_LENGTH = 8;
|
||||
const EMAIL_REGEX = /^[\w-]+@[a-zA-Z0-9_]+?\.[a-zA-Z]{2,50}$/;
|
||||
|
||||
// TODO(jim): Regex should cover some of this.
|
||||
const REJECT_LIST = [
|
||||
@ -71,6 +72,17 @@ export const slatename = (text) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
export const email = (text) => {
|
||||
if (!text || !text.length) {
|
||||
return false;
|
||||
}
|
||||
if (!EMAIL_REGEX.test(text)) {
|
||||
console.log(text);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export const username = (text) => {
|
||||
if (Strings.isEmpty(text)) {
|
||||
return false;
|
||||
|
@ -41,6 +41,7 @@ import SidebarAddFileToBucket from "~/components/sidebars/SidebarAddFileToBucket
|
||||
import SidebarDragDropNotice from "~/components/sidebars/SidebarDragDropNotice";
|
||||
import SidebarSingleSlateSettings from "~/components/sidebars/SidebarSingleSlateSettings";
|
||||
import SidebarFilecoinArchive from "~/components/sidebars/SidebarFilecoinArchive";
|
||||
import SidebarHelp from "~/components/sidebars/SidebarHelp";
|
||||
|
||||
// NOTE(jim):
|
||||
// Core components to the application structure.
|
||||
@ -65,6 +66,7 @@ const SIDEBARS = {
|
||||
SIDEBAR_CREATE_SLATE: <SidebarCreateSlate />,
|
||||
SIDEBAR_DRAG_DROP_NOTICE: <SidebarDragDropNotice />,
|
||||
SIDEBAR_SINGLE_SLATE_SETTINGS: <SidebarSingleSlateSettings />,
|
||||
SIDEBAR_HELP: <SidebarHelp />,
|
||||
};
|
||||
|
||||
const SCENES = {
|
||||
@ -329,10 +331,6 @@ export default class ApplicationPage extends React.Component {
|
||||
return response;
|
||||
};
|
||||
|
||||
_handleCancel = () => {
|
||||
this._handleDismissSidebar();
|
||||
};
|
||||
|
||||
_handleDeleteYourself = async () => {
|
||||
// TODO(jim): Put this somewhere better for messages.
|
||||
const message =
|
||||
@ -623,7 +621,7 @@ export default class ApplicationPage extends React.Component {
|
||||
sidebarLoading: this.state.sidebarLoading,
|
||||
onSelectedChange: this._handleSelectedChange,
|
||||
onSubmit: this._handleSubmit,
|
||||
onCancel: this._handleCancel,
|
||||
onCancel: this._handleDismissSidebar,
|
||||
onRegisterFileLoading: this._handleRegisterFileLoading,
|
||||
onUploadFile: this._handleUploadFile,
|
||||
onSidebarLoading: this._handleSidebarLoading,
|
||||
|
@ -44,7 +44,7 @@ const STYLES_APPLICATION_HEADER = css`
|
||||
|
||||
const STYLES_LEFT = css`
|
||||
flex-shrink: 0;
|
||||
width: 352px;
|
||||
${"" /* width: 352px; */}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
@ -57,11 +57,12 @@ const STYLES_MIDDLE = css`
|
||||
`;
|
||||
|
||||
const STYLES_RIGHT = css`
|
||||
flex-shrink: 0;
|
||||
min-width: 10%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 16px;
|
||||
padding-right: 264px;
|
||||
`;
|
||||
|
||||
const rotate = keyframes`
|
||||
@ -154,8 +155,17 @@ export default class ApplicationHeader extends React.Component {
|
||||
<SVG.Search height="20px" />
|
||||
</span>
|
||||
</div>
|
||||
<div css={STYLES_MIDDLE} />
|
||||
<div css={STYLES_RIGHT} />
|
||||
{/* <div css={STYLES_MIDDLE} /> */}
|
||||
<div css={STYLES_RIGHT}>
|
||||
<span
|
||||
css={STYLES_ICON_ELEMENT}
|
||||
onClick={() =>
|
||||
this.props.onAction({ type: "SIDEBAR", value: "SIDEBAR_HELP" })
|
||||
}
|
||||
>
|
||||
<SVG.Help height="24px" />
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ const STYLES_GROUP = css`
|
||||
const STYLES_HEADER = css`
|
||||
font-family: ${Constants.font.semiBold};
|
||||
font-size: 18px;
|
||||
margin-top: 48px;
|
||||
margin-top: 32px;
|
||||
`;
|
||||
|
||||
export default class SidebarCreateSlate extends React.Component {
|
||||
@ -114,14 +114,7 @@ export default class SidebarCreateSlate extends React.Component {
|
||||
Create slate
|
||||
</System.P>
|
||||
|
||||
<System.P
|
||||
style={{
|
||||
fontFamily: Constants.font.semiBold,
|
||||
fontSize: "1.1rem",
|
||||
}}
|
||||
>
|
||||
Name
|
||||
</System.P>
|
||||
<System.P css={STYLES_HEADER}>Name</System.P>
|
||||
<System.Input
|
||||
name="name"
|
||||
style={{ marginTop: 12 }}
|
||||
|
173
components/sidebars/SidebarHelp.js
Normal file
173
components/sidebars/SidebarHelp.js
Normal file
@ -0,0 +1,173 @@
|
||||
import * as React from "react";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as System from "~/components/system";
|
||||
import * as Validations from "~/common/validations";
|
||||
import * as Actions from "~/common/actions";
|
||||
|
||||
import { dispatchCustomEvent } from "~/common/custom-events";
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
const STYLES_HEADER = css`
|
||||
font-family: ${Constants.font.semiBold};
|
||||
font-size: 18px;
|
||||
margin-top: 32px;
|
||||
`;
|
||||
|
||||
export default class SidebarCreateSlate extends React.Component {
|
||||
state = {
|
||||
name:
|
||||
this.props.viewer.data && this.props.viewer.data.name
|
||||
? this.props.viewer.data.name
|
||||
: "",
|
||||
email: "",
|
||||
message: "",
|
||||
loading: false,
|
||||
};
|
||||
|
||||
_handleSubmit = async () => {
|
||||
this.setState({ loading: true });
|
||||
if (!this.state.email || !this.state.email.length) {
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: {
|
||||
message: "Please provide an email address where we can reach you",
|
||||
},
|
||||
},
|
||||
});
|
||||
this.setState({ loading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.state.message || !this.state.message.length) {
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: {
|
||||
message: "Please provide a message",
|
||||
},
|
||||
},
|
||||
});
|
||||
this.setState({ loading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Validations.email(this.state.email)) {
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: { message: "Please check that your email address is valid" },
|
||||
},
|
||||
});
|
||||
this.setState({ loading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await Actions.createSupportMessage({
|
||||
username: this.props.viewer.username,
|
||||
name: this.state.name,
|
||||
email: this.state.email,
|
||||
message: this.state.message,
|
||||
stored: Strings.bytesToSize(this.props.viewer.stats.bytes),
|
||||
});
|
||||
|
||||
if (!response) {
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: {
|
||||
message:
|
||||
"We're having trouble sending out your message right now. Please try again",
|
||||
},
|
||||
},
|
||||
});
|
||||
this.setState({ loading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
decorator: response.decorator,
|
||||
},
|
||||
});
|
||||
this.setState({ loading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
dispatchCustomEvent({
|
||||
name: "create-alert",
|
||||
detail: {
|
||||
alert: {
|
||||
message: "Message sent. You'll hear from us shortly",
|
||||
status: "INFO",
|
||||
},
|
||||
},
|
||||
});
|
||||
this.setState({ loading: false });
|
||||
this.props.onCancel();
|
||||
return;
|
||||
};
|
||||
|
||||
_handleChange = (e) => {
|
||||
this.setState({ [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<System.P
|
||||
style={{
|
||||
fontFamily: Constants.font.semiBold,
|
||||
fontSize: Constants.typescale.lvl3,
|
||||
marginBottom: "64px",
|
||||
}}
|
||||
>
|
||||
Talk to us
|
||||
</System.P>
|
||||
|
||||
<System.P css={STYLES_HEADER}>Name</System.P>
|
||||
<System.Input
|
||||
name="name"
|
||||
style={{ marginTop: 16 }}
|
||||
placeholder="Name"
|
||||
value={this.state.name}
|
||||
onChange={this._handleChange}
|
||||
onSubmit={this._handleSubmit}
|
||||
/>
|
||||
|
||||
<System.P css={STYLES_HEADER}>Email</System.P>
|
||||
<System.Input
|
||||
name="email"
|
||||
style={{ marginTop: 16 }}
|
||||
placeholder="Email"
|
||||
value={this.state.email}
|
||||
onChange={this._handleChange}
|
||||
onSubmit={this._handleSubmit}
|
||||
/>
|
||||
|
||||
<System.P css={STYLES_HEADER}>Message</System.P>
|
||||
|
||||
<System.Textarea
|
||||
style={{ marginTop: 16 }}
|
||||
name="message"
|
||||
value={this.state.message}
|
||||
placeholder="Leave us your questions or feedback and we'll get back to you soon!"
|
||||
onChange={this._handleChange}
|
||||
onSubmit={this._handleSubmit}
|
||||
/>
|
||||
|
||||
<System.ButtonPrimary
|
||||
full
|
||||
style={{ marginTop: 48 }}
|
||||
onClick={this._handleSubmit}
|
||||
loading={this.state.loading}
|
||||
>
|
||||
Send message
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ const STYLES_GROUP = css`
|
||||
const STYLES_HEADER = css`
|
||||
font-family: ${Constants.font.semiBold};
|
||||
font-size: 18px;
|
||||
margin-top: 48px;
|
||||
margin-top: 32px;
|
||||
`;
|
||||
|
||||
export default class SidebarSingleSlateSettings extends React.Component {
|
||||
@ -168,7 +168,6 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
||||
style={{ marginTop: 12 }}
|
||||
name="body"
|
||||
value={this.state.body}
|
||||
placeholder="A slate."
|
||||
onChange={this._handleChange}
|
||||
onSubmit={this._handleSubmit}
|
||||
/>
|
||||
|
@ -24,7 +24,23 @@ const STYLES_TEXTAREA = css`
|
||||
border: 0;
|
||||
transition: 200ms ease all;
|
||||
padding: 16px 24px 16px 24px;
|
||||
box-shadow: inset 0 0 0 1px ${Constants.system.border};
|
||||
box-shadow: 0 0 0 1px ${Constants.system.border} inset;
|
||||
|
||||
::placeholder {
|
||||
/* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||
color: ${Constants.system.darkGray};
|
||||
opacity: 1; /* Firefox */
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
/* Internet Explorer 10-11 */
|
||||
color: ${Constants.system.darkGray};
|
||||
}
|
||||
|
||||
::-ms-input-placeholder {
|
||||
/* Microsoft Edge */
|
||||
color: ${Constants.system.darkGray};
|
||||
}
|
||||
`;
|
||||
|
||||
export class Textarea extends React.Component {
|
||||
@ -34,6 +50,7 @@ export class Textarea extends React.Component {
|
||||
css={STYLES_TEXTAREA}
|
||||
style={this.props.style}
|
||||
onChange={this.props.onChange}
|
||||
placeholder={this.props.placeholder}
|
||||
name={this.props.name}
|
||||
value={this.props.value}
|
||||
readOnly={this.props.readOnly}
|
||||
|
31
node_common/support.js
Normal file
31
node_common/support.js
Normal file
@ -0,0 +1,31 @@
|
||||
import * as Environment from "~/node_common/environment";
|
||||
import * as Strings from "~/common/strings";
|
||||
|
||||
import { IncomingWebhook } from "@slack/webhook";
|
||||
|
||||
const url = `https://hooks.slack.com/services/${Environment.SUPPORT_SLACK_WEBHOOK_KEY}`;
|
||||
const webhook = new IncomingWebhook(url);
|
||||
|
||||
export const sendSlackMessage = ({
|
||||
username,
|
||||
name,
|
||||
email,
|
||||
message,
|
||||
stored,
|
||||
}) => {
|
||||
if (Strings.isEmpty(Environment.SUPPORT_SLACK_WEBHOOK_KEY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const userProfileURL = `https://slate.host/${username}`;
|
||||
const userURL = `<${userProfileURL}|${username}>`;
|
||||
|
||||
try {
|
||||
webhook.send({
|
||||
text: `\n*Username:* ${userURL} (${stored} stored)\n*Name:* ${name}\n*Email:* ${email}\n*Message:* ${message}`,
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
69
pages/api/support-message.js
Normal file
69
pages/api/support-message.js
Normal file
@ -0,0 +1,69 @@
|
||||
import * as Environment from "~/node_common/environment";
|
||||
import * as Data from "~/node_common/data";
|
||||
import * as Utilities from "~/node_common/utilities";
|
||||
import * as Serializers from "~/node_common/serializers";
|
||||
import * as Support from "~/node_common/support";
|
||||
|
||||
export default async (req, res) => {
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
if (!id) {
|
||||
return res.status(500).send({ decorator: "SERVER_SUPPORT", error: true });
|
||||
}
|
||||
|
||||
const user = await Data.getUserById({
|
||||
id,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ decorator: "SERVER_SUPPORT_USER_NOT_FOUND", error: true });
|
||||
}
|
||||
|
||||
if (user.error) {
|
||||
return res
|
||||
.status(500)
|
||||
.send({ decorator: "SERVER_SUPPORT_USER_NOT_FOUND", error: true });
|
||||
}
|
||||
|
||||
if (!req.body.data) {
|
||||
return res.status(500).send({
|
||||
decorator: "SERVER_SUPPORT_NO_DATA_PROVIDED",
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!req.body.data.email) {
|
||||
return res.status(500).send({
|
||||
decorator: "SERVER_SUPPORT_MUST_PROVIDE_EMAIL",
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!req.body.data.message) {
|
||||
return res.status(500).send({
|
||||
decorator: "SERVER_SUPPORT_MUST_PROVIDE_MESSAGE",
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!req.body.data.username) {
|
||||
return res.status(500).send({
|
||||
decorator: "SERVER_SUPPORT_NO_DATA_PROVIDED",
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
|
||||
let status = await Support.sendSlackMessage(req.body.data);
|
||||
if (status) {
|
||||
return res.status(200).send({
|
||||
decorator: "SERVER_SUPPORT",
|
||||
data: true,
|
||||
});
|
||||
} else {
|
||||
return res.status(500).send({
|
||||
decorator: "SERVER_SUPPORT",
|
||||
error: true,
|
||||
});
|
||||
}
|
||||
};
|
@ -71,7 +71,7 @@ export default class SceneFilesFolder extends React.Component {
|
||||
_interval;
|
||||
|
||||
state = {
|
||||
view: "list",
|
||||
view: "grid",
|
||||
startIndex: 0,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user