slates: deeplinking

This commit is contained in:
@wwwjim 2020-08-26 21:24:49 -07:00
parent 3eca7e8946
commit ab15d09572
12 changed files with 215 additions and 33 deletions

View File

@ -46,6 +46,13 @@ export const sendFilecoin = async (data) => {
});
};
export const getSlateBySlatename = async (data) => {
return await returnJSON(`/api/search/slates/${data.query}`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
});
};
export const deleteTrustRelationship = async (data) => {
return await returnJSON(`/api/users/trust-delete`, {
...DEFAULT_OPTIONS,

View File

@ -35,9 +35,9 @@ export const getCIDGatewayURL = (cid) => {
return `https://${cid}.${Constants.gateways.ipfs}`;
};
export const createSlug = (text) => {
export const createSlug = (text, base = "untitled") => {
if (isEmpty(text)) {
return "untitled";
return base;
}
const a = "æøåàáäâèéëêìíïîòóöôùúüûñçßÿœæŕśńṕẃǵǹḿǘẍźḧ·/_,:;";

View File

@ -1,3 +1,23 @@
export const DeepLink = (props) => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
height={props.height}
style={props.style}
>
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
</svg>
);
};
export const Directory = (props) => {
return (
<svg

View File

@ -0,0 +1,33 @@
import * as React from "react";
import * as Constants from "~/common/constants";
import * as SVG from "~/components/system/svg";
import { css } from "@emotion/react";
const STYLES_BUTTON = css`
background-color: ${Constants.system.gray};
color: ${Constants.system.pitchBlack};
display: inline-flex;
width: 36px;
height: 36px;
border-radius: 36px;
background-size: cover;
background-position: 50% 50%;
transition: 100ms ease all;
align-items: center;
justify-content: center;
cursor: pointer;
user-select: none;
flex-shrink: 0;
box-shadow: 0 0 1px 4px rgba(0, 0, 0, 0.04);
:hover {
background-color: ${Constants.system.brand};
color: ${Constants.system.white};
transform: scale(1.2);
}
`;
export default (props) => {
return <span css={STYLES_BUTTON} {...props} />;
};

View File

@ -19,12 +19,6 @@ const STYLES_BUTTON = css`
cursor: pointer;
user-select: none;
flex-shrink: 0;
/*
:hover {
color: ${Constants.system.white};
background-color: ${Constants.system.brand};
}
*/
`;
export default (props) => {

View File

@ -1,13 +1,15 @@
import * as React from "react";
import * as Constants from "~/common/constants";
import * as SVG from "~/components/system/svg";
import * as Actions from "~/common/actions";
import * as OldSVG from "~/common/svg";
import { Responsive, WidthProvider } from "react-grid-layout";
import { css } from "@emotion/react";
import { LoaderSpinner } from "~/components/system/components/Loaders";
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
import CircleButtonLight from "~/components/core/CircleButtonLight";
import CircleButtonGray from "~/components/core/CircleButtonGray";
// NOTE(jim): I broke my own rules to do this. Sorry.
const STYLES_ITEM = css`
@ -22,6 +24,11 @@ const STYLES_ITEM = css`
visibility: visible;
opacity: 1;
}
img,
article {
opacity: 0.4;
}
}
`;
@ -42,10 +49,16 @@ const STYLES_BUTTON = css`
opacity: 0;
visibility: hidden;
transition: 200ms ease all;
margin: auto;
position: absolute;
top: 16px;
left: 16px;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
`;
const STYLES_ACTION_BUTTON = css`
@ -128,6 +141,36 @@ export default class Slate extends React.Component {
this.setState({ saving: "SAVED" });
};
_handleSelect = (e, index) => {
// TODO(jim): Test this again in React 17
e.preventDefault();
e.stopPropagation();
this.props.onSelect(index);
};
_handleDeepLink = async (e, object) => {
// TODO(jim): Test this again in React 17
e.preventDefault();
e.stopPropagation();
const response = await Actions.getSlateBySlatename({
query: object.deeplink,
deeplink: true,
});
if (!response.data) {
alert("TODO: Can not find deeplink");
}
if (!response.data.slate) {
alert("TODO: Can not find deeplink");
}
return window.open(
`/${response.data.slate.user.username}/${response.data.slate.slatename}`
);
};
generateDOM = () => {
return this.props.layouts.lg.map((each, index) => {
const data = this.props.items[each.i];
@ -138,13 +181,23 @@ export default class Slate extends React.Component {
return (
<div key={index} css={STYLES_ITEM}>
<SlateMediaObjectPreview type={data.type} url={data.url} />
<figure
css={STYLES_BUTTON}
onClick={() => this.props.onSelect(index)}
>
<CircleButtonLight>
<figure css={STYLES_BUTTON}>
<CircleButtonGray
style={{ marginRight: 16 }}
onMouseUp={(e) => this._handleSelect(e, index)}
onTouchEnd={(e) => this._handleSelect(e, index)}
>
<SVG.Eye height="16px" />
</CircleButtonLight>
</CircleButtonGray>
{data.deeplink ? (
<CircleButtonGray
onMouseUp={(e) => this._handleDeepLink(e, data)}
onTouchEnd={(e) => this._handleDeepLink(e, data)}
>
<OldSVG.DeepLink height="16px" />
</CircleButtonGray>
) : null}
</figure>
</div>
);

View File

@ -8,6 +8,7 @@ const STYLES_IMAGE = css`
max-width: 100%;
max-height: 100%;
pointer-events: none;
transition: 200ms ease all;
`;
const STYLES_ENTITY = css`
@ -29,22 +30,22 @@ export default class SlateMediaObjectPreview extends React.Component {
// This is a hack to catch this undefined case I don't want to track down yet.
const url = this.props.url.replace("https://undefined", "https://");
let element = <div css={STYLES_ENTITY}>No Preview</div>;
let element = <article css={STYLES_ENTITY}>No Preview</article>;
if (this.props.type && this.props.type.startsWith("video/")) {
element = <div css={STYLES_ENTITY}>Video</div>;
element = <article css={STYLES_ENTITY}>Video</article>;
}
if (this.props.type && this.props.type.startsWith("audio/")) {
element = <div css={STYLES_ENTITY}>Audio</div>;
element = <article css={STYLES_ENTITY}>Audio</article>;
}
if (this.props.type && this.props.type.startsWith("application/epub")) {
element = <div css={STYLES_ENTITY}>EPub</div>;
element = <article css={STYLES_ENTITY}>EPub</article>;
}
if (this.props.type && this.props.type.startsWith("application/pdf")) {
element = <div css={STYLES_ENTITY}>PDF</div>;
element = <article css={STYLES_ENTITY}>PDF</article>;
}
if (this.props.type && this.props.type.startsWith("image/")) {

View File

@ -143,12 +143,17 @@ export default class SlateMediaObjectSidebar extends React.Component {
body: this.props.data.body ? this.props.data.body : "",
source: this.props.data.source ? this.props.data.source : "",
author: this.props.data.author ? this.props.data.author : "",
deeplink: this.props.data.deeplink ? this.props.data.deeplink : "",
};
_handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
_handleChangeDeepLink = (e) => {
this.setState({ [e.target.name]: Strings.createSlug(e.target.value, "") });
};
render() {
const elements = [];
@ -193,7 +198,17 @@ export default class SlateMediaObjectSidebar extends React.Component {
value={this.state.author}
onChange={this._handleChange}
/>
</div>{" "}
</div>
<div
css={STYLES_SIDEBAR_SECTION}
style={{ borderTop: `1px solid #222222` }}
>
<SidebarInput
name="deeplink"
value={this.state.deeplink}
onChange={this._handleChangeDeepLink}
/>
</div>
</React.Fragment>
);

View File

@ -53,8 +53,6 @@ const STYLES_STRONG = css`
export default class SidebarAddFileToBucket extends React.Component {
_handleUpload = async (e) => {
console.log("handle upload");
e.persist();
let files = [];
let fileLoading = {};
@ -110,8 +108,6 @@ export default class SidebarAddFileToBucket extends React.Component {
}
}
console.log("file upload");
await this.props.onRehydrate({ resetFiles: true });
};

View File

@ -20,6 +20,7 @@ export default async ({ query }) => {
id: each.id,
slatename: each.slatename,
data: {
ownerId: each.data.ownerId,
name: each.data.name,
body: each.data.body,
objects: each.data.objects,

View File

@ -0,0 +1,58 @@
import * as MW from "~/node_common/middleware";
import * as Strings from "~/common/strings";
import * as Data from "~/node_common/data";
const initCORS = MW.init(MW.CORS);
const initAuth = MW.init(MW.RequireCookieAuthentication);
export default async (req, res) => {
initCORS(req, res);
initAuth(req, res);
if (Strings.isEmpty(req.query.query)) {
return {
decorator: "SERVER_SEARCH_NO_QUERY",
data: { results: [] },
};
}
const { query } = req.query;
const slates = await Data.querySlates({ query });
if (req.body.data.deeplink) {
if (slates.length) {
const slate = { ...slates[0] };
const user = await Data.getUserById({ id: slate.data.ownerId });
// NOTE(jim): I need to make sure that serializing user data is more
// straightforward, because there is sensitive data that comes back
// from this query.
slate.user = {
username: user.username,
id: user.id,
data: {
name: user.data.name,
body: user.data.body,
photo: user.data.photo,
},
};
return res.status(200).send({
decorator: "SERVER_FIND_CLOSEST_LINK",
data: { slate },
});
}
return res.status(500).send({
decorator: "SERVER_FIND_CLOSEST_LINK_ERROR",
error: true,
});
}
let results = [...slates];
return res.status(200).send({
decorator: "SERVER_SEARCH_QUERY",
data: { query, results },
});
};

View File

@ -267,15 +267,19 @@ export default class SceneSlate extends React.Component {
title={name}
actions={
<React.Fragment>
<CircleButtonLight
onClick={this._handleAdd}
<CircleButtonGray
onMouseUp={this._handleAdd}
onTouchEnd={this._handleAdd}
style={{ marginLeft: 12, marginRight: 12 }}
>
<SVG.Plus height="16px" />
</CircleButtonLight>
<CircleButtonLight onClick={this._handleShowSettings}>
</CircleButtonGray>
<CircleButtonGray
onMouseUp={this._handleShowSettings}
onTouchEnd={this._handleShowSettings}
>
<SVG.Settings height="16px" />
</CircleButtonLight>
</CircleButtonGray>
</React.Fragment>
}
>