slates: creating slates

This commit is contained in:
@wwwjim 2020-07-26 18:51:51 -07:00
parent c446bf92d4
commit e70c55f4e7
12 changed files with 350 additions and 74 deletions

View File

@ -24,7 +24,7 @@ const transformAddresses = (addrsList, info) => {
});
};
const transformPeers = (peersList) => {
const transformPeers = (peersList = []) => {
return peersList.map((each) => {
return {
id: each.addrInfo.id,
@ -73,6 +73,7 @@ export const getInitialState = (props) => {
username,
storageList,
retrievalList,
slates,
} = props;
return {
@ -114,6 +115,7 @@ export const getInitialState = (props) => {
data_transfers: [],
storageList,
retrievalList,
slates,
peers: transformPeers(peersList),
addresses: transformAddresses(addrsList, info),
library,

View File

@ -11,6 +11,8 @@ export const copyText = (str) => {
el.setAttribute("readonly", "");
el.style.position = "absolute";
el.style.left = "-9999px";
el.style.visibility = "hidden";
el.style.opacity = "0";
document.body.appendChild(el);
el.select();
document.execCommand("copy");
@ -19,6 +21,27 @@ export const copyText = (str) => {
return true;
};
export const createSlug = (text) => {
if (isEmpty(text)) {
return "untitled";
}
const a = "æøåàáäâèéëêìíïîòóöôùúüûñçßÿœæŕśńṕẃǵǹḿǘẍźḧ·/_,:;";
const b = "aoaaaaaeeeeiiiioooouuuuncsyoarsnpwgnmuxzh------";
const p = new RegExp(a.split("").join("|"), "g");
return text
.toString()
.toLowerCase()
.replace(/\s+/g, "-") // Replace spaces with -
.replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special chars
.replace(/&/g, "-and-") // Replace & with 'and'
.replace(/[^\w\-]+/g, "") // Remove all non-word chars
.replace(/\-\-+/g, "-") // Replace multiple - with single -
.replace(/^-+/, "") // Trim - from start of text
.replace(/-+$/, ""); // Trim - from end of text
};
export const hexToRGBA = (hex, alpha = 1) => {
hex = hex.replace("#", "");
var r = parseInt(

View File

@ -0,0 +1,83 @@
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 { css } from "@emotion/react";
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export default class SidebarCreateSlate extends React.Component {
state = {
name: "",
loading: false,
};
_handleSubmit = async () => {
this.setState({ loading: true });
if (!Strings.isEmpty) {
alert("TODO: Provide a name");
return;
}
const response = await this.props.onSubmit({
type: "CREATE_SLATE",
name: this.state.name,
});
if (response) {
// TODO(jim): Error task.
alert(response.decorator);
}
this.setState({ loading: false });
};
_handleCancel = () => {
this.props.onCancel();
};
_handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
render() {
return (
<div>
<System.P style={{ fontFamily: Constants.font.semiBold }}>
Create Slate
</System.P>
<System.Input
containerStyle={{ marginTop: 24 }}
label="Slate name"
name="name"
value={this.state.name}
onChange={this._handleChange}
/>
<System.P style={{ marginTop: 24 }}>
This will create a new slate address at https://slate.host/@
{this.props.viewer.username}/{Strings.createSlug(this.state.name)}
</System.P>
<System.ButtonPrimaryFull
style={{ marginTop: 48 }}
onClick={this._handleSubmit}
loading={this.state.loading}
>
Create {this.state.name}
</System.ButtonPrimaryFull>
<System.ButtonSecondaryFull
style={{ marginTop: 16 }}
onClick={this._handleSubmit}
>
Cancel
</System.ButtonSecondaryFull>
</div>
);
}
}

View File

@ -295,6 +295,19 @@ export const TableContent = ({
</span>
);
});
case "SLATE_PUBLIC_TEXT_TAG":
return !text ? (
<span css={STYLES_TABLE_TAG}>Private</span>
) : (
<span
css={STYLES_TABLE_TAG}
style={{ background: Constants.system.green }}
>
{text}
</span>
);
case "TEXT_TAG":
return <span css={STYLES_TABLE_TAG}>{text}</span>;
case "FILE_DATE":
return Strings.toDate(text);

View File

@ -7,6 +7,7 @@ import getUserById from "~/node_common/data/methods/get-user-by-id";
import createSlate from "~/node_common/data/methods/create-slate";
import getSlateByName from "~/node_common/data/methods/get-slate-by-name";
import getSlateById from "~/node_common/data/methods/get-slate-by-id";
import getSlatesByUserId from "~/node_common/data/methods/get-slates-by-user-id";
import updateSlateById from "~/node_common/data/methods/update-slate-by-id";
export {
@ -20,5 +21,6 @@ export {
createSlate,
getSlateByName,
getSlateById,
getSlatesByUserId,
updateSlateById,
};

View File

@ -0,0 +1,27 @@
import { runQuery } from "~/node_common/data/utilities";
export default async ({ userId }) => {
return await runQuery({
label: "GET_SLATES_BY_USER_ID",
queryFn: async (DB) => {
const hasUser = (id) =>
DB.raw(`?? @> ?::jsonb`, ["data", JSON.stringify({ ownerId: id })]);
let query = await DB.select("*")
.from("slates")
.where(hasUser(userId));
if (!query || query.error) {
return [];
}
return query;
},
errorFn: async (e) => {
return {
error: "GET_SLATES_BY_USER_ID",
source: e,
};
},
});
};

View File

@ -16,8 +16,9 @@ export const getById = async ({ id }) => {
}
let data = null;
const response = await Data.getSlatesByUserId({ userId: id });
const slates = JSON.parse(JSON.stringify(response));
// NOTE(jim): Essential for getting the right Powergate data for a user.
try {
data = {
id: user.id,
@ -29,7 +30,7 @@ export const getById = async ({ id }) => {
library: user.data.library,
storageList: [],
retrievalList: [],
peersList: null,
slates,
messageList: null,
status: null,
addrsList: null,

View File

@ -67,10 +67,7 @@ export const refresh = async (user) => {
const status = Health.status ? Health.status : null;
const messageList = Health.messageList ? Health.messageList : null;
const Peers = await PG.net.peers();
const peersList = Peers.peersList ? Peers.peersList : null;
return { peersList, messageList, status };
return { messageList, status };
};
// NOTE(jim): Requires Powergate & authentication

View File

@ -0,0 +1,70 @@
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);
export default async (req, res) => {
initCORS(req, res);
initAuth(req, res);
const id = Utilities.getIdFromCookie(req);
if (!id) {
return res
.status(500)
.json({ decorator: "SERVER_FIND_USER_CREATE_SLATE", error: true });
}
const user = await Data.getUserById({
id,
});
if (!user) {
return res
.status(500)
.json({ decorator: "SERVER_FIND_USER_CREATE_SLATE", error: true });
}
if (user.error) {
return res
.status(500)
.json({ decorator: "SERVER_FIND_USER_CREATE_SLATE", error: true });
}
const slatename = Strings.createSlug(req.body.data.name);
const found = await Data.getSlateByName({ slatename });
console.log(found);
if (found) {
return res
.status(500)
.json({ decorator: "SERVER_EXISTING_SLATE", error: true });
}
const slate = await Data.createSlate({
slatename,
data: {
ownerId: id,
name: req.body.data.name,
objects: [],
},
});
if (!slate) {
return res
.status(500)
.json({ decorator: "SERVER_CREATE_SLATE", error: true });
}
if (slate.error) {
return res
.status(500)
.json({ decorator: "SERVER_CREATE_SLATE", error: true });
}
return res.status(200).json({ decorator: "SERVER_CREATE_SLATE", slate });
};

View File

@ -24,6 +24,7 @@ import SceneLocalData from "~/scenes/SceneLocalData";
import SceneSettingsDeveloper from "~/scenes/SceneSettingsDeveloper";
import SceneSignIn from "~/scenes/SceneSignIn";
import SidebarCreateSlate from "~/components/sidebars/SidebarCreateSlate";
import SidebarCreateWalletAddress from "~/components/sidebars/SidebarCreateWalletAddress";
import SidebarDeleteWalletAddress from "~/components/sidebars/SidebarDeleteWalletAddress";
import SidebarWalletSendFunds from "~/components/sidebars/SidebarWalletSendFunds";
@ -193,8 +194,15 @@ export default class ApplicationPage extends React.Component {
};
_handleSubmit = async (data) => {
let response;
if (data.type === "CREATE_SLATE") {
response = await Actions.createSlate({
name: data.name,
});
}
if (data.type === "CREATE_WALLET_ADDRESS") {
const address = await Actions.updateViewer({
response = await Actions.updateViewer({
type: "CREATE_FILECOIN_ADDRESS",
address: {
name: data.name,
@ -205,16 +213,20 @@ export default class ApplicationPage extends React.Component {
}
if (data.type === "SEND_WALLET_ADDRESS_FILECOIN") {
const response = await Actions.sendFilecoin({
response = await Actions.sendFilecoin({
source: data.source,
target: data.target,
amount: data.amount,
});
}
console.log({ response });
await this.rehydrate();
this._handleDismissSidebar();
return response;
};
_handleCancel = () => {
@ -399,6 +411,7 @@ export default class ApplicationPage extends React.Component {
SIDEBAR_DELETE_WALLET_ADDRESS: <SidebarDeleteWalletAddress />,
SIDEBAR_REDEEM_PAYMENT_CHANNEL: <SidebarRedeemPaymentChannel />,
SIDEBAR_ADD_FILE_TO_BUCKET: <SidebarAddFileToBucket />,
SIDEBAR_CREATE_SLATE: <SidebarCreateSlate />,
};
scenes = {

View File

@ -51,60 +51,122 @@ export default class SceneHome extends React.Component {
};
render() {
console.log(this.props.viewer.slates);
// TODO(jim): Refactor later.
const slates = {
columns: [
{ key: "id", id: "id", name: "ID" },
{ key: "slatename", name: "Slate Name", width: "228px" },
{ key: "url", name: "URL", width: "268px" },
{
key: "public",
name: "Public",
type: "SLATE_PUBLIC_TEXT_TAG",
width: "188px",
},
],
rows: this.props.viewer.slates.map((each) => {
return {
...each,
url: `https://slate.host/@${this.props.viewer.username}/${
each.slatename
}`,
public: false,
};
}),
};
// TODO(jim): Refactor later.
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: "size",
name: "Size",
width: "140px",
type: "FILE_SIZE",
},
{
key: "date",
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",
},
],
rows: this.props.viewer.library[0].children.map((each) => {
return {
...each,
button: "Store on Filecoin",
};
}),
};
// TODO(jim): Refactor later.
const dataButtons = [
{
name: "View files",
type: "NAVIGATE",
value: this.props.viewer.library[0].folderId,
},
{
name: "Upload to IPFS",
type: "SIDEBAR",
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
},
];
// TODO(jim): Refactor later.
const wallet = {
columns: [
{ key: "address", name: "Address" },
{ key: "balance", name: "Filecoin", width: "228px" },
{ key: "type", name: "Type", width: "188px", type: "TEXT_TAG" },
],
rows: this.props.viewer.addresses,
};
// TODO(jim): Refactor later.
const walletButtons = [
{
name: "View all",
type: "NAVIGATE",
value: 2,
},
];
return (
<ScenePage>
<GLRenderer width={1200} height={480} />
<Section
title="Slates"
buttons={slateButtons}
onAction={this.props.onAction}
>
<System.Table data={slates} name="slate" />
</Section>
{this.props.viewer.library[0] ? (
<Section
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
title="Recent data"
buttons={[
{
name: "View files",
type: "NAVIGATE",
value: this.props.viewer.library[0].folderId,
},
{
name: "Upload to IPFS",
type: "SIDEBAR",
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
},
]}
buttons={dataButtons}
>
<System.Table
data={{
columns: [
{ key: "name", name: "Data", type: "FILE_LINK" },
{
key: "size",
name: "Size",
width: "140px",
type: "FILE_SIZE",
},
{
key: "date",
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",
},
],
rows: this.props.viewer.library[0].children.map((each) => {
return {
...each,
button: "Store on Filecoin",
};
}),
}}
selectedRowId={this.state.data}
onChange={this._handleChange}
data={data}
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
name="data"
@ -117,25 +179,10 @@ export default class SceneHome extends React.Component {
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
title="Wallet addresses"
buttons={[
{
name: "View all",
type: "NAVIGATE",
value: 2,
},
]}
buttons={walletButtons}
>
<System.Table
data={{
columns: [
{ key: "address", name: "Address" },
{ key: "balance", name: "Filecoin", width: "228px" },
{ key: "type", name: "Type" },
],
rows: this.props.viewer.addresses,
}}
selectedRowId={this.state.transaction}
onChange={this._handleChange}
data={wallet}
onAction={this.props.onAction}
onNavigateTo={this.props.onNavigateTo}
name="transaction"

View File

@ -9,16 +9,14 @@ const db = knex(envConfig);
console.log(`RUNNING: seed-database.js`);
/*
const u = db.schema.table("slates", function(table) {
table
.integer("slatename")
.string("slatename")
.unique()
.nullable();
});
*/
Promise.all([]);
Promise.all([u]);
console.log(`FINISHED: seed-database.js`);
console.log(` CTRL +C to return to terminal.`);