mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-23 14:07:20 +03:00
cleanup: small touchups, updates readme for electron
This commit is contained in:
parent
8ab55ffc46
commit
d293e479aa
10
README.md
10
README.md
@ -71,14 +71,8 @@ npm run dev
|
||||
|
||||
- Visit `localhost:1337` in a browser.
|
||||
|
||||
### BROKEN: Run electron (MacOS)
|
||||
|
||||
@jimmylee broke the current version of the client.
|
||||
### Run electron (MacOS)
|
||||
|
||||
```sh
|
||||
rm -rf .next
|
||||
npm run build-electron
|
||||
npm run electron-pack
|
||||
npm run electron-dev
|
||||
```
|
||||
|
||||
Open **Slate.app** in `dist/mac/slate.app`.
|
||||
|
@ -31,12 +31,11 @@ export const getCurrentById = (navigation, targetId) => {
|
||||
};
|
||||
|
||||
const constructFilesTreeForNavigation = (library) => {
|
||||
let bytes = 0;
|
||||
library[0].children.forEach((o) => {
|
||||
bytes = o.size + bytes;
|
||||
});
|
||||
|
||||
return { ...library[0], name: `Data (${Strings.bytesToSize(bytes)})`, children: [] };
|
||||
return {
|
||||
...library[0],
|
||||
name: `Data`,
|
||||
children: [],
|
||||
};
|
||||
};
|
||||
|
||||
const constructSlatesTreeForNavigation = (slates) => {
|
||||
@ -103,7 +102,7 @@ export const generate = ({ library = [], slates = [] }) => [
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
name: "Developer API",
|
||||
name: "API",
|
||||
pageTitle: "Developer API",
|
||||
decorator: "SETTINGS_DEVELOPER",
|
||||
children: null,
|
||||
@ -116,16 +115,9 @@ export const generate = ({ library = [], slates = [] }) => [
|
||||
children: null,
|
||||
ignore: true,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Public profile",
|
||||
pageTitle: "Profile Page",
|
||||
decorator: "PROFILE_PAGE",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Filecoin Wallet",
|
||||
name: "Wallet",
|
||||
pageTitle: "Your wallet and addresses",
|
||||
decorator: "WALLET",
|
||||
children: [
|
||||
|
@ -100,12 +100,13 @@ export const injectTooltipStyles = () =>
|
||||
}
|
||||
|
||||
.tippy-tooltip {
|
||||
font-family: ${Constants.font.text};
|
||||
font-family: ${Constants.font.code};
|
||||
color: ${Constants.system.white};
|
||||
background-color: ${Constants.system.pitchBlack};
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
will-change: transform;
|
||||
|
@ -350,12 +350,6 @@ export default class ApplicationPage extends React.Component {
|
||||
};
|
||||
|
||||
_handleNavigateTo = (next, data = null) => {
|
||||
// TODO(jim): Refactor this hack for profile pages.
|
||||
if (next.id === 5) {
|
||||
window.open(`/@${this.state.viewer.username}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.history[this.state.currentIndex].scrollTop = window.scrollY;
|
||||
this.state.history[this.state.currentIndex].data = data;
|
||||
|
||||
|
78
components/core/ApplicationControlMenu.js
Normal file
78
components/core/ApplicationControlMenu.js
Normal file
@ -0,0 +1,78 @@
|
||||
import * as React from "react";
|
||||
import * as Constants from "~/common/constants";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
import Dismissible from "~/components/core/Dismissible";
|
||||
|
||||
const STYLES_AVATAR = css`
|
||||
display: inline-flex;
|
||||
background-size: cover;
|
||||
background-position: 50% 50%;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
background-color: ${Constants.system.black};
|
||||
color: ${Constants.system.white};
|
||||
transition: 100ms ease all;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
:hover {
|
||||
background-color: ${Constants.system.brand};
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_AVATAR_ONLINE = css`
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-color: ${Constants.system.green};
|
||||
border: 2px solid ${Constants.system.white};
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
right: -4px;
|
||||
border-radius: 16px;
|
||||
`;
|
||||
|
||||
export default class ApplicationControlMenu extends React.Component {
|
||||
state = {};
|
||||
|
||||
_handleClick = (e) => {
|
||||
if (this.props.popover) {
|
||||
this.setState({ visible: !this.state.visible });
|
||||
}
|
||||
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(e);
|
||||
}
|
||||
};
|
||||
|
||||
_handleHide = () => {
|
||||
this.setState({ visible: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Dismissible
|
||||
css={STYLES_AVATAR}
|
||||
captureResize={false}
|
||||
captureScroll={true}
|
||||
enabled={this.state.visible}
|
||||
onOutsideRectEvent={this._handleHide}
|
||||
onClick={this._handleClick}
|
||||
style={{
|
||||
...this.props.style,
|
||||
width: `${this.props.size}px`,
|
||||
height: `${this.props.size}px`,
|
||||
borderRadius: `${this.props.size}px`,
|
||||
backgroundImage: `url('${this.props.url}')`,
|
||||
cursor: this.props.onClick ? "pointer" : this.props.style,
|
||||
backgroundColor: this.state.visible ? Constants.system.brand : null,
|
||||
}}
|
||||
>
|
||||
{this.props.icon ? this.props.icon : null}
|
||||
{this.state.visible ? this.props.popover : null}
|
||||
{this.props.online ? <span css={STYLES_AVATAR_ONLINE} /> : null}
|
||||
</Dismissible>
|
||||
);
|
||||
}
|
||||
}
|
@ -2,10 +2,12 @@ import * as React from "react";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as System from "~/components/system";
|
||||
import * as SVG from "~/common/svg";
|
||||
import * as OldSVG from "~/components/system/svg";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { Tooltip } from "react-tippy";
|
||||
|
||||
import Avatar from "~/components/core/Avatar";
|
||||
import ApplicationControlMenu from "~/components/core/ApplicationControlMenu";
|
||||
|
||||
const STYLES_CIRCLE = css`
|
||||
height: 32px;
|
||||
@ -55,7 +57,7 @@ const STYLES_HOME = css`
|
||||
user-select: none;
|
||||
margin-right: 24px;
|
||||
margin-left: 24px;
|
||||
font-size: 11px;
|
||||
font-size: 14px;
|
||||
text-transform: uppercase;
|
||||
font-family: ${Constants.font.codeBold};
|
||||
`;
|
||||
@ -92,27 +94,37 @@ const STYLES_RIGHT = css`
|
||||
padding-right: 16px;
|
||||
`;
|
||||
|
||||
const STYLES_INPUT = css`
|
||||
width: 100%;
|
||||
max-width: 1024px;
|
||||
font-size: 16px;
|
||||
height: 40px;
|
||||
padding: 0 16px 0 16px;
|
||||
background-color: ${Constants.system.white};
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 0 0 1px #e0e0e0, 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||
border: 0;
|
||||
outline: 0;
|
||||
box-sizing: border-box;
|
||||
const STYLES_PROFILE = css`
|
||||
font-family: ${Constants.font.semiBold};
|
||||
background-color: ${Constants.system.pitchBlack};
|
||||
color: ${Constants.system.white};
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
text-decoration: none;
|
||||
height: 36px;
|
||||
padding-right: 24px;
|
||||
border-radius: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
transition: 200ms ease all;
|
||||
|
||||
:focus {
|
||||
box-shadow: 0 1px 4px rgba(0, 71, 255, 0.3),
|
||||
inset 0 0 0 1px ${Constants.system.brand};
|
||||
outline: 0;
|
||||
:hover {
|
||||
background-color: ${Constants.system.brand};
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_IMAGE = css`
|
||||
background-size: cover;
|
||||
background-position: 50% 50%;
|
||||
flex-shrink: 0;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
border-radius: 32px;
|
||||
margin-right: 16px;
|
||||
margin-left: 2px;
|
||||
`;
|
||||
|
||||
export default class ApplicationHeader extends React.Component {
|
||||
render() {
|
||||
const isBackDisabled =
|
||||
@ -125,7 +137,7 @@ export default class ApplicationHeader extends React.Component {
|
||||
return (
|
||||
<header css={STYLES_APPLICATION_HEADER}>
|
||||
<div css={STYLES_LEFT}>
|
||||
<span css={STYLES_HOME}>Slate {Constants.values.version}</span>
|
||||
<span css={STYLES_HOME}>Slate</span>
|
||||
<span
|
||||
css={STYLES_ICON_ELEMENT}
|
||||
style={
|
||||
@ -154,11 +166,32 @@ export default class ApplicationHeader extends React.Component {
|
||||
</div>
|
||||
<div css={STYLES_MIDDLE} />
|
||||
<div css={STYLES_RIGHT}>
|
||||
<Avatar
|
||||
<Tooltip
|
||||
animation="fade"
|
||||
animateFill={false}
|
||||
title="View your profile"
|
||||
>
|
||||
<a
|
||||
css={STYLES_PROFILE}
|
||||
href={`/@${this.props.viewer.username}`}
|
||||
target="_blank"
|
||||
>
|
||||
<span
|
||||
css={STYLES_IMAGE}
|
||||
style={{
|
||||
backgroundImage: `url('${this.props.viewer.data.photo}')`,
|
||||
}}
|
||||
/>
|
||||
{this.props.viewer.username}
|
||||
</a>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip animation="fade" animateFill={false} title="Settings Menu">
|
||||
<ApplicationControlMenu
|
||||
style={{ marginLeft: 12 }}
|
||||
onClick={() => {}}
|
||||
size={32}
|
||||
url={this.props.viewer.data.photo}
|
||||
size={36}
|
||||
icon={<OldSVG.ChevronDown height="20px" />}
|
||||
popover={
|
||||
<System.PopoverNavigation
|
||||
style={{ right: 0, top: "48px", cursor: "pointer" }}
|
||||
@ -173,6 +206,7 @@ export default class ApplicationHeader extends React.Component {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
@ -6,6 +6,7 @@ import * as SVG from "~/common/svg";
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
import Pill from "~/components/core/Pill";
|
||||
import DataMeter from "~/components/core/DataMeter";
|
||||
|
||||
const IconMap = {
|
||||
HOME: <SVG.Home height="20px" />,
|
||||
@ -235,6 +236,13 @@ class NodeReference extends React.Component {
|
||||
|
||||
export default class ApplicationNavigation extends React.Component {
|
||||
render() {
|
||||
// TODO(jim):
|
||||
// Calculate this idea elsewhere if you keep it.
|
||||
let bytes = 0;
|
||||
this.props.viewer.library[0].children.forEach((each) => {
|
||||
bytes = each.size + bytes;
|
||||
});
|
||||
|
||||
return (
|
||||
<nav css={STYLES_NAVIGATION}>
|
||||
{this.props.navigation.map((each) => {
|
||||
@ -262,6 +270,8 @@ export default class ApplicationNavigation extends React.Component {
|
||||
</NodeReference>
|
||||
);
|
||||
})}
|
||||
<br />
|
||||
<DataMeter currentBytes={bytes} />
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
130
components/core/DataMeter.js
Normal file
130
components/core/DataMeter.js
Normal file
@ -0,0 +1,130 @@
|
||||
import * as React from "react";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as System from "~/components/system";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
// NOTE(jim): 10 GB
|
||||
const MAX_IN_BYTES = 10737418240;
|
||||
|
||||
const STYLES_CONTAINER = css`
|
||||
border-radius: 4px;
|
||||
border: 1px solid ${Constants.system.border};
|
||||
padding: 16px;
|
||||
`;
|
||||
|
||||
const STYLES_GUTTER = css`
|
||||
padding: 16px 16px 16px 16px;
|
||||
`;
|
||||
|
||||
const STYLES_DATA = css`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 8px;
|
||||
border-radius: 3px;
|
||||
background-color: ${Constants.system.border};
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const STYLES_DATA_METER = css`
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
background-color: ${Constants.system.brand};
|
||||
background-image: linear-gradient(
|
||||
315deg,
|
||||
${Constants.system.brand} 0%,
|
||||
#009ffd 74%
|
||||
);
|
||||
`;
|
||||
|
||||
const STYLES_ROW = css`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
font-family: ${Constants.font.code};
|
||||
color: ${Constants.system.darkGray};
|
||||
font-size: 10px;
|
||||
margin-top: 2px;
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
const STYLES_STATS_ROW = css`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
font-family: ${Constants.font.code};
|
||||
color: ${Constants.system.black};
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
const STYLES_LEFT = css`
|
||||
min-width: 10%;
|
||||
width: 100% "";
|
||||
`;
|
||||
|
||||
const STYLES_RIGHT = css`
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
|
||||
const STYLES_TITLE = css`
|
||||
font-family: ${Constants.font.semiBold};
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
`;
|
||||
|
||||
const STYLES_HREF = css`
|
||||
font-family: ${Constants.font.semiBold};
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
transition: 200ms ease color;
|
||||
|
||||
:hover {
|
||||
color: ${Constants.system.brand};
|
||||
}
|
||||
`;
|
||||
|
||||
export default (props) => {
|
||||
const percentage = props.currentBytes / MAX_IN_BYTES;
|
||||
console.log(percentage * 100);
|
||||
|
||||
return (
|
||||
<div css={STYLES_GUTTER}>
|
||||
<div css={STYLES_CONTAINER}>
|
||||
<System.P style={{ fontSize: 12 }}>
|
||||
<strong css={STYLES_TITLE}>Usage</strong>
|
||||
Slate users get 10GB of IPFS storage for free.{" "}
|
||||
<strong
|
||||
css={STYLES_HREF}
|
||||
onClick={() => alert("TODO: SUBSCRIPTION OPTIONS")}
|
||||
>
|
||||
(upgrade)
|
||||
</strong>
|
||||
<br />
|
||||
<br />
|
||||
</System.P>
|
||||
|
||||
<div css={STYLES_STATS_ROW}>
|
||||
<div css={STYLES_LEFT}>{Strings.bytesToSize(props.currentBytes)}</div>
|
||||
<div css={STYLES_RIGHT}>{Strings.bytesToSize(MAX_IN_BYTES)}</div>
|
||||
</div>
|
||||
|
||||
<div css={STYLES_ROW}>
|
||||
<div css={STYLES_LEFT}>Used</div>
|
||||
<div css={STYLES_RIGHT}>Total</div>
|
||||
</div>
|
||||
|
||||
<div css={STYLES_DATA} style={{ marginTop: 4 }}>
|
||||
<div
|
||||
css={STYLES_DATA_METER}
|
||||
style={{ width: `${percentage * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -160,6 +160,7 @@ export class Input extends React.Component {
|
||||
full={this.props.full}
|
||||
tooltip={this.props.tooltip}
|
||||
label={this.props.label}
|
||||
style={this.props.descriptionStyle}
|
||||
description={this.props.description}
|
||||
/>
|
||||
<div style={{ position: "relative" }}>
|
||||
|
@ -9,7 +9,9 @@ const STYLES_POPOVER = css`
|
||||
position: absolute;
|
||||
width: 288px;
|
||||
border-radius: 4px;
|
||||
user-select: none;
|
||||
background-color: ${Constants.system.white};
|
||||
color: ${Constants.system.pitchBlack};
|
||||
box-shadow: inset 0 0 0 1px ${Constants.system.border},
|
||||
0 1px 4px rgba(0, 0, 0, 0.07);
|
||||
`;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as React from "react";
|
||||
import * as Actions from "~/common/actions";
|
||||
import * as System from "~/components/system";
|
||||
import * as Strings from "~/common/strings";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
@ -53,10 +54,15 @@ export default class SceneFilesFolder extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let bytes = 0;
|
||||
let rows = this.props.viewer.library[0].children.map((each) => {
|
||||
bytes = each.size + bytes;
|
||||
return {
|
||||
...each,
|
||||
button: each.networks && each.networks.includes("FILECOIN") ? null : "Store on Filecoin",
|
||||
button:
|
||||
each.networks && each.networks.includes("FILECOIN")
|
||||
? null
|
||||
: "Store on Filecoin",
|
||||
};
|
||||
});
|
||||
|
||||
@ -102,14 +108,15 @@ export default class SceneFilesFolder extends React.Component {
|
||||
<System.H1>{this.props.current.name}</System.H1>
|
||||
<Section
|
||||
onAction={this.props.onAction}
|
||||
title="All data"
|
||||
title={`${Strings.bytesToSize(bytes)} uploaded`}
|
||||
buttons={[
|
||||
{
|
||||
name: "Upload data",
|
||||
type: "SIDEBAR",
|
||||
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
|
||||
},
|
||||
]}>
|
||||
]}
|
||||
>
|
||||
<System.Table
|
||||
key={this.props.current.folderId}
|
||||
data={data}
|
||||
|
@ -108,7 +108,9 @@ export default class SceneSlate extends React.Component {
|
||||
},
|
||||
];
|
||||
|
||||
const slateURL = `https://slate.host/@${this.props.viewer.username}/${slatename}`;
|
||||
const slateURL = `https://slate.host/@${
|
||||
this.props.viewer.username
|
||||
}/${slatename}`;
|
||||
|
||||
return (
|
||||
<ScenePage>
|
||||
@ -116,10 +118,12 @@ export default class SceneSlate extends React.Component {
|
||||
label="Will the Slate page look like this in the final product?"
|
||||
description="No! Consider this page just a functionality test. Slates will be collaborative mood boards and will have a much more intuitive experience than this."
|
||||
/>
|
||||
<System.H1 style={{ marginTop: 48 }}>
|
||||
https://slate.host/@{this.props.viewer.username}/{slatename}
|
||||
</System.H1>
|
||||
<Section title="Slate elements" buttons={slateButtons} onAction={this.props.onAction}>
|
||||
<System.H1 style={{ marginTop: 48 }}>{slatename}</System.H1>
|
||||
<Section
|
||||
title="Slate elements"
|
||||
buttons={slateButtons}
|
||||
onAction={this.props.onAction}
|
||||
>
|
||||
<System.Table
|
||||
data={slates}
|
||||
name={slateURL}
|
||||
@ -130,10 +134,12 @@ export default class SceneSlate extends React.Component {
|
||||
|
||||
<System.Input
|
||||
containerStyle={{ marginTop: 48 }}
|
||||
style={{ marginTop: 24 }}
|
||||
label="Slatename"
|
||||
description={
|
||||
<React.Fragment>
|
||||
Changing the slatename will change your public slate URL. Your slate URL is:{" "}
|
||||
Changing the slatename will change your public slate URL. Your
|
||||
slate URL is:{" "}
|
||||
<a href={slateURL} target="_blank">
|
||||
{slateURL}
|
||||
</a>
|
||||
@ -154,12 +160,19 @@ export default class SceneSlate extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
<div css={STYLES_RIGHT}>
|
||||
<System.Toggle name="public" onChange={this._handleChange} active={this.state.public} />
|
||||
<System.Toggle
|
||||
name="public"
|
||||
onChange={this._handleChange}
|
||||
active={this.state.public}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<System.ButtonPrimary onClick={this._handleSave} loading={this.state.loading}>
|
||||
<System.ButtonPrimary
|
||||
onClick={this._handleSave}
|
||||
loading={this.state.loading}
|
||||
>
|
||||
Save changes
|
||||
</System.ButtonPrimary>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user