activity feed front end basics

This commit is contained in:
Martina 2020-12-03 15:34:19 -08:00
parent b804027720
commit 73a04b9131
6 changed files with 417 additions and 32 deletions

View File

@ -293,3 +293,9 @@ export const createSupportMessage = async (data) => {
body: JSON.stringify({ data }),
});
};
export const getActivity = async () => {
return await returnJSON(`/api/activity/get`, {
...DEFAULT_OPTIONS,
});
};

View File

@ -14,9 +14,7 @@ export const deal = ({ userId, data }) => {
const userProfileURL = `https://slate.host/${data.context.username}`;
const userURL = `<${userProfileURL}|${data.context.username}>`;
const message = `*${userURL}* made a one-off storage deal with bucket "${
data.context.bucketName
}".`;
const message = `*${userURL}* made a one-off storage deal with bucket "${data.context.bucketName}".`;
Social.sendSlackMessage(message);
} catch (e) {
@ -57,7 +55,7 @@ const createSlateActivityForEachSubscriber = async ({ userId, data }) => {
Data.createActivity({
userId: s.owner_user_id,
data: {
type: "ANOTHER_USER_CREATED_SLATE",
type: "OTHER_USER_CREATE_SLATE",
actorUserId: data.actorUserId,
context: data.context,
},
@ -83,9 +81,7 @@ export const createSlate = ({ userId, data }) => {
const userProfileURL = `https://slate.host/${data.context.username}`;
const userURL = `<${userProfileURL}|${data.context.username}>`;
const message = `*${userURL}* created a slate: https://slate.host/${data.context.username}/${
data.context.slatename
}`;
const message = `*${userURL}* created a slate: https://slate.host/${data.context.username}/${data.context.slatename}`;
Social.sendSlackMessage(message);
} catch (e) {
@ -105,7 +101,7 @@ const createSlateObjectActivityForEachSubscriber = async ({ slateId, data }) =>
Data.createActivity({
userId: s.owner_user_id,
data: {
type: "ANOTHER_USER_CREATE_SLATE_OBJECT",
type: "OTHER_USER_CREATE_SLATE_OBJECT",
actorUserId: data.actorUserId,
context: data.context,
},
@ -140,12 +136,8 @@ export const createSlateObject = ({ slateId, data }) => {
const userProfileURL = `https://slate.host/${data.context.username}`;
const userURL = `<${userProfileURL}|${data.context.username}>`;
const objectURL = `<https://slate.host/${data.context.username}/${data.context.slatename}/cid:${
data.context.cid
}|${data.context.cid}>`;
const message = `*${userURL}* added ${objectURL} to https://slate.host/${
data.context.username
}/${data.context.slatename}`;
const objectURL = `<https://slate.host/${data.context.username}/${data.context.slatename}/cid:${data.context.cid}|${data.context.cid}>`;
const message = `*${userURL}* added ${objectURL} to https://slate.host/${data.context.username}/${data.context.slatename}`;
Social.sendSlackMessage(message);
} catch (e) {

40
pages/api/activity/get.js Normal file
View File

@ -0,0 +1,40 @@
import * as Utilities from "~/node_common/utilities";
import * as Data from "~/node_common/data";
import * as Strings from "~/common/strings";
export default async (req, res) => {
const id = Utilities.getIdFromCookie(req);
if (!id) {
return res.status(500).send({ decorator: "SERVER_GET_ACTIVITY", error: true });
}
const user = await Data.getUserById({
id,
});
if (!user) {
return res.status(404).send({
decorator: "SERVER_GET_ACTIVITY_USER_NOT_FOUND",
error: true,
});
}
if (user.error) {
return res.status(500).send({
decorator: "SERVER_GET_ACTIVITY_USER_NOT_FOUND",
error: true,
});
}
let response = await Data.getActivityForUserId({ userId: user.id });
if (!response) {
return res.status(404).send({ decorator: "SERVER_GET_ACTIVITY_NOT_FOUND", error: true });
}
if (response.error) {
return res.status(500).send({ decorator: "SERVER_GET_ACTIVITY_NOT_FOUND", error: true });
}
return res.status(200).send({ decorator: "SERVER_GET_SLATE", activity: response });
};

View File

@ -85,10 +85,25 @@ export default async (req, res) => {
data: {
actorUserId: user.id,
context: {
username: user.username,
slatename: slate.slatename,
url,
cid,
slate: {
id: slate.id,
slatename: slate.slatename,
name: slate.data.name,
},
user: {
id: user.id,
username: user.username,
photo: user.data.photo,
},
file: {
blurhash: each.blurhash,
id: each.id,
url,
cid,
type: each.type,
name: each.name,
title: each.title,
},
},
},
});

View File

@ -79,7 +79,18 @@ export default async (req, res) => {
userId: user.id,
data: {
actorUserId: user.id,
context: { id: user.id, username: user.username, slatename: slate.slatename },
context: {
user: {
id: user.id,
username: user.username,
photo: user.data.photo,
},
slate: {
slatename: slate.slatename,
id: slate.id,
name: slate.data.name,
},
},
},
});

View File

@ -1,10 +1,16 @@
import * as React from "react";
import * as Constants from "~/common/constants";
import * as Validations from "~/common/validations";
import * as Window from "~/common/window";
import * as SVG from "~/common/svg";
import * as Actions from "~/common/actions";
import * as Events from "~/common/custom-events";
import * as System from "~/components/system";
import { css } from "@emotion/react";
import ScenePage from "~/components/core/ScenePage";
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
import DataView from "~/components/core/DataView";
import ScenePageHeader from "~/components/core/ScenePageHeader";
@ -27,7 +33,178 @@ const STYLES_VIDEO_BIG = css`
}
`;
const STYLES_IMAGE_BOX = css`
cursor: pointer;
${"" /* background-size: cover;
background-position: 50% 50%; */}
position: relative;
box-shadow: ${Constants.shadow.light};
margin: 10px;
:hover {
box-shadow: ${Constants.shadow.medium};
}
`;
const STYLES_PROFILE_IMAGE_BOX = css`
background-size: cover;
background-position: 50% 50%;
position: relative;
border-radius: 4px;
position: absolute;
top: 16px;
left: 16px;
width: 24px;
height: 24px;
`;
const STYLES_SLATE_NAME = css`
position: absolute;
bottom: 16px;
left: 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: ${Constants.font.medium};
font-size: ${Constants.typescale.lvlN1};
color: ${Constants.system.white};
${"" /* text-shadow: 0px 0px 7px rgba(0, 0, 0, 0.5), 1px 1px 4px rgba(0, 0, 0, 0.1),
-1px -1px 4px rgba(0, 0, 0, 0.1); */}
`;
const STYLES_TITLE = css`
position: absolute;
bottom: 16px;
left: 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: ${Constants.system.white};
${"" /* text-shadow: 0px 0px 7px rgba(0, 0, 0, 0.5), 0px 0px 1px rgba(0, 0, 0, 0.3); */}
`;
const STYLES_GRADIENT = css`
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.2) 26.56%,
rgba(0, 0, 0, 0.3) 100%
);
backdrop-filter: blur(2px);
width: 100%;
height: 72px;
position: absolute;
bottom: 0px;
left: 0px;
`;
const ActivitySquare = ({ item, size }) => {
return (
<div css={STYLES_IMAGE_BOX} style={{ width: size, height: size }}>
<SlateMediaObjectPreview
centeredImage
iconOnly
blurhash={item.file.blurhash}
url={item.file.url}
title={item.file.title || item.file.name}
type={item.file.type}
style={{ border: "none" }}
imageStyle={{ border: "none" }}
/>
<div css={STYLES_GRADIENT} />
{/* <div css={STYLES_PROFILE_IMAGE_BOX} style={{ backgroundImage: `url(${item.user.photo})` }} /> */}
<div css={STYLES_SLATE_NAME} style={{ width: size }}>
<div
style={{
lineHeight: "12px",
}}
>
{item.slate.name || item.slate.slatename}
</div>
</div>
</div>
);
};
const ActivityRectangle = ({ item, size }) => {
let file = item.slate?.objects?.length ? item.slate.objects[0] : null; //need to check if it's an image, go through and find one that works
let numObjects = item.slate?.objects?.length || 0;
return (
<div css={STYLES_IMAGE_BOX} style={{ width: size * 2 + 10, height: size }}>
<SlateMediaObjectPreview
centeredImage
iconOnly
blurhash={file.blurhash}
url={file.url}
title={file.title || file.name}
type={file.type}
style={{ border: "none" }}
imageStyle={{ border: "none" }}
/>
<div css={STYLES_GRADIENT} />
{/* <div css={STYLES_PROFILE_IMAGE_BOX} style={{ backgroundImage: `url(${item.user.photo})` }} /> */}
<div css={STYLES_TITLE} style={{ width: size }}>
<div
style={{
lineHeight: "12px",
fontFamily: Constants.font.semiBold,
marginBottom: 8,
}}
>
{item.slate.name || item.slate.slatename}
</div>
<div
style={{
lineHeight: "12px",
fontFamily: Constants.font.medium,
fontSize: Constants.typescale.lvlN1,
color: Constants.system.textGrayLight,
}}
>
{numObjects} File{numObjects == 1 ? "" : "s"}
</div>
</div>
</div>
);
};
export default class SceneHome extends React.Component {
state = {
imageSize: 200,
};
async componentDidMount() {
//only fetch the last x days worth of updates maybe? or last x entries of updates?
//maybe do this when get viewer, not here. So that dont' redo every time you go back to this scene. Or maybe save it to viewer so you don't have to redo it?
console.log(this.props.viewer.activity);
this.calculateWidth();
this.debounceInstance = Window.debounce(this.calculateWidth, 200);
window.addEventListener("resize", this.debounceInstance);
let activity = this.props.viewer.activity;
let slateIds = [];
if (activity && activity.length) {
activity = activity.filter((item) => {
if (item.data.type === "OTHER_USER_CREATE_SLATE") {
slateIds.push(item.data.context.slate.id);
}
return (
item.data.type === "OTHER_USER_CREATE_SLATE" ||
item.data.type === "OTHER_USER_CREATE_SLATE_OBJECT"
);
});
}
let slates = await Actions.getSlatesByIds({ id: slateIds });
console.log(slates);
//match them up with the slates. add to a hashtable?
//remove ones with no objects
//reorder to get a nice ordering
//maybe you try and fill a row. if it fails, you try and remove a "1" square from it and move that down to the next row
}
componentWillUnmount() {
window.removeEventListener("resize", this.debounceInstance);
}
_handleCreateSlate = () => {
this.props.onAction({
type: "NAVIGATE",
@ -36,27 +213,171 @@ export default class SceneHome extends React.Component {
});
};
render() {
let hasChildren = false;
if (this.props.viewer && this.props.viewer.library[0].children.length) {
hasChildren = true;
calculateWidth = () => {
let windowWidth = window.innerWidth;
let imageSize;
if (windowWidth < Constants.sizes.mobile) {
imageSize = (windowWidth - 2 * 24 - 20) / 2;
} else {
imageSize = (windowWidth - 2 * 56 - 5 * 10) / 6;
}
this.setState({ imageSize });
};
render() {
let squareItem = {
slate: {
id: "6fef590d-6347-48bd-a262-d7fd09319c55",
slatename: "playin-around",
name: "Playin Around",
},
user: {
id: "5172dd8b-6b11-40d3-8c9f-b4cbaa0eb8e7",
username: "martina",
photo:
"https://slate.textile.io/ipfs/bafkreib2kqtibn3pt25gdmyufrsg6gnlv5iw5mncbyenfpgfwqgq5xtk3m",
},
file: {
blurhash: null,
id: "data-adae67cb-4bab-4ade-a136-2512aab47904",
url:
"https://slate.textile.io/ipfs/bafkreiajw7ucp354rb4utaryhqde4e6rn3ydyukhnwrom3wuf6hs6m7msu",
cid: "bafkreiajw7ucp354rb4utaryhqde4e6rn3ydyukhnwrom3wuf6hs6m7msu",
type: "image/jpeg",
name: "IMG_299.jpg",
title: "IMG_299.jpg",
},
};
let rectItem = {
user: {
id: "ee4817fb-5b57-4a6c-b762-9127a2cdc04f",
username: "tuna",
photo:
"https://bafybeiabcoa7egpafljp6rnfhdbz7ifhnc27hpocu7clgld5oxsbjjimri.ipfs.slate.textile.io",
},
slate: {
slatename: "Ouroboros",
id: "53548922-ba60-4dd4-8358-4a4aff9ba3f3",
name: "ouroboros",
objects: [
{
id: "data-adae67cb-4bab-4ade-a136-2512aab47904",
name: "IMG_9173.jpg",
ownerId: "3cad78ea-01ad-4c92-8983-a97524fb9e35",
size: 1697947,
title: "IMG_9173.jpg",
type: "image/jpeg",
url:
"https://slate.textile.io/ipfs/bafybeie5pyqpddco6s6pq2wypkrxfbsqr4vec532tz6kaqwpe5cn3v2bu4",
},
{
id: "data-b114d7e5-1092-4729-86c4-71a94ebe2215",
name: "IMG_6730.jpg",
ownerId: "3cad78ea-01ad-4c92-8983-a97524fb9e35",
size: 2144037,
title: "IMG_6730.jpg",
type: "image/jpeg",
url:
"https://slate.textile.io/ipfs/bafybeih6ak5oufwhzqb5iuujukvmsnlitrhmhjds6dwkw5x6eqvxs3hke4",
},
],
},
};
let squareItem2 = {
slate: {
id: "6fef590d-6347-48bd-a262-d7fd09319c55",
slatename: "looper",
name: "Looper",
},
user: {
id: "5172dd8b-6b11-40d3-8c9f-b4cbaa0eb8e7",
username: "martina",
photo:
"https://slate.textile.io/ipfs/bafkreifqrqyijknvg47uprc3rcjscavgesv6vuo7ypxwj27xqdi5aso6de",
},
file: {
blurhash: null,
id: "data-adae67cb-4bab-4ade-a136-2512aab47904",
url:
"https://slate.textile.io/ipfs/bafkreibh27gx3wmy4dxsl5px46mu4jjbemhlkbfuoszuro44atwznj7jqi",
cid: "bafkreiajw7ucp354rb4utaryhqde4e6rn3ydyukhnwrom3wuf6hs6m7msu",
type: "image/jpeg",
name: "IMG_9173.jpg",
title: "IMG_9173.jpg",
},
};
let rectItem2 = {
user: {
id: "ee4817fb-5b57-4a6c-b762-9127a2cdc04f",
username: "slate",
photo:
"https://slate.textile.io/ipfs/bafkreidc5he4qipe4dbentxtle5fao6kmeuwn7tujk5hdm7zqxc2a46uce",
},
slate: {
slatename: "Maps of the world",
id: "53548922-ba60-4dd4-8358-4a4aff9ba3f3",
name: "maps-of-the-world",
objects: [
{
id: "data-adae67cb-4bab-4ade-a136-2512aab47904",
name: "IMG_9173.jpg",
ownerId: "3cad78ea-01ad-4c92-8983-a97524fb9e35",
size: 1697947,
title: "IMG_9173.jpg",
type: "image/jpeg",
url:
"https://slate.textile.io/ipfs/bafybeigesvxcebq4b4kggvnpmgdpuxowyackzrcbuujq6t75cy7e3o6u6u",
},
{
id: "data-adae67cb-4bab-4ade-a136-2512aab47904",
name: "IMG_9173.jpg",
ownerId: "3cad78ea-01ad-4c92-8983-a97524fb9e35",
size: 1697947,
title: "IMG_9173.jpg",
type: "image/jpeg",
url:
"https://slate.textile.io/ipfs/bafybeih6ak5oufwhzqb5iuujukvmsnlitrhmhjds6dwkw5x6eqvxs3hke4",
},
{
id: "data-adae67cb-4bab-4ade-a136-2512aab47904",
name: "IMG_9173.jpg",
ownerId: "3cad78ea-01ad-4c92-8983-a97524fb9e35",
size: 1697947,
title: "IMG_9173.jpg",
type: "image/jpeg",
url:
"https://slate.textile.io/ipfs/bafybeih6ak5oufwhzqb5iuujukvmsnlitrhmhjds6dwkw5x6eqvxs3hke4",
},
{
id: "data-b114d7e5-1092-4729-86c4-71a94ebe2215",
name: "IMG_6730.jpg",
ownerId: "3cad78ea-01ad-4c92-8983-a97524fb9e35",
size: 2144037,
title: "IMG_6730.jpg",
type: "image/jpeg",
url:
"https://slate.textile.io/ipfs/bafybeih6ak5oufwhzqb5iuujukvmsnlitrhmhjds6dwkw5x6eqvxs3hke4",
},
],
},
};
//OTHER_USER_CREATE_SLATE and OTHER_USER_CREATE_SLATE_OBJECT are the ones you're looking for
return (
<ScenePage>
<ScenePageHeader title="Home">
{hasChildren
? "Welcome back! Here is your data."
{this.props.viewer.activity.length
? null
: "Welcome to Slate! You can share files with anyone in the world. Here is how it works:"}
</ScenePageHeader>
{hasChildren ? (
<div style={{ marginTop: "48px" }}>
<DataView
viewer={this.props.viewer}
items={this.props.viewer.library[0].children}
onAction={this.props.onAction}
/>
{this.props.viewer.activity.length ? (
<div
style={{ margin: "-10px", marginTop: "38px", display: "flex", flexDirection: "row" }}
>
<ActivitySquare size={this.state.imageSize} item={squareItem} />
<ActivityRectangle size={this.state.imageSize} item={rectItem} />
<ActivityRectangle size={this.state.imageSize} item={rectItem2} />
<ActivitySquare size={this.state.imageSize} item={squareItem2} />
</div>
) : (
<React.Fragment>