mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-22 12:24:02 +03:00
added standard typography styles
This commit is contained in:
parent
a01c262f6b
commit
17f1ca18cf
@ -19,7 +19,7 @@ export const init = ({ resource = "", viewer, onUpdate, onNewActiveUser = () =>
|
|||||||
console.log(`${resource}: init`);
|
console.log(`${resource}: init`);
|
||||||
|
|
||||||
if (client) {
|
if (client) {
|
||||||
console.log("ERROR: Already have client !?");
|
console.log("ERROR: Already has websocket client");
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +31,6 @@ export const init = ({ resource = "", viewer, onUpdate, onNewActiveUser = () =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
const payload = { type: "SUBSCRIBE_VIEWER", data: { id: viewer.id } };
|
const payload = { type: "SUBSCRIBE_VIEWER", data: { id: viewer.id } };
|
||||||
console.log(payload);
|
|
||||||
client.send(JSON.stringify(payload));
|
client.send(JSON.stringify(payload));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -76,7 +75,7 @@ export const init = ({ resource = "", viewer, onUpdate, onNewActiveUser = () =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type === "UPDATE") {
|
if (type === "UPDATE") {
|
||||||
onUpdate(data);
|
onUpdate({ viewer: data });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === "UPDATE_USERS_ONLINE" && typeof onNewActiveUser === "function") {
|
if (type === "UPDATE_USERS_ONLINE" && typeof onNewActiveUser === "function") {
|
||||||
@ -86,7 +85,7 @@ export const init = ({ resource = "", viewer, onUpdate, onNewActiveUser = () =>
|
|||||||
|
|
||||||
client.addEventListener("close", (e) => {
|
client.addEventListener("close", (e) => {
|
||||||
if (e.reason === "SIGN_OUT") {
|
if (e.reason === "SIGN_OUT") {
|
||||||
window.location.replace("/_");
|
window.location.replace("/_/auth");
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
client = null;
|
client = null;
|
||||||
|
@ -61,6 +61,7 @@ export const system = {
|
|||||||
active: "#00BB00",
|
active: "#00BB00",
|
||||||
blurBlack: "#262626",
|
blurBlack: "#262626",
|
||||||
bgBlurGray: "#403F42",
|
bgBlurGray: "#403F42",
|
||||||
|
grayLight2: "#AEAEB2",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const shadow = {
|
export const shadow = {
|
||||||
@ -78,6 +79,7 @@ export const zindex = {
|
|||||||
header: 4,
|
header: 4,
|
||||||
modal: 6,
|
modal: 6,
|
||||||
tooltip: 7,
|
tooltip: 7,
|
||||||
|
cta: 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const font = {
|
export const font = {
|
||||||
@ -131,3 +133,5 @@ export const filetypes = {
|
|||||||
videos: ["video/mpeg", "video/webm", "video/quicktime"],
|
videos: ["video/mpeg", "video/webm", "video/quicktime"],
|
||||||
books: ["application/pdf", "application/epub+zip", "application/vnd.amazon.ebook"],
|
books: ["application/pdf", "application/epub+zip", "application/vnd.amazon.ebook"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const linkPreviewSizeLimit = 5000000; //NOTE(martina): 5mb limit for twitter preview images
|
||||||
|
@ -1,146 +1,205 @@
|
|||||||
|
import * as Actions from "~/common/actions";
|
||||||
import * as Strings from "~/common/strings";
|
import * as Strings from "~/common/strings";
|
||||||
|
|
||||||
// NOTE(jim):
|
export const getById = (id, viewer) => {
|
||||||
// Recursion for nested entities (any number).
|
let target;
|
||||||
export const getCurrent = ({ id, data }) => {
|
|
||||||
let target = null;
|
|
||||||
let activeIds = {};
|
|
||||||
|
|
||||||
const findById = (state, id) => {
|
if (id) {
|
||||||
for (let i = 0; i < state.length; i++) {
|
target = navigation.find((each) => each.id === id);
|
||||||
if (state[i].id === id) {
|
}
|
||||||
target = state[i];
|
if (!target) {
|
||||||
activeIds[state[i].id] = true;
|
return { ...errorPage };
|
||||||
|
}
|
||||||
|
|
||||||
if (target.id === "NAV_SLATE") {
|
if (viewer && target.id === authPage.id) {
|
||||||
target.slateId = data && data.id;
|
return { ...activityPage }; //NOTE(martina): authenticated users should be redirected to the home page rather than the
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// if (!target && state[i].children) {
|
if (!viewer && !target.externalAllowed) {
|
||||||
// activeIds[state[i].id] = true;
|
return { ...authPage }; //NOTE(martina): redirect to sign in page if try to access internal page while logged out
|
||||||
// findById(state[i].children, id);
|
}
|
||||||
|
|
||||||
// if (!target) {
|
return { ...target };
|
||||||
// activeIds[state[i].id] = false;
|
};
|
||||||
// }
|
|
||||||
// }
|
export const getByHref = (href, viewer) => {
|
||||||
|
let pathname;
|
||||||
|
if (href) {
|
||||||
|
pathname = href.split("?")[0];
|
||||||
|
}
|
||||||
|
if (!pathname) {
|
||||||
|
return { page: { ...errorPage } };
|
||||||
|
}
|
||||||
|
if (pathname === "/_") {
|
||||||
|
return { page: { ...activityPage } };
|
||||||
|
}
|
||||||
|
|
||||||
|
let page = navigation.find((each) => pathname.startsWith(each.pathname));
|
||||||
|
|
||||||
|
let details;
|
||||||
|
if (page) {
|
||||||
|
page = { ...page };
|
||||||
|
if (page.id === "NAV_SLATE" || page.id === "NAV_PROFILE") {
|
||||||
|
details = {
|
||||||
|
id: pathname.replace(page.pathname, ""),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
} else {
|
||||||
|
let params = pathname.slice(1).split("/");
|
||||||
|
if (params.length === 1) {
|
||||||
|
page = { ...profilePage };
|
||||||
|
details = {
|
||||||
|
username: params[0],
|
||||||
|
};
|
||||||
|
} else if (params.length === 2) {
|
||||||
|
page = { ...slatePage };
|
||||||
|
details = {
|
||||||
|
username: params[0],
|
||||||
|
slatename: params[1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page.pathname = href;
|
||||||
|
|
||||||
findById(navigation, id);
|
let redirected = false;
|
||||||
|
|
||||||
return { target, activeIds };
|
if (viewer && page === authPage) {
|
||||||
|
redirected = true;
|
||||||
|
page = { ...activityPage };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!viewer && !page.externalAllowed) {
|
||||||
|
redirected = true;
|
||||||
|
page = { ...authPage }; //NOTE(martina): redirect to sign in page if try to access internal page while logged out
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!page) {
|
||||||
|
window.location.replace("/_/404");
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOTE(martina): to transform query params into more easily usable key value pairs in page
|
||||||
|
if (!redirected) {
|
||||||
|
let params = Strings.getParamsFromUrl(href);
|
||||||
|
if (page.id === "NAV_PROFILE" && page.cid) {
|
||||||
|
params.tab = "FILES";
|
||||||
|
}
|
||||||
|
page.params = params;
|
||||||
|
}
|
||||||
|
return { page, details };
|
||||||
|
};
|
||||||
|
|
||||||
|
const authPage = {
|
||||||
|
id: "NAV_SIGN_IN",
|
||||||
|
name: "Sign in",
|
||||||
|
pageTitle: "Sign in & Sign up",
|
||||||
|
ignore: true,
|
||||||
|
pathname: "/_/auth",
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataPage = {
|
||||||
|
id: "NAV_DATA",
|
||||||
|
name: "Files",
|
||||||
|
pageTitle: "Your Files",
|
||||||
|
pathname: "/_/data",
|
||||||
|
mainNav: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const activityPage = {
|
||||||
|
id: "NAV_ACTIVITY",
|
||||||
|
name: "Activity",
|
||||||
|
pageTitle: "Activity",
|
||||||
|
ignore: true,
|
||||||
|
externalAllowed: true,
|
||||||
|
pathname: "/_/activity",
|
||||||
|
mainNav: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const slatePage = {
|
||||||
|
id: "NAV_SLATE",
|
||||||
|
name: "Collection",
|
||||||
|
pageTitle: "A Collection",
|
||||||
|
ignore: true,
|
||||||
|
externalAllowed: true,
|
||||||
|
pathname: "/$/slate/",
|
||||||
|
};
|
||||||
|
|
||||||
|
const profilePage = {
|
||||||
|
id: "NAV_PROFILE",
|
||||||
|
name: "Profile",
|
||||||
|
pageTitle: "A Profile",
|
||||||
|
ignore: true,
|
||||||
|
externalAllowed: true,
|
||||||
|
pathname: "/$/user/",
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorPage = {
|
||||||
|
id: "NAV_ERROR",
|
||||||
|
name: "404",
|
||||||
|
pageTitle: "404 Not found",
|
||||||
|
ignore: true,
|
||||||
|
externalAllowed: true,
|
||||||
|
pathname: "/_/404",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const navigation = [
|
export const navigation = [
|
||||||
{
|
errorPage,
|
||||||
id: "NAV_DATA",
|
authPage,
|
||||||
decorator: "DATA",
|
activityPage,
|
||||||
name: "Home",
|
dataPage,
|
||||||
pageTitle: "Welcome back!",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "NAV_ACTIVITY",
|
|
||||||
decorator: "ACTIVITY",
|
|
||||||
name: "Activity",
|
|
||||||
pageTitle: "Activity",
|
|
||||||
ignore: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "NAV_EXPLORE",
|
|
||||||
decorator: "EXPLORE",
|
|
||||||
name: "Explore",
|
|
||||||
pageTitle: "Welcome back!",
|
|
||||||
ignore: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "NAV_SLATES",
|
id: "NAV_SLATES",
|
||||||
decorator: "SLATES",
|
|
||||||
name: "Collections",
|
name: "Collections",
|
||||||
pageTitle: "Collections",
|
pageTitle: "Your Collections",
|
||||||
ignore: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "NAV_SLATES_FOLLOWING",
|
|
||||||
decorator: "SLATES_FOLLOWING",
|
|
||||||
name: "Collections following",
|
|
||||||
pageTitle: "Collections following",
|
|
||||||
ignore: true,
|
ignore: true,
|
||||||
|
pathname: "/_/collections",
|
||||||
|
mainNav: true,
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// id: "NAV_SEARCH",
|
||||||
|
// name: "Search",
|
||||||
|
// pageTitle: "Search Slate",
|
||||||
|
// ignore: true,
|
||||||
|
// pathname: "/_/search",
|
||||||
|
// mainNav: true,
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
id: "NAV_DIRECTORY",
|
id: "NAV_DIRECTORY",
|
||||||
decorator: "DIRECTORY",
|
|
||||||
name: "Directory",
|
name: "Directory",
|
||||||
pageTitle: "Your directory",
|
pageTitle: "Your Following",
|
||||||
},
|
pathname: "/_/directory",
|
||||||
{
|
|
||||||
id: "NAV_DIRECTORY_FOLLOWERS",
|
|
||||||
decorator: "DIRECTORY_FOLLOWERS",
|
|
||||||
name: "Directory",
|
|
||||||
pageTitle: "Your directory",
|
|
||||||
ignore: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "NAV_SLATE",
|
|
||||||
decorator: "SLATE",
|
|
||||||
name: "Collection",
|
|
||||||
pageTitle: "Collection",
|
|
||||||
ignore: true,
|
|
||||||
},
|
},
|
||||||
|
slatePage,
|
||||||
{
|
{
|
||||||
id: "NAV_FILECOIN",
|
id: "NAV_FILECOIN",
|
||||||
decorator: "FILECOIN",
|
|
||||||
name: "Filecoin",
|
name: "Filecoin",
|
||||||
pageTitle: "Archive on Filecoin",
|
pageTitle: "Archive on Filecoin",
|
||||||
filecoin: true,
|
pathname: "/_/filecoin",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "NAV_STORAGE_DEAL",
|
id: "NAV_STORAGE_DEAL",
|
||||||
decorator: "STORAGE_DEAL",
|
|
||||||
name: "Storage Deal",
|
name: "Storage Deal",
|
||||||
filecoin: true,
|
pageTitle: "Filecoin Storage Deal",
|
||||||
pageTitle: "Make a one-off Filecoin storage deal",
|
pathname: "/_/storage-deal",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "NAV_API",
|
id: "NAV_API",
|
||||||
decorator: "API",
|
|
||||||
name: "API",
|
name: "API",
|
||||||
pageTitle: "Developer API",
|
pageTitle: "Developer API",
|
||||||
|
pathname: "/_/api",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "NAV_SETTINGS",
|
id: "NAV_SETTINGS",
|
||||||
decorator: "SETTINGS",
|
name: "Settings",
|
||||||
name: "Profile & Account Settings",
|
pageTitle: "Profile & Account Settings",
|
||||||
pageTitle: "Your Profile & Account Settings",
|
|
||||||
ignore: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "NAV_PROFILE_FILES",
|
|
||||||
decorator: "PROFILE_FILES",
|
|
||||||
name: "Profile",
|
|
||||||
pageTitle: "Profile",
|
|
||||||
ignore: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "NAV_PROFILE",
|
|
||||||
decorator: "PROFILE",
|
|
||||||
name: "Profile",
|
|
||||||
pageTitle: "Profile",
|
|
||||||
ignore: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "NAV_PROFILE_PEERS",
|
|
||||||
decorator: "PROFILE_PEERS",
|
|
||||||
name: "Profile",
|
|
||||||
pageTitle: "Profile",
|
|
||||||
ignore: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "NAV_FILE",
|
|
||||||
decorator: "FILE",
|
|
||||||
name: "File",
|
|
||||||
pageTitle: "File",
|
|
||||||
ignore: true,
|
ignore: true,
|
||||||
|
pathname: "/_/settings",
|
||||||
},
|
},
|
||||||
|
profilePage,
|
||||||
|
// {
|
||||||
|
// id: "NAV_FILE",
|
||||||
|
// name: "File",
|
||||||
|
// pageTitle: "A File",
|
||||||
|
// ignore: true,
|
||||||
|
// externalAllowed: true,
|
||||||
|
// },
|
||||||
];
|
];
|
||||||
|
@ -217,6 +217,34 @@ export const urlToCid = (url) => {
|
|||||||
.replace("hub.textile.io/ipfs/", "");
|
.replace("hub.textile.io/ipfs/", "");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getQueryStringFromParams = (params) => {
|
||||||
|
let pairs = Object.entries(params).map(([key, value]) => `${key}=${value}`);
|
||||||
|
let query = "?".concat(pairs.join("&"));
|
||||||
|
if (query.length === 1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
};
|
||||||
|
|
||||||
|
//NOTE(martina): works with both url and search passed in
|
||||||
|
export const getParamsFromUrl = (url) => {
|
||||||
|
let startIndex = url.indexOf("?") + 1;
|
||||||
|
let search = url.slice(startIndex);
|
||||||
|
if (search.length < 3) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
let params = {};
|
||||||
|
let pairs = search.split("&");
|
||||||
|
for (let pair of pairs) {
|
||||||
|
let key = pair.split("=")[0];
|
||||||
|
let value = pair.slice(key.length + 1);
|
||||||
|
if (key && value) {
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
};
|
||||||
|
|
||||||
export const hexToRGBA = (hex, alpha = 1) => {
|
export const hexToRGBA = (hex, alpha = 1) => {
|
||||||
hex = hex.replace("#", "");
|
hex = hex.replace("#", "");
|
||||||
var r = parseInt(hex.length == 3 ? hex.slice(0, 1).repeat(2) : hex.slice(0, 2), 16);
|
var r = parseInt(hex.length == 3 ? hex.slice(0, 1).repeat(2) : hex.slice(0, 2), 16);
|
||||||
|
148
common/styles.js
Normal file
148
common/styles.js
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import * as Constants from "~/common/constants";
|
||||||
|
|
||||||
|
import { css } from "@emotion/react";
|
||||||
|
|
||||||
|
/* TYPOGRAPHY */
|
||||||
|
|
||||||
|
export const HEADING_01 = css`
|
||||||
|
font-family: ${Constants.font.text};
|
||||||
|
font-size: 1.953rem;
|
||||||
|
font-weight: medium;
|
||||||
|
line-height: 1.5;
|
||||||
|
letter-spacing: -0.02px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const HEADING_02 = css`
|
||||||
|
font-family: ${Constants.font.text};
|
||||||
|
font-size: 1.563rem;
|
||||||
|
font-weight: medium;
|
||||||
|
line-height: 1.5;
|
||||||
|
letter-spacing: -0.02px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const HEADING_03 = css`
|
||||||
|
font-family: ${Constants.font.text};
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: medium;
|
||||||
|
line-height: 1.5;
|
||||||
|
letter-spacing: -0.02px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const HEADING_04 = css`
|
||||||
|
font-family: ${Constants.font.text};
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: medium;
|
||||||
|
line-height: 1.5;
|
||||||
|
letter-spacing: -0.01px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const HEADING_05 = css`
|
||||||
|
font-family: ${Constants.font.text};
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: medium;
|
||||||
|
line-height: 1.5;
|
||||||
|
letter-spacing: -0.01px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const BODY_01 = css`
|
||||||
|
font-family: ${Constants.font.text};
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: regular;
|
||||||
|
line-height: 1.5;
|
||||||
|
letter-spacing: -0.01px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const BODY_02 = css`
|
||||||
|
font-family: ${Constants.font.text};
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: regular;
|
||||||
|
line-height: 1.5;
|
||||||
|
letter-spacing: -0.01px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SMALL_TEXT = css`
|
||||||
|
font-family: ${Constants.font.text};
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1.3;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CODE_01 = css`
|
||||||
|
font-family: ${Constants.font.code};
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1.3;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CODE_02 = css`
|
||||||
|
font-family: ${Constants.font.code};
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
`;
|
||||||
|
|
||||||
|
/* FREQUENTLY USED */
|
||||||
|
|
||||||
|
export const HORIZONTAL_CONTAINER = css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const VERTICAL_CONTAINER = css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const HORIZONTAL_CONTAINER_CENTERED = css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const VERTICAL_CONTAINER_CENTERED = css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CONTAINER_CENTERED = css`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ICON_CONTAINER = css`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 4px;
|
||||||
|
margin: -4px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: ${Constants.system.black};
|
||||||
|
|
||||||
|
:hover {
|
||||||
|
color: ${Constants.system.brand};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const HOVERABLE = css`
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
:hover {
|
||||||
|
color: ${Constants.system.brand};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MOBILE_HIDDEN = css`
|
||||||
|
@media (max-width: ${Constants.sizes.mobile}px) {
|
||||||
|
display: none;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MOBILE_ONLY = css`
|
||||||
|
@media (min-width: ${Constants.sizes.mobile}px) {
|
||||||
|
display: none;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
`;
|
@ -1692,6 +1692,22 @@ export const Menu = (props) => (
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const MenuMinimal = (props) => (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
stroke="currentColor"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path d="M3.33331 6H21.3333" />
|
||||||
|
<path d="M3.33331 18H21.3333" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
export const Globe = (props) => (
|
export const Globe = (props) => (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
@ -25,7 +25,11 @@ export const authenticate = async (state) => {
|
|||||||
const jwt = cookies.get(Credentials.session.key);
|
const jwt = cookies.get(Credentials.session.key);
|
||||||
|
|
||||||
if (jwt) {
|
if (jwt) {
|
||||||
cookies.remove(Credentials.session.key);
|
cookies.remove(Credentials.session.key, {
|
||||||
|
path: "/",
|
||||||
|
maxAge: 3600 * 24 * 7,
|
||||||
|
sameSite: "strict",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = await Actions.signIn(state);
|
let response = await Actions.signIn(state);
|
||||||
@ -38,7 +42,7 @@ export const authenticate = async (state) => {
|
|||||||
// + One week.
|
// + One week.
|
||||||
// + Only requests to the same site.
|
// + Only requests to the same site.
|
||||||
// + Not using sessionStorage so the cookie doesn't leave when the browser dies.
|
// + Not using sessionStorage so the cookie doesn't leave when the browser dies.
|
||||||
cookies.set(Credentials.session.key, response.token, true, {
|
cookies.set(Credentials.session.key, response.token, {
|
||||||
path: "/",
|
path: "/",
|
||||||
maxAge: 3600 * 24 * 7,
|
maxAge: 3600 * 24 * 7,
|
||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
@ -48,7 +52,7 @@ export const authenticate = async (state) => {
|
|||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE(jim): Signs a user out and redirects to the sign in screen.
|
// NOTE(jim): Signs a user out
|
||||||
export const signOut = async ({ viewer }) => {
|
export const signOut = async ({ viewer }) => {
|
||||||
let wsclient = Websockets.getClient();
|
let wsclient = Websockets.getClient();
|
||||||
if (wsclient) {
|
if (wsclient) {
|
||||||
@ -60,10 +64,14 @@ export const signOut = async ({ viewer }) => {
|
|||||||
|
|
||||||
const jwt = cookies.get(Credentials.session.key);
|
const jwt = cookies.get(Credentials.session.key);
|
||||||
if (jwt) {
|
if (jwt) {
|
||||||
cookies.remove(Credentials.session.key);
|
cookies.remove(Credentials.session.key, {
|
||||||
|
path: "/",
|
||||||
|
maxAge: 3600 * 24 * 7,
|
||||||
|
sameSite: "strict",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.location.replace("/_");
|
window.location.replace("/_/auth");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,11 +87,11 @@ export const deleteMe = async ({ viewer }) => {
|
|||||||
|
|
||||||
await signOut({ viewer });
|
await signOut({ viewer });
|
||||||
|
|
||||||
let wsclient = Websockets.getClient();
|
// let wsclient = Websockets.getClient();
|
||||||
if (wsclient) {
|
// if (wsclient) {
|
||||||
await Websockets.deleteClient();
|
// await Websockets.deleteClient();
|
||||||
wsclient = null;
|
// wsclient = null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
@ -115,10 +115,13 @@ export class Alert extends React.Component {
|
|||||||
|
|
||||||
_handleDismissPrivacyAlert = (e) => {
|
_handleDismissPrivacyAlert = (e) => {
|
||||||
Actions.updateStatus({ status: { hidePrivacyAlert: true } });
|
Actions.updateStatus({ status: { hidePrivacyAlert: true } });
|
||||||
this.props.onUpdateViewer({
|
this.props.onAction({
|
||||||
data: {
|
type: "UDPATE_VIEWER",
|
||||||
...this.props.viewer.data,
|
viewer: {
|
||||||
status: { ...this.props.viewer.data.status, hidePrivacyAlert: true },
|
data: {
|
||||||
|
...this.props.viewer.data,
|
||||||
|
status: { ...this.props.viewer.data.status, hidePrivacyAlert: true },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -126,7 +129,7 @@ export class Alert extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
if (!this.state.message) {
|
if (!this.state.message) {
|
||||||
if (!this.props.fileLoading || !Object.keys(this.props.fileLoading).length) {
|
if (!this.props.fileLoading || !Object.keys(this.props.fileLoading).length) {
|
||||||
if (this.props.noWarning) {
|
if (this.props.noWarning || !this.props.viewer) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import * as React from "react";
|
|||||||
import * as NavigationData from "~/common/navigation-data";
|
import * as NavigationData from "~/common/navigation-data";
|
||||||
import * as Actions from "~/common/actions";
|
import * as Actions from "~/common/actions";
|
||||||
import * as Strings from "~/common/strings";
|
import * as Strings from "~/common/strings";
|
||||||
import * as State from "~/common/state";
|
import * as Styles from "~/common/styles";
|
||||||
import * as Credentials from "~/common/credentials";
|
import * as Credentials from "~/common/credentials";
|
||||||
import * as Constants from "~/common/constants";
|
import * as Constants from "~/common/constants";
|
||||||
import * as Validations from "~/common/validations";
|
import * as Validations from "~/common/validations";
|
||||||
@ -15,6 +15,7 @@ import * as Events from "~/common/custom-events";
|
|||||||
|
|
||||||
// NOTE(jim):
|
// NOTE(jim):
|
||||||
// Scenes each have an ID and can be navigated to with _handleAction
|
// Scenes each have an ID and can be navigated to with _handleAction
|
||||||
|
import SceneError from "~/scenes/SceneError";
|
||||||
import SceneEditAccount from "~/scenes/SceneEditAccount";
|
import SceneEditAccount from "~/scenes/SceneEditAccount";
|
||||||
import SceneFile from "~/scenes/SceneFile";
|
import SceneFile from "~/scenes/SceneFile";
|
||||||
import SceneFilesFolder from "~/scenes/SceneFilesFolder";
|
import SceneFilesFolder from "~/scenes/SceneFilesFolder";
|
||||||
@ -49,6 +50,7 @@ import SidebarEditTags from "~/components/sidebars/SidebarEditTags";
|
|||||||
import ApplicationHeader from "~/components/core/ApplicationHeader";
|
import ApplicationHeader from "~/components/core/ApplicationHeader";
|
||||||
import ApplicationLayout from "~/components/core/ApplicationLayout";
|
import ApplicationLayout from "~/components/core/ApplicationLayout";
|
||||||
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
|
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
|
||||||
|
import CTATransition from "~/components/core/CTATransition";
|
||||||
|
|
||||||
import { GlobalModal } from "~/components/system/components/GlobalModal";
|
import { GlobalModal } from "~/components/system/components/GlobalModal";
|
||||||
import { OnboardingModal } from "~/components/core/OnboardingModal";
|
import { OnboardingModal } from "~/components/core/OnboardingModal";
|
||||||
@ -56,6 +58,7 @@ import { SearchModal } from "~/components/core/SearchModal";
|
|||||||
import { Alert } from "~/components/core/Alert";
|
import { Alert } from "~/components/core/Alert";
|
||||||
import { announcements } from "~/components/core/OnboardingModal";
|
import { announcements } from "~/components/core/OnboardingModal";
|
||||||
import { Logo } from "~/common/logo";
|
import { Logo } from "~/common/logo";
|
||||||
|
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||||
|
|
||||||
const SIDEBARS = {
|
const SIDEBARS = {
|
||||||
SIDEBAR_FILECOIN_ARCHIVE: <SidebarFilecoinArchive />,
|
SIDEBAR_FILECOIN_ARCHIVE: <SidebarFilecoinArchive />,
|
||||||
@ -73,23 +76,20 @@ const SIDEBARS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SCENES = {
|
const SCENES = {
|
||||||
ACTIVITY: <SceneActivity tab={0} />,
|
NAV_ERROR: <SceneError />,
|
||||||
EXPLORE: <SceneActivity tab={1} />,
|
NAV_SIGN_IN: <SceneSignIn />,
|
||||||
DIRECTORY: <SceneDirectory />,
|
NAV_ACTIVITY: <SceneActivity />,
|
||||||
PROFILE_FILES: <SceneProfile tab={0} />,
|
NAV_DIRECTORY: <SceneDirectory />,
|
||||||
PROFILE: <SceneProfile tab={1} />,
|
NAV_PROFILE: <SceneProfile />,
|
||||||
PROFILE_PEERS: <SceneProfile tab={2} />,
|
NAV_DATA: <SceneFilesFolder />,
|
||||||
DATA: <SceneFilesFolder />,
|
// NAV_FILE: <SceneFile />,
|
||||||
FILE: <SceneFile />,
|
NAV_SLATE: <SceneSlate />,
|
||||||
SLATE: <SceneSlate />,
|
NAV_API: <SceneSettingsDeveloper />,
|
||||||
API: <SceneSettingsDeveloper />,
|
NAV_SETTINGS: <SceneEditAccount />,
|
||||||
SETTINGS: <SceneEditAccount />,
|
NAV_SLATES: <SceneSlates />,
|
||||||
SLATES: <SceneSlates tab={0} />,
|
NAV_DIRECTORY: <SceneDirectory />,
|
||||||
SLATES_FOLLOWING: <SceneSlates tab={1} />,
|
NAV_FILECOIN: <SceneArchive />,
|
||||||
DIRECTORY: <SceneDirectory tab={0} />,
|
NAV_STORAGE_DEAL: <SceneMakeFilecoinDeal />,
|
||||||
DIRECTORY_FOLLOWERS: <SceneDirectory tab={1} />,
|
|
||||||
FILECOIN: <SceneArchive />,
|
|
||||||
STORAGE_DEAL: <SceneMakeFilecoinDeal />,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mounted;
|
let mounted;
|
||||||
@ -100,15 +100,18 @@ export default class ApplicationPage extends React.Component {
|
|||||||
state = {
|
state = {
|
||||||
selected: {},
|
selected: {},
|
||||||
viewer: this.props.viewer,
|
viewer: this.props.viewer,
|
||||||
data: null,
|
page: this.props.page || {},
|
||||||
|
data: this.props.data,
|
||||||
|
activePage: this.props.page?.id,
|
||||||
sidebar: null,
|
sidebar: null,
|
||||||
online: null,
|
online: null,
|
||||||
isMobile: this.props.isMobile,
|
isMobile: this.props.isMobile,
|
||||||
loaded: false,
|
|
||||||
activeUsers: null,
|
activeUsers: null,
|
||||||
|
loading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
this._handleWindowResize();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -128,8 +131,9 @@ export default class ApplicationPage extends React.Component {
|
|||||||
if (this.state.viewer) {
|
if (this.state.viewer) {
|
||||||
await this._handleSetupWebsocket();
|
await this._handleSetupWebsocket();
|
||||||
}
|
}
|
||||||
|
console.log(this.props.page);
|
||||||
this._handleURLRedirect();
|
console.log(this.props.data);
|
||||||
|
// this._handleBackForward();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@ -160,17 +164,9 @@ export default class ApplicationPage extends React.Component {
|
|||||||
|
|
||||||
this._handleRegisterFileLoading({ fileLoading });
|
this._handleRegisterFileLoading({ fileLoading });
|
||||||
|
|
||||||
let page;
|
const page = this.state.page;
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
page = window?.history?.state;
|
|
||||||
}
|
|
||||||
if (!page || !page.id) {
|
|
||||||
page = { id: "NAV_DATA", scrollTop: 0, data: null };
|
|
||||||
}
|
|
||||||
const current = NavigationData.getCurrent(page);
|
|
||||||
|
|
||||||
let slate = null;
|
let slate = null;
|
||||||
if (current.target.id === "NAV_SLATE" && this.state.data?.ownerId === this.state.viewer?.id) {
|
if (page?.id === "NAV_SLATE" && this.state.data?.ownerId === this.state.viewer?.id) {
|
||||||
slate = this.state.data;
|
slate = this.state.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +177,8 @@ export default class ApplicationPage extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleUpdateViewer = (newViewerState, callback) => {
|
_handleUpdateViewer = ({ viewer, callback }) => {
|
||||||
|
// _handleUpdateViewer = (newViewerState, callback) => {
|
||||||
// let setAsyncState = (newState) =>
|
// let setAsyncState = (newState) =>
|
||||||
// new Promise((resolve) =>
|
// new Promise((resolve) =>
|
||||||
// this.setState(
|
// this.setState(
|
||||||
@ -194,17 +191,11 @@ export default class ApplicationPage extends React.Component {
|
|||||||
// await setAsyncState(newViewerState);
|
// await setAsyncState(newViewerState);
|
||||||
|
|
||||||
//NOTE(martina): if updating viewer affects this.state.data (e.g. you're viewing your own slate), update data as well
|
//NOTE(martina): if updating viewer affects this.state.data (e.g. you're viewing your own slate), update data as well
|
||||||
if (newViewerState.slates?.length) {
|
if (viewer?.slates?.length) {
|
||||||
let page;
|
const page = this.state.page;
|
||||||
if (typeof window !== "undefined") {
|
if (page?.id === "NAV_SLATE" && this.state.data?.ownerId === this.state.viewer.id) {
|
||||||
page = window?.history?.state;
|
|
||||||
}
|
|
||||||
if (!page || !page.id) {
|
|
||||||
page = { id: "NAV_DATA", scrollTop: 0, data: null };
|
|
||||||
}
|
|
||||||
if (page.id === "NAV_SLATE" && this.state.data?.ownerId === this.state.viewer.id) {
|
|
||||||
let data = this.state.data;
|
let data = this.state.data;
|
||||||
for (let slate of newViewerState.slates) {
|
for (let slate of viewer.slates) {
|
||||||
if (slate.id === data.id) {
|
if (slate.id === data.id) {
|
||||||
data = slate;
|
data = slate;
|
||||||
break;
|
break;
|
||||||
@ -212,13 +203,14 @@ export default class ApplicationPage extends React.Component {
|
|||||||
}
|
}
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
viewer: { ...this.state.viewer, ...newViewerState },
|
viewer: { ...this.state.viewer, ...viewer },
|
||||||
data,
|
data,
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
console.log(this.state.viewer);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -226,9 +218,10 @@ export default class ApplicationPage extends React.Component {
|
|||||||
}
|
}
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
viewer: { ...this.state.viewer, ...newViewerState },
|
viewer: { ...this.state.viewer, ...viewer },
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
|
console.log(this.state.viewer);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -236,7 +229,8 @@ export default class ApplicationPage extends React.Component {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleUpdateData = (data, callback) => {
|
_handleUpdateData = ({ data, callback }) => {
|
||||||
|
// _handleUpdateData = (data, callback) => {
|
||||||
//TODO(martina): maybe add a default window.history.replacestate where it pushes the new data to browser?
|
//TODO(martina): maybe add a default window.history.replacestate where it pushes the new data to browser?
|
||||||
this.setState({ data }, () => {
|
this.setState({ data }, () => {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
@ -256,7 +250,6 @@ export default class ApplicationPage extends React.Component {
|
|||||||
console.log("WEBSOCKET: NOT AUTHENTICATED");
|
console.log("WEBSOCKET: NOT AUTHENTICATED");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("inside handle setup websocket in application.js");
|
|
||||||
wsclient = Websockets.init({
|
wsclient = Websockets.init({
|
||||||
resource: this.props.resources.pubsub,
|
resource: this.props.resources.pubsub,
|
||||||
viewer: this.state.viewer,
|
viewer: this.state.viewer,
|
||||||
@ -311,17 +304,10 @@ export default class ApplicationPage extends React.Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let page;
|
let page = this.state.page;
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
page = window?.history?.state;
|
|
||||||
}
|
|
||||||
if (!page || !page.id) {
|
|
||||||
page = { id: "NAV_DATA", scrollTop: 0, data: null };
|
|
||||||
}
|
|
||||||
const current = NavigationData.getCurrent(page);
|
|
||||||
|
|
||||||
let slate = null;
|
let slate = null;
|
||||||
if (current.target.id === "NAV_SLATE" && this.state.data?.ownerId === this.state.viewer?.id) {
|
if (page?.id === "NAV_SLATE" && this.state.data?.ownerId === this.state.viewer?.id) {
|
||||||
slate = this.state.data;
|
slate = this.state.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,17 +471,17 @@ export default class ApplicationPage extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._handleAuthenticate(state, true);
|
this._handleAuthenticate(state, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleAuthenticate = async (state, newAccount) => {
|
_handleAuthenticate = async (state, newAccount) => {
|
||||||
let response = await UserBehaviors.authenticate(state);
|
let response = await UserBehaviors.authenticate(state);
|
||||||
if (!response) {
|
if (Events.hasError(response)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let viewer = await UserBehaviors.hydrate();
|
let viewer = await UserBehaviors.hydrate();
|
||||||
if (!viewer) {
|
if (Events.hasError(viewer)) {
|
||||||
return viewer;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ viewer });
|
this.setState({ viewer });
|
||||||
@ -507,7 +493,6 @@ export default class ApplicationPage extends React.Component {
|
|||||||
unseenAnnouncements.push(feature);
|
unseenAnnouncements.push(feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(unseenAnnouncements);
|
|
||||||
|
|
||||||
if (newAccount || unseenAnnouncements.length) {
|
if (newAccount || unseenAnnouncements.length) {
|
||||||
Events.dispatchCustomEvent({
|
Events.dispatchCustomEvent({
|
||||||
@ -530,28 +515,28 @@ export default class ApplicationPage extends React.Component {
|
|||||||
Actions.updateSearch("create-user");
|
Actions.updateSearch("create-user");
|
||||||
}
|
}
|
||||||
|
|
||||||
let redirected = this._handleURLRedirect();
|
// let redirected = this._handleURLRedirect();
|
||||||
if (!redirected) {
|
// if (!redirected) {
|
||||||
this._handleAction({ type: "NAVIGATE", value: "NAV_DATA" });
|
// this._handleAction({ type: "NAVIGATE", value: "NAV_DATA" });
|
||||||
}
|
// }
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleURLRedirect = () => {
|
// _handleURLRedirect = () => {
|
||||||
const id = Window.getQueryParameterByName("scene");
|
// const id = Window.getQueryParameterByName("scene");
|
||||||
const user = Window.getQueryParameterByName("user");
|
// const username = Window.getQueryParameterByName("username");
|
||||||
const slate = Window.getQueryParameterByName("slate");
|
// const slatename = Window.getQueryParameterByName("slatename");
|
||||||
const cid = Window.getQueryParameterByName("cid");
|
// const cid = Window.getQueryParameterByName("cid");
|
||||||
|
|
||||||
if (!Strings.isEmpty(id) && this.state.viewer) {
|
// if (!Strings.isEmpty(id)) {
|
||||||
this._handleNavigateTo({ id, user, slate, cid }, null, true);
|
// this._handleNavigateTo({ id, username, slatename, cid }, null, true);
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
if (!this.state.loaded) {
|
// if (!this.state.loaded) {
|
||||||
this.setState({ loaded: true });
|
// this.setState({ loaded: true });
|
||||||
}
|
// }
|
||||||
return false;
|
// return false;
|
||||||
};
|
// };
|
||||||
|
|
||||||
_handleSelectedChange = (e) => {
|
_handleSelectedChange = (e) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -565,30 +550,15 @@ export default class ApplicationPage extends React.Component {
|
|||||||
|
|
||||||
_handleAction = (options) => {
|
_handleAction = (options) => {
|
||||||
if (options.type === "NAVIGATE") {
|
if (options.type === "NAVIGATE") {
|
||||||
// NOTE(martina): The `scene` property is only necessary when you need to display a component different from the one corresponding to the tab it appears in
|
return this._handleNavigateTo(options);
|
||||||
// + e.g. to display <SceneProfile/> while on the Home tab
|
|
||||||
// + `scene` should be the decorator of the component you want displayed
|
|
||||||
return this._handleNavigateTo(
|
|
||||||
{
|
|
||||||
...options,
|
|
||||||
id: options.value,
|
|
||||||
redirect: null,
|
|
||||||
},
|
|
||||||
options.data,
|
|
||||||
options.redirect
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.type === "NEW_WINDOW") {
|
if (options.type === "UPDATE_VIEWER") {
|
||||||
return window.open(options.value);
|
return this._handleUpdateViewer(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.type === "ACTION") {
|
if (options.type === "UPDATE_PARAMS") {
|
||||||
Events.dispatchMessage({ message: JSON.stringify(options), status: "INFO" });
|
return this._handleUpdatePageParams(options);
|
||||||
}
|
|
||||||
|
|
||||||
if (options.type === "DOWNLOAD") {
|
|
||||||
Events.dispatchMessage({ message: JSON.stringify(options), status: "INFO" });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.type === "SIDEBAR") {
|
if (options.type === "SIDEBAR") {
|
||||||
@ -602,125 +572,167 @@ export default class ApplicationPage extends React.Component {
|
|||||||
return this._handleRegisterFileCancelled({ key: options.value });
|
return this._handleRegisterFileCancelled({ key: options.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
return alert(JSON.stringify(options));
|
if (options.type === "NEW_WINDOW") {
|
||||||
};
|
return window.open(options.value);
|
||||||
|
|
||||||
_handleNavigateTo = (next, data = null, redirect = false) => {
|
|
||||||
if (redirect) {
|
|
||||||
window.history.replaceState(
|
|
||||||
{ ...next, data },
|
|
||||||
"Slate",
|
|
||||||
`/_${next.id ? `?scene=${next.id}` : ""}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
window.history.pushState(
|
|
||||||
{ ...next, data },
|
|
||||||
"Slate",
|
|
||||||
`/_${next.id ? `?scene=${next.id}` : ""}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.state.loaded) {
|
console.log("Error: Failed to _handleAction because TYPE did not match any known actions");
|
||||||
this.setState({ loaded: true });
|
};
|
||||||
|
|
||||||
|
_handleNavigateTo = async ({ href, redirect = false, popstate = false }) => {
|
||||||
|
const { page, details } = NavigationData.getByHref(href, this.state.viewer);
|
||||||
|
|
||||||
|
Events.dispatchCustomEvent({ name: "slate-global-close-carousel", detail: {} });
|
||||||
|
|
||||||
|
if (redirect || popstate) {
|
||||||
|
window.history.replaceState(null, "Slate", page.pathname);
|
||||||
|
} else {
|
||||||
|
window.history.pushState(null, "Slate", page.pathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = { data: null, sidebar: null, page };
|
||||||
|
if (!next.ignore) {
|
||||||
|
state.activePage = page.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = document.documentElement || document.body;
|
let body = document.documentElement || document.body;
|
||||||
if (data) {
|
if (page.id === "NAV_SLATE" || page.id === "NAV_PROFILE") {
|
||||||
this.setState(
|
state.loading = true;
|
||||||
{
|
|
||||||
data,
|
|
||||||
sidebar: null,
|
|
||||||
},
|
|
||||||
() => body.scrollTo(0, 0)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
sidebar: null,
|
|
||||||
},
|
|
||||||
() => body.scrollTo(0, 0)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
this.setState(state, () => {
|
||||||
|
if (!popstate) {
|
||||||
|
body.scrollTo(0, 0);
|
||||||
|
}
|
||||||
|
if (page.id === "NAV_SLATE" || page.id === "NAV_PROFILE") {
|
||||||
|
this.updateDataAndPathname({ page, details });
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleBackForward = (e) => {
|
updateDataAndPathname = async ({ page, details }) => {
|
||||||
let page = window.history.state;
|
let pathname = page.pathname.split("?")[0];
|
||||||
this.setState({
|
let search = Strings.getQueryStringFromParams(page.params);
|
||||||
sidebar: null,
|
let data;
|
||||||
data: page.data,
|
if (page?.id === "NAV_SLATE") {
|
||||||
|
let response = await Actions.getSerializedSlate(details);
|
||||||
|
if (!response || response.error) {
|
||||||
|
this.setState({ loading: false });
|
||||||
|
this._handleNavigateTo({ href: "/_/404", redirect: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data = response.data;
|
||||||
|
pathname = `/${data.user.username}/${data.slatename}${search}`;
|
||||||
|
} else if (page?.id === "NAV_PROFILE") {
|
||||||
|
let response = await Actions.getSerializedProfile(details);
|
||||||
|
if (!response || response.error) {
|
||||||
|
this.setState({ loading: false });
|
||||||
|
this._handleNavigateTo({ href: "/_/404", redirect: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data = response.data;
|
||||||
|
pathname = `/${data.username}${search}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ data, loading: false });
|
||||||
|
|
||||||
|
window.history.replaceState(null, "Slate", pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
_handleUpdatePageParams = ({ params, callback, redirect = false }) => {
|
||||||
|
let query = Strings.getQueryStringFromParams(params);
|
||||||
|
const href = window.location.pathname.concat(query);
|
||||||
|
if (redirect) {
|
||||||
|
window.history.replaceState(null, "Slate", href);
|
||||||
|
} else {
|
||||||
|
window.history.pushState(null, "Slate", href);
|
||||||
|
}
|
||||||
|
this.setState({ page: { ...this.state.page, params } }, () => {
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Events.dispatchCustomEvent({ name: "slate-global-close-carousel", detail: {} });
|
};
|
||||||
|
|
||||||
|
// _handleUpdatePageParams = ({ search, callback }) => {
|
||||||
|
// if (!search.length) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// let target = {};
|
||||||
|
// let searchParams = search.replace("?", "");
|
||||||
|
// let pairs = searchParams.split("&");
|
||||||
|
// for (let pair of pairs) {
|
||||||
|
// let key = pair.split("=")[0];
|
||||||
|
// let value = pair.slice(key.length + 1);
|
||||||
|
// if (key && value) {
|
||||||
|
// target[key] = value;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// const href = window.location.pathname + "?" + searchParams;
|
||||||
|
// if (target) {
|
||||||
|
// window.history.replaceState(null, "Slate", href);
|
||||||
|
// this.setState({ page: target }, () => {
|
||||||
|
// if (callback) {
|
||||||
|
// callback();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// window.history.replaceState(null, "Slate", href);
|
||||||
|
// if (callback) {
|
||||||
|
// callback();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
_handleBackForward = () => {
|
||||||
|
let href = window.location.pathname.concat(
|
||||||
|
window.location.search ? `${window.location.search}` : ""
|
||||||
|
);
|
||||||
|
this._handleNavigateTo({ href, popstate: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// NOTE(jim): Not authenticated.
|
let page = this.state.page;
|
||||||
if (!this.state.viewer) {
|
if (!page?.id) {
|
||||||
return (
|
page = NavigationData.getById(null, this.state.viewer);
|
||||||
<WebsitePrototypeWrapper
|
}
|
||||||
title="Slate: sign in"
|
let headerElement;
|
||||||
description="Sign in to your Slate account to manage your assets."
|
if (page.id !== "NAV_SIGN_IN") {
|
||||||
url="https://slate.host/_"
|
headerElement = (
|
||||||
>
|
<ApplicationHeader
|
||||||
<Alert noWarning style={{ top: 0, zIndex: Constants.zindex.sidebar }} />
|
viewer={this.state.viewer}
|
||||||
<SceneSignIn
|
navigation={NavigationData.navigation}
|
||||||
onCreateUser={this._handleCreateUser}
|
page={page}
|
||||||
onAuthenticate={this._handleAuthenticate}
|
onAction={this._handleAction}
|
||||||
onAction={this._handleAction}
|
isMobile={this.state.isMobile}
|
||||||
/>
|
isMac={this.props.isMac}
|
||||||
</WebsitePrototypeWrapper>
|
activePage={this.state.activePage}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// NOTE(jim): Authenticated.
|
|
||||||
let page;
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
page = window?.history?.state;
|
|
||||||
}
|
|
||||||
if (!page || !page.id) {
|
|
||||||
page = { id: "NAV_DATA", scrollTop: 0, data: null };
|
|
||||||
}
|
|
||||||
const current = NavigationData.getCurrent(page);
|
|
||||||
|
|
||||||
// NOTE(jim): Only happens during a bad query parameter.
|
const scene = React.cloneElement(SCENES[page.id], {
|
||||||
if (!current.target) {
|
|
||||||
window.location.replace("/_");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let headerElement = (
|
|
||||||
<ApplicationHeader
|
|
||||||
viewer={this.state.viewer}
|
|
||||||
navigation={NavigationData.navigation}
|
|
||||||
activeIds={current.activeIds}
|
|
||||||
onAction={this._handleAction}
|
|
||||||
isMobile={this.state.isMobile}
|
|
||||||
isMac={this.props.isMac}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const scene = React.cloneElement(SCENES[page.scene || current.target.decorator], {
|
|
||||||
key: this.state.data?.id,
|
key: this.state.data?.id,
|
||||||
page: page,
|
page: page,
|
||||||
current: current.target,
|
|
||||||
data: this.state.data,
|
data: this.state.data,
|
||||||
viewer: this.state.viewer,
|
viewer: this.state.viewer,
|
||||||
selected: this.state.selected,
|
selected: this.state.selected,
|
||||||
onSelectedChange: this._handleSelectedChange,
|
onSelectedChange: this._handleSelectedChange,
|
||||||
|
onAuthenticate: this._handleAuthenticate,
|
||||||
|
onCreateUser: this._handleCreateUser,
|
||||||
onAction: this._handleAction,
|
onAction: this._handleAction,
|
||||||
onUpload: this._handleUploadFiles,
|
onUpload: this._handleUploadFiles,
|
||||||
onUpdateData: this._handleUpdateData,
|
|
||||||
onUpdateViewer: this._handleUpdateViewer,
|
|
||||||
sceneId: current.target.id,
|
|
||||||
isMobile: this.state.isMobile,
|
isMobile: this.state.isMobile,
|
||||||
isMac: this.props.isMac,
|
isMac: this.props.isMac,
|
||||||
resources: this.props.resources,
|
resources: this.props.resources,
|
||||||
activeUsers: this.state.activeUsers,
|
activeUsers: this.state.activeUsers,
|
||||||
userBucketCID: this.state.userBucketCID,
|
userBucketCID: this.state.userBucketCID,
|
||||||
|
external: !!!this.state.viewer,
|
||||||
});
|
});
|
||||||
|
|
||||||
let sidebarElement;
|
let sidebarElement;
|
||||||
if (this.state.sidebar) {
|
if (this.state.sidebar) {
|
||||||
sidebarElement = React.cloneElement(this.state.sidebar, {
|
sidebarElement = React.cloneElement(this.state.sidebar, {
|
||||||
current: current.target,
|
page: page,
|
||||||
selected: this.state.selected,
|
selected: this.state.selected,
|
||||||
viewer: this.state.viewer,
|
viewer: this.state.viewer,
|
||||||
data: this.state.data,
|
data: this.state.data,
|
||||||
@ -730,34 +742,30 @@ export default class ApplicationPage extends React.Component {
|
|||||||
onCancel: this._handleDismissSidebar,
|
onCancel: this._handleDismissSidebar,
|
||||||
onUpload: this._handleUploadFiles,
|
onUpload: this._handleUploadFiles,
|
||||||
onAction: this._handleAction,
|
onAction: this._handleAction,
|
||||||
onUpdateViewer: this._handleUpdateViewer,
|
|
||||||
resources: this.props.resources,
|
resources: this.props.resources,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = `Slate : ${current.target.pageTitle}`;
|
const title = `Slate: ${page.pageTitle}`;
|
||||||
const description = "";
|
const description = "";
|
||||||
const url = "https://slate.host/_";
|
const url = "https://slate.host/_";
|
||||||
|
|
||||||
// console.log("application state:", { target: current.target });
|
// if (!this.state.loaded) {
|
||||||
// console.log("application state:", { data: this.state.data });
|
// return (
|
||||||
|
// <WebsitePrototypeWrapper description={description} title={title} url={url}>
|
||||||
if (!this.state.loaded) {
|
// <div
|
||||||
return (
|
// style={{
|
||||||
<WebsitePrototypeWrapper description={description} title={title} url={url}>
|
// height: "100vh",
|
||||||
<div
|
// display: "flex",
|
||||||
style={{
|
// alignItems: "center",
|
||||||
height: "100vh",
|
// justifyContent: "center",
|
||||||
display: "flex",
|
// }}
|
||||||
alignItems: "center",
|
// >
|
||||||
justifyContent: "center",
|
// <Logo style={{ width: "20vw", maxWidth: "200px" }} />
|
||||||
}}
|
// </div>
|
||||||
>
|
// </WebsitePrototypeWrapper>
|
||||||
<Logo style={{ width: "20vw", maxWidth: "200px" }} />
|
// );
|
||||||
</div>
|
// }
|
||||||
</WebsitePrototypeWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<WebsitePrototypeWrapper description={description} title={title} url={url}>
|
<WebsitePrototypeWrapper description={description} title={title} url={url}>
|
||||||
@ -767,13 +775,23 @@ export default class ApplicationPage extends React.Component {
|
|||||||
sidebar={sidebarElement}
|
sidebar={sidebarElement}
|
||||||
onDismissSidebar={this._handleDismissSidebar}
|
onDismissSidebar={this._handleDismissSidebar}
|
||||||
fileLoading={this.state.fileLoading}
|
fileLoading={this.state.fileLoading}
|
||||||
filecoin={current.target.filecoin}
|
|
||||||
isMobile={this.state.isMobile}
|
isMobile={this.state.isMobile}
|
||||||
isMac={this.props.isMac}
|
isMac={this.props.isMac}
|
||||||
viewer={this.state.viewer}
|
viewer={this.state.viewer}
|
||||||
onUpdateViewer={this._handleUpdateViewer}
|
|
||||||
>
|
>
|
||||||
{scene}
|
{this.state.loading ? (
|
||||||
|
<div
|
||||||
|
css={Styles.CONTAINER_CENTERED}
|
||||||
|
style={{
|
||||||
|
width: "100vw",
|
||||||
|
height: "100vh",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LoaderSpinner style={{ height: 32, width: 32 }} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
scene
|
||||||
|
)}
|
||||||
</ApplicationLayout>
|
</ApplicationLayout>
|
||||||
<GlobalModal />
|
<GlobalModal />
|
||||||
<SearchModal
|
<SearchModal
|
||||||
@ -782,7 +800,8 @@ export default class ApplicationPage extends React.Component {
|
|||||||
isMobile={this.props.isMobile}
|
isMobile={this.props.isMobile}
|
||||||
resourceURI={this.props.resources.search}
|
resourceURI={this.props.resources.search}
|
||||||
/>
|
/>
|
||||||
{!this.state.loaded ? (
|
<CTATransition onAction={this._handleAction} />
|
||||||
|
{/* {!this.state.loaded ? (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
@ -795,7 +814,7 @@ export default class ApplicationPage extends React.Component {
|
|||||||
>
|
>
|
||||||
<Logo style={{ width: "20vw", maxWidth: "200px" }} />
|
<Logo style={{ width: "20vw", maxWidth: "200px" }} />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null} */}
|
||||||
</WebsitePrototypeWrapper>
|
</WebsitePrototypeWrapper>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
@ -2,88 +2,80 @@ import * as React from "react";
|
|||||||
import * as Constants from "~/common/constants";
|
import * as Constants from "~/common/constants";
|
||||||
import * as SVG from "~/common/svg";
|
import * as SVG from "~/common/svg";
|
||||||
import * as Events from "~/common/custom-events";
|
import * as Events from "~/common/custom-events";
|
||||||
|
import * as Styles from "~/common/styles";
|
||||||
|
|
||||||
import ApplicationUserControls from "~/components/core/ApplicationUserControls";
|
import {
|
||||||
|
ApplicationUserControls,
|
||||||
|
ApplicationUserControlsPopup,
|
||||||
|
} from "~/components/core/ApplicationUserControls";
|
||||||
|
|
||||||
import { css, keyframes } from "@emotion/react";
|
import { css, keyframes } from "@emotion/react";
|
||||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
||||||
import { PopoverNavigation } from "~/components/system";
|
import { PopoverNavigation } from "~/components/system";
|
||||||
|
import { Logo, Symbol } from "~/common/logo";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
import { ButtonPrimary, ButtonTertiary } from "~/components/system/components/Buttons";
|
||||||
|
|
||||||
const IconMap = {
|
const STYLES_NAV_LINKS = css`
|
||||||
// ACTIVITY: <SVG.Home height="20px" />,
|
|
||||||
ENCRYPTED: <SVG.SecurityLock height="20px" />,
|
|
||||||
NETWORK: <SVG.Activity height="20px" />,
|
|
||||||
DIRECTORY: <SVG.Directory height="20px" />,
|
|
||||||
// DATA: <SVG.Folder height="20px" />,
|
|
||||||
DATA: <SVG.Home height="20px" />,
|
|
||||||
WALLET: <SVG.OldWallet height="20px" />,
|
|
||||||
DEALS: <SVG.Deals height="20px" />,
|
|
||||||
STORAGE_DEAL: <SVG.HardDrive height="20px" />,
|
|
||||||
SLATES: <SVG.Layers height="20px" />,
|
|
||||||
SLATE: <SVG.Slate height="20px" />,
|
|
||||||
LOCAL_DATA: <SVG.HardDrive height="20px" />,
|
|
||||||
PROFILE_PAGE: <SVG.ProfileUser height="20px" />,
|
|
||||||
API: <SVG.Tool height="20px" />,
|
|
||||||
SETTINGS: <SVG.Settings height="20px" />,
|
|
||||||
DIRECTORY: <SVG.Directory height="20px" />,
|
|
||||||
FILECOIN: <SVG.Wallet height="20px" />,
|
|
||||||
MINERS: <SVG.Miners height="20px" />,
|
|
||||||
};
|
|
||||||
|
|
||||||
const STYLES_SHORTCUTS = css`
|
|
||||||
background-color: ${Constants.system.white};
|
|
||||||
border-radius: 2px;
|
|
||||||
height: 24px;
|
|
||||||
width: 24px;
|
|
||||||
margin-left: 4px;
|
|
||||||
font-size: 15px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: row;
|
||||||
justify-content: center;
|
|
||||||
color: ${Constants.system.textGray};
|
@media (max-width: ${Constants.sizes.mobile}px) {
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_ICON_ELEMENT = css`
|
const STYLES_NAV_LINK = css`
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: ${Constants.system.textGray};
|
color: ${Constants.system.textGray};
|
||||||
user-select: none;
|
text-decoration: none;
|
||||||
|
transition: 200ms ease color;
|
||||||
|
display: block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
pointer-events: auto;
|
padding: 4px 24px;
|
||||||
|
font-size: ${Constants.typescale.lvl1};
|
||||||
|
|
||||||
:hover {
|
:hover {
|
||||||
color: ${Constants.system.brand};
|
color: ${Constants.system.brand};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: ${Constants.sizes.mobile}px) {
|
||||||
|
border-bottom: 1px solid ${Constants.system.grayLight2};
|
||||||
|
margin: 0px 24px;
|
||||||
|
padding: 12px 0px;
|
||||||
|
${Styles.BODY_02};
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_APPLICATION_HEADER = css`
|
const STYLES_APPLICATION_HEADER_CONTAINER = css`
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 56px;
|
background-color: ${Constants.system.white};
|
||||||
padding: 0 24px 0 16px;
|
|
||||||
pointer-events: none;
|
|
||||||
background-color: ${Constants.system.foreground};
|
|
||||||
|
|
||||||
@supports ((-webkit-backdrop-filter: blur(25px)) or (backdrop-filter: blur(25px))) {
|
@supports ((-webkit-backdrop-filter: blur(25px)) or (backdrop-filter: blur(25px))) {
|
||||||
-webkit-backdrop-filter: blur(25px);
|
-webkit-backdrop-filter: blur(25px);
|
||||||
backdrop-filter: blur(25px);
|
backdrop-filter: blur(25px);
|
||||||
background-color: rgba(248, 248, 248, 0.7);
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
}
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const STYLES_APPLICATION_HEADER = css`
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto 1fr;
|
||||||
|
align-items: center;
|
||||||
|
${"" /* justify-content: space-between; */}
|
||||||
|
width: 100%;
|
||||||
|
height: 56px;
|
||||||
|
${"" /* padding: 0 24px 0 16px; */}
|
||||||
|
padding: 0px 32px;
|
||||||
|
|
||||||
@media (max-width: ${Constants.sizes.mobile}px) {
|
@media (max-width: ${Constants.sizes.mobile}px) {
|
||||||
padding: 0px 12px;
|
padding: 0px 24px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_LEFT = css`
|
const STYLES_LEFT = css`
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
${"" /* width: 352px; */}
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
@ -92,38 +84,34 @@ const STYLES_LEFT = css`
|
|||||||
const STYLES_MIDDLE = css`
|
const STYLES_MIDDLE = css`
|
||||||
min-width: 10%;
|
min-width: 10%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 24px 0 48px;
|
padding: 0 24px;
|
||||||
`;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
const STYLES_MOBILE_HIDDEN = css`
|
|
||||||
@media (max-width: ${Constants.sizes.mobile}px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_RIGHT = css`
|
const STYLES_RIGHT = css`
|
||||||
min-width: 10%;
|
flex-shrink: 0;
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const rotate = keyframes`
|
const STYLES_BACKGROUND = css`
|
||||||
0% {
|
position: absolute;
|
||||||
transform: rotate(0deg);
|
width: 100vw;
|
||||||
}
|
height: 100vh;
|
||||||
100% {
|
background-color: ${Constants.system.bgBlurGrayBlack};
|
||||||
transform: rotate(360deg);
|
pointer-events: auto;
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const STYLES_ROTATION = css`
|
@keyframes fade-in {
|
||||||
animation: ${rotate} 1s linear infinite;
|
from {
|
||||||
`;
|
opacity: 50%;
|
||||||
|
}
|
||||||
const STYLES_STATIC = css`
|
to {
|
||||||
transition: 200ms ease all;
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
animation: fade-in 200ms ease-out;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default class ApplicationHeader extends React.Component {
|
export default class ApplicationHeader extends React.Component {
|
||||||
@ -135,6 +123,7 @@ export default class ApplicationHeader extends React.Component {
|
|||||||
);
|
);
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
showDropdown: false,
|
||||||
popup: null,
|
popup: null,
|
||||||
isRefreshing: false,
|
isRefreshing: false,
|
||||||
};
|
};
|
||||||
@ -162,6 +151,7 @@ export default class ApplicationHeader extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleCreateSearch = (e) => {
|
_handleCreateSearch = (e) => {
|
||||||
|
this.setState({ showDropdown: false });
|
||||||
Events.dispatchCustomEvent({
|
Events.dispatchCustomEvent({
|
||||||
name: "show-search",
|
name: "show-search",
|
||||||
detail: {},
|
detail: {},
|
||||||
@ -169,6 +159,7 @@ export default class ApplicationHeader extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleTogglePopup = (value) => {
|
_handleTogglePopup = (value) => {
|
||||||
|
console.log(value);
|
||||||
if (!value || this.state.popup === value) {
|
if (!value || this.state.popup === value) {
|
||||||
this.setState({ popup: null });
|
this.setState({ popup: null });
|
||||||
} else {
|
} else {
|
||||||
@ -177,135 +168,217 @@ export default class ApplicationHeader extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// const isBackDisabled = this.props.currentIndex === 0 || this.props.history.length < 2;
|
const navigation = this.props.navigation.filter((item) => item.mainNav);
|
||||||
|
|
||||||
// const isForwardDisabled =
|
if (!this.props.viewer) {
|
||||||
// this.props.currentIndex === this.props.history.length - 1 || this.props.history.length < 2;
|
const searchComponent = (
|
||||||
return (
|
<div
|
||||||
<header css={STYLES_APPLICATION_HEADER}>
|
onClick={this._handleCreateSearch}
|
||||||
<div css={STYLES_LEFT}>
|
css={Styles.HORIZONTAL_CONTAINER_CENTERED}
|
||||||
<span
|
style={{ border: "none", pointerEvents: "auto", cursor: "pointer" }}
|
||||||
css={STYLES_ICON_ELEMENT}
|
>
|
||||||
style={{ position: "relative" }}
|
<SVG.Search
|
||||||
onClick={() => this._handleTogglePopup("nav")}
|
height="16px"
|
||||||
>
|
style={{ color: Constants.system.textGrayDark, marginRight: 8 }}
|
||||||
<SVG.Menu height="24px" />
|
/>
|
||||||
{this.state.popup === "nav" ? (
|
<span css={Styles.BODY_02} style={{ color: Constants.system.textGray }}>
|
||||||
<Boundary
|
Search Slate...
|
||||||
captureResize={true}
|
</span>
|
||||||
captureScroll={false}
|
</div>
|
||||||
enabled
|
);
|
||||||
onOutsideRectEvent={() => this._handleTogglePopup()}
|
|
||||||
style={this.props.style}
|
//NOTE(martina): signed out view
|
||||||
|
return (
|
||||||
|
<header css={STYLES_APPLICATION_HEADER_CONTAINER}>
|
||||||
|
<div css={STYLES_APPLICATION_HEADER}>
|
||||||
|
<div css={STYLES_LEFT}>
|
||||||
|
<Symbol style={{ height: 24, marginRight: 16 }} />
|
||||||
|
<div css={Styles.MOBILE_ONLY}>{searchComponent}</div>
|
||||||
|
</div>
|
||||||
|
<div css={STYLES_MIDDLE}>
|
||||||
|
<span css={Styles.MOBILE_HIDDEN}>{searchComponent}</span>
|
||||||
|
</div>
|
||||||
|
<div css={STYLES_RIGHT}>
|
||||||
|
<Link
|
||||||
|
href="/_/auth?tab=signin"
|
||||||
|
onAction={this.props.onAction}
|
||||||
|
style={{ pointerEvents: "auto" }}
|
||||||
>
|
>
|
||||||
<PopoverNavigation
|
<span css={Styles.MOBILE_HIDDEN}>
|
||||||
style={{ top: 44, left: 8, width: 220, padding: "12px 24px" }}
|
<ButtonTertiary
|
||||||
itemStyle={{ padding: "12px 0" }}
|
style={{
|
||||||
navigation={this.props.navigation
|
padding: "0px 12px",
|
||||||
.filter((item) => !item.ignore)
|
minHeight: "30px",
|
||||||
.map((item) => {
|
fontFamily: Constants.font.text,
|
||||||
return {
|
marginRight: 8,
|
||||||
text: (
|
}}
|
||||||
<div
|
>
|
||||||
style={{
|
Sign in
|
||||||
display: "flex",
|
</ButtonTertiary>
|
||||||
alignItems: "center",
|
</span>
|
||||||
color: this.props.activeIds[item.id] ? Constants.system.brand : null,
|
</Link>
|
||||||
}}
|
<Link
|
||||||
>
|
href="/_/auth?tab=signup"
|
||||||
<span style={{ marginRight: 16 }}>{IconMap[item.decorator]}</span>
|
onAction={this.props.onAction}
|
||||||
<span style={{ fontSize: 18 }}>{item.name}</span>
|
style={{ pointerEvents: "auto" }}
|
||||||
</div>
|
>
|
||||||
),
|
<ButtonPrimary
|
||||||
onClick: (e) => {
|
style={{
|
||||||
this._handleTogglePopup();
|
padding: "0px 12px",
|
||||||
this.props.onAction({
|
minHeight: "30px",
|
||||||
type: "NAVIGATE",
|
fontFamily: Constants.font.text,
|
||||||
value: item.id,
|
}}
|
||||||
});
|
>
|
||||||
},
|
Sign up
|
||||||
};
|
</ButtonPrimary>
|
||||||
})}
|
</Link>
|
||||||
/>
|
</div>
|
||||||
</Boundary>
|
</div>
|
||||||
) : null}
|
</header>
|
||||||
</span>
|
);
|
||||||
{/* <span css={STYLES_MOBILE_HIDDEN} style={{ marginLeft: 32 }}>
|
}
|
||||||
<span
|
const mobilePopup = (
|
||||||
css={STYLES_ICON_ELEMENT}
|
// <Boundary
|
||||||
style={
|
// captureResize={false}
|
||||||
isBackDisabled ? { cursor: "not-allowed", color: Constants.system.border } : null
|
// captureScroll={false}
|
||||||
}
|
// enabled={this.state.popup === "profile"}
|
||||||
onClick={isBackDisabled ? () => {} : this.props.onBack}
|
// onOutsideRectEvent={(e) => {
|
||||||
>
|
// e.stopPropagation();
|
||||||
<SVG.ChevronLeft height="28px" />
|
// e.preventDefault();
|
||||||
</span>
|
// this._handleTogglePopup(e);
|
||||||
<span
|
// }}
|
||||||
css={STYLES_ICON_ELEMENT}
|
// >
|
||||||
style={
|
<>
|
||||||
isForwardDisabled
|
<ApplicationUserControlsPopup
|
||||||
? { cursor: "not-allowed", color: Constants.system.border, marginLeft: 8 }
|
popup={this.state.popup}
|
||||||
: { marginLeft: 8 }
|
onTogglePopup={this._handleTogglePopup}
|
||||||
}
|
viewer={this.props.viewer}
|
||||||
onClick={isForwardDisabled ? () => {} : this.props.onForward}
|
onAction={this.props.onAction}
|
||||||
>
|
style={{ pointerEvents: "auto", paddingBottom: 16 }}
|
||||||
<SVG.ChevronRight height="28px" />
|
/>
|
||||||
</span>
|
<div css={STYLES_BACKGROUND} />
|
||||||
</span> */}
|
</>
|
||||||
{/* <div
|
// </Boundary>
|
||||||
style={{
|
);
|
||||||
height: 28,
|
|
||||||
margin: "0px 12px",
|
const mobileDropdown = (
|
||||||
borderRight: `1px solid ${Constants.system.border}`,
|
<>
|
||||||
}}
|
<Boundary
|
||||||
/> */}
|
captureResize={false}
|
||||||
<span
|
captureScroll={false}
|
||||||
css={STYLES_ICON_ELEMENT}
|
enabled={this.state.showDropdown}
|
||||||
style={{ marginLeft: 24 }}
|
onOutsideRectEvent={(e) => {
|
||||||
onClick={this._handleCreateSearch}
|
e.stopPropagation();
|
||||||
>
|
e.preventDefault();
|
||||||
<SVG.Search height="24px" />
|
this.setState({ showDropdown: false });
|
||||||
</span>
|
}}
|
||||||
<span css={STYLES_MOBILE_HIDDEN}>
|
>
|
||||||
|
<div css={STYLES_NAV_LINKS} style={{ pointerEvents: "auto", paddingBottom: 16 }}>
|
||||||
|
{this.props.navigation
|
||||||
|
.filter((item) => item.mainNav)
|
||||||
|
.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.id}
|
||||||
|
href={item.pathname}
|
||||||
|
onAction={this.props.onAction}
|
||||||
|
onClick={() => this.setState({ showDropdown: false })}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
css={STYLES_NAV_LINK}
|
||||||
|
style={{
|
||||||
|
color: this.props.activePage === item.id ? Constants.system.black : null,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
<div
|
<div
|
||||||
style={{
|
onClick={this._handleCreateSearch}
|
||||||
display: "flex",
|
css={STYLES_NAV_LINK}
|
||||||
alignItems: "center",
|
style={{ border: "none" }}
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div css={STYLES_SHORTCUTS} style={{ width: "auto" }}>
|
Search
|
||||||
{this.searchModKey}
|
</div>
|
||||||
|
</div>
|
||||||
|
</Boundary>
|
||||||
|
<div css={STYLES_BACKGROUND} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={{ width: "100vw", height: "100vh", position: "absolute" }} />
|
||||||
|
<header css={STYLES_APPLICATION_HEADER_CONTAINER}>
|
||||||
|
<span css={Styles.MOBILE_HIDDEN}>
|
||||||
|
<div css={STYLES_APPLICATION_HEADER}>
|
||||||
|
<div css={STYLES_LEFT}>
|
||||||
|
<Symbol style={{ height: 24 }} />
|
||||||
|
</div>
|
||||||
|
<div css={STYLES_MIDDLE}>
|
||||||
|
<div css={STYLES_NAV_LINKS} style={{ pointerEvents: "auto" }}>
|
||||||
|
{navigation.map((item, i) => (
|
||||||
|
<Link key={item.id} href={item.pathname} onAction={this.props.onAction}>
|
||||||
|
<div
|
||||||
|
css={STYLES_NAV_LINK}
|
||||||
|
style={{
|
||||||
|
color: this.props.activePage === item.id ? Constants.system.black : null,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
<div onClick={this._handleCreateSearch} css={STYLES_NAV_LINK}>
|
||||||
|
Search
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div css={STYLES_RIGHT}>
|
||||||
|
<span style={{ pointerEvents: "auto", marginLeft: 24 }}>
|
||||||
|
<ApplicationUserControls
|
||||||
|
popup={this.state.popup}
|
||||||
|
onTogglePopup={this._handleTogglePopup}
|
||||||
|
viewer={this.props.viewer}
|
||||||
|
onAction={this.props.onAction}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div css={STYLES_SHORTCUTS}>F</div>
|
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<span css={Styles.MOBILE_ONLY}>
|
||||||
{/* <div css={STYLES_MIDDLE} /> */}
|
<div css={STYLES_APPLICATION_HEADER}>
|
||||||
<div css={STYLES_RIGHT}>
|
<div css={STYLES_LEFT}>
|
||||||
{/* <span css={STYLES_MOBILE_HIDDEN}>
|
<div
|
||||||
<span
|
css={Styles.ICON_CONTAINER}
|
||||||
css={STYLES_ICON_ELEMENT}
|
style={{ pointerEvents: "auto" }}
|
||||||
onClick={() =>
|
onClick={() => this.setState({ showDropdown: !this.state.showDropdown })}
|
||||||
this.props.onAction({
|
>
|
||||||
type: "SIDEBAR",
|
<SVG.MenuMinimal height="16px" />
|
||||||
value: "SIDEBAR_HELP",
|
</div>
|
||||||
})
|
</div>
|
||||||
}
|
<div css={STYLES_MIDDLE}>
|
||||||
>
|
<Symbol style={{ height: 24 }} />
|
||||||
<SVG.Help height="24px" />
|
</div>
|
||||||
</span>
|
<div css={STYLES_RIGHT}>
|
||||||
</span> */}
|
<span style={{ pointerEvents: "auto", marginLeft: 24 }}>
|
||||||
<span style={{ pointerEvents: "auto", marginLeft: 24 }}>
|
<ApplicationUserControls
|
||||||
<ApplicationUserControls
|
popup={false}
|
||||||
popup={this.state.popup}
|
onTogglePopup={this._handleTogglePopup}
|
||||||
onTogglePopup={this._handleTogglePopup}
|
viewer={this.props.viewer}
|
||||||
viewer={this.props.viewer}
|
onAction={this.props.onAction}
|
||||||
onAction={this.props.onAction}
|
/>
|
||||||
/>
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{this.state.popup === "profile"
|
||||||
|
? mobilePopup
|
||||||
|
: this.state.showDropdown
|
||||||
|
? mobileDropdown
|
||||||
|
: null}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</header>
|
||||||
</header>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,23 +169,20 @@ export default class ApplicationLayout extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div css={STYLES_CONTENT}>
|
<div css={STYLES_CONTENT}>
|
||||||
{/* <GlobalTooltip elementRef={this._body} allowedTypes={["body"]} /> */}
|
|
||||||
<GlobalTooltip />
|
<GlobalTooltip />
|
||||||
|
{this.props.header && (
|
||||||
<div
|
<div
|
||||||
css={STYLES_HEADER}
|
css={STYLES_HEADER}
|
||||||
style={{ top: this.props.isMobile ? this.state.headerTop : null }}
|
style={{ top: this.props.isMobile ? this.state.headerTop : null }}
|
||||||
>
|
>
|
||||||
{this.props.header}
|
{this.props.header}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<Alert
|
<Alert
|
||||||
noWarning={this.props.viewer.data.status?.hidePrivacyAlert}
|
noWarning={this.props.viewer ? this.props.viewer.data.status?.hidePrivacyAlert : false}
|
||||||
fileLoading={this.props.fileLoading}
|
fileLoading={this.props.fileLoading}
|
||||||
onAction={this.props.onAction}
|
onAction={this.props.onAction}
|
||||||
filecoin={this.props.filecoin}
|
|
||||||
id={this.props.isMobile ? "slate-mobile-alert" : null}
|
id={this.props.isMobile ? "slate-mobile-alert" : null}
|
||||||
onUpdateViewer={this.props.onUpdateViewer}
|
|
||||||
viewer={this.props.viewer}
|
viewer={this.props.viewer}
|
||||||
style={
|
style={
|
||||||
this.props.isMobile
|
this.props.isMobile
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as Constants from "~/common/constants";
|
import * as Constants from "~/common/constants";
|
||||||
import * as SVG from "~/common/svg";
|
import * as Styles from "~/common/styles";
|
||||||
import * as UserBehaviors from "~/common/user-behaviors";
|
import * as UserBehaviors from "~/common/user-behaviors";
|
||||||
|
|
||||||
import { PopoverNavigation } from "~/components/system";
|
import { PopoverNavigation } from "~/components/system";
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
||||||
|
|
||||||
@ -47,19 +48,19 @@ const STYLES_PROFILE_MOBILE = css`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_PROFILE_IMAGE = css`
|
const STYLES_PROFILE_IMAGE = css`
|
||||||
background-color: ${Constants.system.white};
|
background-color: ${Constants.system.foreground};
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: 50% 50%;
|
background-position: 50% 50%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
height: 32px;
|
height: 24px;
|
||||||
width: 32px;
|
width: 24px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@media (max-width: ${Constants.sizes.mobile}px) {
|
${"" /* @media (max-width: ${Constants.sizes.mobile}px) {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
} */}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_PROFILE_USERNAME = css`
|
const STYLES_PROFILE_USERNAME = css`
|
||||||
@ -100,35 +101,10 @@ const STYLES_ITEM_BOX = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_MENU = css`
|
export class ApplicationUserControlsPopup extends React.Component {
|
||||||
position: absolute;
|
_handleAction = (props) => {
|
||||||
top: 48px;
|
|
||||||
right: 0px;
|
|
||||||
|
|
||||||
${"" /* @media (max-width: ${Constants.sizes.mobile}px) {
|
|
||||||
top: 48px;
|
|
||||||
left: 0px;
|
|
||||||
} */}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const STYLES_MOBILE_HIDDEN = css`
|
|
||||||
@media (max-width: ${Constants.sizes.mobile}px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const STYLES_MOBILE_ONLY = css`
|
|
||||||
@media (min-width: ${Constants.sizes.mobile}px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default class ApplicationUserControls extends React.Component {
|
|
||||||
_handleAction = (e, data) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
this.props.onTogglePopup();
|
this.props.onTogglePopup();
|
||||||
return this.props.onAction(data);
|
this.props.onAction(props);
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleSignOut = (e) => {
|
_handleSignOut = (e) => {
|
||||||
@ -139,55 +115,166 @@ export default class ApplicationUserControls extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let tooltip = (
|
if (this.props.popup === "profile") {
|
||||||
<Boundary
|
const topSection = (
|
||||||
captureResize={true}
|
<div css={Styles.HORIZONTAL_CONTAINER} style={{ marginBottom: 14 }}>
|
||||||
captureScroll={false}
|
<span
|
||||||
enabled
|
css={STYLES_PROFILE_IMAGE}
|
||||||
onOutsideRectEvent={() => this.props.onTogglePopup()}
|
style={{
|
||||||
style={this.props.style}
|
cursor: "default",
|
||||||
>
|
width: 46,
|
||||||
<div>
|
height: 46,
|
||||||
<PopoverNavigation
|
marginRight: 16,
|
||||||
style={{ top: 36, right: 0, padding: "12px 24px", width: 220 }}
|
backgroundImage: `url('${this.props.viewer.data.photo}')`,
|
||||||
itemStyle={{ padding: "12px 0", fontSize: 18, justifyContent: "flex-end" }}
|
}}
|
||||||
navigation={[
|
|
||||||
{
|
|
||||||
text: "View profile",
|
|
||||||
onClick: (e) =>
|
|
||||||
this._handleAction(e, {
|
|
||||||
type: "NAVIGATE",
|
|
||||||
value: "NAV_PROFILE",
|
|
||||||
data: this.props.viewer,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Account settings",
|
|
||||||
onClick: (e) =>
|
|
||||||
this._handleAction(e, {
|
|
||||||
type: "NAVIGATE",
|
|
||||||
value: "NAV_SETTINGS",
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Contact us",
|
|
||||||
onClick: (e) =>
|
|
||||||
this._handleAction(e, {
|
|
||||||
type: "SIDEBAR",
|
|
||||||
value: "SIDEBAR_HELP",
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Sign out",
|
|
||||||
onClick: (e) => {
|
|
||||||
this._handleSignOut(e);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
css={Styles.VERTICAL_CONTAINER}
|
||||||
|
style={{
|
||||||
|
height: 46,
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div css={Styles.HEADING_04}>
|
||||||
|
{this.props.viewer.data.name || `@${this.props.viewer.username}`}
|
||||||
|
</div>
|
||||||
|
<div css={Styles.HORIZONTAL_CONTAINER}>
|
||||||
|
<span css={Styles.SMALL_TEXT} style={{ marginRight: 8 }}>{`${
|
||||||
|
this.props.viewer.library.length
|
||||||
|
} File${this.props.viewer.library.length === 1 ? "" : "s"}`}</span>
|
||||||
|
<span css={Styles.SMALL_TEXT}>{`${this.props.viewer.slates.length} Collection${
|
||||||
|
this.props.viewer.slates.length === 1 ? "" : "s"
|
||||||
|
}`}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Boundary>
|
);
|
||||||
);
|
|
||||||
|
const navigation = [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: (
|
||||||
|
<Link href={`/$/user/${this.props.viewer.id}`} onAction={this._handleAction}>
|
||||||
|
Profile
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: (
|
||||||
|
<Link href={"/_/directory"} onAction={this._handleAction}>
|
||||||
|
Directory
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: (
|
||||||
|
<Link href={"/_/filecoin"} onAction={this._handleAction}>
|
||||||
|
Filecoin
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: (
|
||||||
|
<Link href={"/_/storage-deal"} onAction={this._handleAction}>
|
||||||
|
Storage deal
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: (
|
||||||
|
<Link href={"/_/api"} onAction={this._handleAction}>
|
||||||
|
API
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: (
|
||||||
|
<Link href={"/_/settings"} onAction={this._handleAction}>
|
||||||
|
Settings
|
||||||
|
</Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: "Help",
|
||||||
|
onClick: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
this._handleAction({
|
||||||
|
type: "SIDEBAR",
|
||||||
|
value: "SIDEBAR_HELP",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Sign out",
|
||||||
|
onClick: (e) => {
|
||||||
|
this._handleSignOut(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div css={Styles.MOBILE_ONLY}>
|
||||||
|
<Boundary
|
||||||
|
captureResize={true}
|
||||||
|
captureScroll={false}
|
||||||
|
enabled={this.props.popup === "profile"}
|
||||||
|
onOutsideRectEvent={() => this.props.onTogglePopup()}
|
||||||
|
>
|
||||||
|
<PopoverNavigation
|
||||||
|
style={{
|
||||||
|
width: "max-content",
|
||||||
|
position: "relative",
|
||||||
|
border: "none",
|
||||||
|
boxShadow: "none",
|
||||||
|
width: "100vw",
|
||||||
|
background: "none",
|
||||||
|
pointerEvents: "auto",
|
||||||
|
}}
|
||||||
|
css={Styles.HEADING_04}
|
||||||
|
itemStyle={{ fontSize: Constants.typescale.lvl0 }}
|
||||||
|
topSection={topSection}
|
||||||
|
navigation={navigation}
|
||||||
|
/>
|
||||||
|
</Boundary>
|
||||||
|
</div>
|
||||||
|
<div css={Styles.MOBILE_HIDDEN}>
|
||||||
|
<Boundary
|
||||||
|
captureResize={true}
|
||||||
|
captureScroll={false}
|
||||||
|
enabled={this.props.popup === "profile"}
|
||||||
|
onOutsideRectEvent={() => this.props.onTogglePopup()}
|
||||||
|
>
|
||||||
|
<PopoverNavigation
|
||||||
|
style={{
|
||||||
|
top: 36,
|
||||||
|
right: 0,
|
||||||
|
width: "max-content",
|
||||||
|
}}
|
||||||
|
css={Styles.HEADING_04}
|
||||||
|
itemStyle={{ fontSize: Constants.typescale.lvl0 }}
|
||||||
|
topSection={topSection}
|
||||||
|
navigation={navigation}
|
||||||
|
/>
|
||||||
|
</Boundary>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApplicationUserControls extends React.Component {
|
||||||
|
render() {
|
||||||
|
let tooltip = <ApplicationUserControlsPopup {...this.props} />;
|
||||||
return (
|
return (
|
||||||
<div css={STYLES_HEADER}>
|
<div css={STYLES_HEADER}>
|
||||||
<div css={STYLES_PROFILE_MOBILE} style={{ position: "relative" }}>
|
<div css={STYLES_PROFILE_MOBILE} style={{ position: "relative" }}>
|
||||||
|
@ -7,9 +7,10 @@ import * as Credentials from "~/common/credentials";
|
|||||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { Logo } from "~/common/logo";
|
import { Logo } from "~/common/logo";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
const STYLES_BACKGROUND = css`
|
const STYLES_BACKGROUND = css`
|
||||||
z-index: ${Constants.zindex.tooltip};
|
z-index: ${Constants.zindex.cta};
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
@ -17,7 +18,6 @@ const STYLES_BACKGROUND = css`
|
|||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: ${Constants.zindex.modal};
|
|
||||||
background-color: rgba(0, 0, 0, 0.85);
|
background-color: rgba(0, 0, 0, 0.85);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
@ -88,10 +88,38 @@ const STYLES_LINK_ITEM = css`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export default class CTATransition extends React.Component {
|
export default class CTATransition extends React.Component {
|
||||||
|
state = {
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount = () => {
|
||||||
|
window.addEventListener("slate-global-open-cta", this._handleOpen);
|
||||||
|
window.addEventListener("slate-global-close-cta", this._handleClose);
|
||||||
|
};
|
||||||
|
|
||||||
|
componentWillUnmount = () => {
|
||||||
|
window.removeEventListener("slate-global-open-cta", this._handleOpen);
|
||||||
|
window.removeEventListener("slate-global-close-cta", this._handleClose);
|
||||||
|
};
|
||||||
|
|
||||||
|
_handleOpen = (e) => {
|
||||||
|
this.setState({ visible: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
_handleClose = (e) => {
|
||||||
|
this.setState({ visible: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
_handleAction = (props) => {
|
||||||
|
this.setState({ visible: false }, () => {
|
||||||
|
this.props.onAction(props);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
this.state.visible && (
|
||||||
{open && (
|
<div>
|
||||||
<div css={STYLES_BACKGROUND}>
|
<div css={STYLES_BACKGROUND}>
|
||||||
<div css={STYLES_TRANSITION}>
|
<div css={STYLES_TRANSITION}>
|
||||||
<div css={STYLES_EXPLAINER}>Sign up or sign in to continue</div>
|
<div css={STYLES_EXPLAINER}>Sign up or sign in to continue</div>
|
||||||
@ -100,7 +128,7 @@ export default class CTATransition extends React.Component {
|
|||||||
captureResize={true}
|
captureResize={true}
|
||||||
captureScroll={false}
|
captureScroll={false}
|
||||||
enabled
|
enabled
|
||||||
onOutsideRectEvent={this.props.onClose}
|
onOutsideRectEvent={this._handleClose}
|
||||||
>
|
>
|
||||||
<div css={STYLES_POPOVER}>
|
<div css={STYLES_POPOVER}>
|
||||||
<Logo height="36px" style={{ display: "block", margin: "56px auto 0px auto" }} />
|
<Logo height="36px" style={{ display: "block", margin: "56px auto 0px auto" }} />
|
||||||
@ -108,20 +136,27 @@ export default class CTATransition extends React.Component {
|
|||||||
<System.P style={{ margin: "56px 0", textAlign: "center" }}>
|
<System.P style={{ margin: "56px 0", textAlign: "center" }}>
|
||||||
An open-source file sharing network for research and collaboration
|
An open-source file sharing network for research and collaboration
|
||||||
</System.P>
|
</System.P>
|
||||||
<a href={this.props.redirectURL} style={{ textDecoration: `none` }}>
|
<Link href={"/_/auth?tab=signup"} onAction={this._handleAction}>
|
||||||
<System.ButtonPrimary full style={{ marginBottom: 16 }}>
|
<div style={{ textDecoration: `none` }}>
|
||||||
Continue to sign up
|
<System.ButtonPrimary full style={{ marginBottom: 16 }}>
|
||||||
</System.ButtonPrimary>{" "}
|
Continue to sign up
|
||||||
</a>
|
</System.ButtonPrimary>
|
||||||
<a css={STYLES_LINK_ITEM} href={this.props.redirectURL}>
|
</div>
|
||||||
Already have an account?
|
</Link>
|
||||||
</a>
|
<Link href={"/_/auth?tab=signin"} onAction={this._handleAction}>
|
||||||
|
<div css={STYLES_LINK_ITEM}>Already have an account?</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</Boundary>
|
</Boundary>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `/_${Strings.createQueryParams({
|
||||||
|
// scene: "NAV_PROFILE",
|
||||||
|
// user: this.props.creator.username,
|
||||||
|
// })}`
|
||||||
|
@ -17,6 +17,7 @@ import { Input } from "~/components/system/components/Input";
|
|||||||
import { Toggle } from "~/components/system/components/Toggle";
|
import { Toggle } from "~/components/system/components/Toggle";
|
||||||
import { Textarea } from "~/components/system/components/Textarea";
|
import { Textarea } from "~/components/system/components/Textarea";
|
||||||
import { Tag } from "~/components/system/components/Tag";
|
import { Tag } from "~/components/system/components/Tag";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
import isEqual from "lodash/isEqual";
|
import isEqual from "lodash/isEqual";
|
||||||
import cloneDeep from "lodash/cloneDeep";
|
import cloneDeep from "lodash/cloneDeep";
|
||||||
@ -279,11 +280,11 @@ export const FileTypeDefaultPreview = (props) => {
|
|||||||
|
|
||||||
class CarouselSidebar extends React.Component {
|
class CarouselSidebar extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
name: this.props.data.data.name || this.props.data.filename,
|
name: this.props.file.data.name || this.props.file.filename,
|
||||||
body: this.props.data.data.body,
|
body: this.props.file.data.body,
|
||||||
source: this.props.data.data.source,
|
source: this.props.file.data.source,
|
||||||
author: this.props.data.data.author,
|
author: this.props.file.data.author,
|
||||||
tags: this.props.data.data.tags || [],
|
tags: this.props.file.data.tags || [],
|
||||||
suggestions: this.props.viewer?.tags || [],
|
suggestions: this.props.viewer?.tags || [],
|
||||||
selected: {},
|
selected: {},
|
||||||
isPublic: false,
|
isPublic: false,
|
||||||
@ -316,9 +317,13 @@ class CarouselSidebar extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
calculateSelected = () => {
|
calculateSelected = () => {
|
||||||
|
if (!this.props.viewer) {
|
||||||
|
this.setState({ selected: {}, inPublicSlates: 0, isPublic: this.props.file.isPublic });
|
||||||
|
return;
|
||||||
|
}
|
||||||
let inPublicSlates = 0;
|
let inPublicSlates = 0;
|
||||||
let selected = {};
|
let selected = {};
|
||||||
const id = this.props.data.id;
|
const id = this.props.file.id;
|
||||||
for (let slate of this.props.viewer.slates) {
|
for (let slate of this.props.viewer.slates) {
|
||||||
if (slate.objects.some((obj) => obj.id === id)) {
|
if (slate.objects.some((obj) => obj.id === id)) {
|
||||||
if (slate.isPublic) {
|
if (slate.isPublic) {
|
||||||
@ -327,7 +332,7 @@ class CarouselSidebar extends React.Component {
|
|||||||
selected[slate.id] = true;
|
selected[slate.id] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({ selected, inPublicSlates, isPublic: this.props.data.isPublic });
|
this.setState({ selected, inPublicSlates, isPublic: this.props.file.isPublic });
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleToggleAccordion = (tab) => {
|
_handleToggleAccordion = (tab) => {
|
||||||
@ -363,9 +368,9 @@ class CarouselSidebar extends React.Component {
|
|||||||
|
|
||||||
_handleSave = async () => {
|
_handleSave = async () => {
|
||||||
if (this.props.external || !this.props.isOwner) return;
|
if (this.props.external || !this.props.isOwner) return;
|
||||||
this.props.onUpdateViewer({ tags: this.state.suggestions });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { tags: this.state.suggestions } });
|
||||||
const response = await Actions.updateFile({
|
const response = await Actions.updateFile({
|
||||||
id: this.props.data.id,
|
id: this.props.file.id,
|
||||||
data: {
|
data: {
|
||||||
name: this.state.name,
|
name: this.state.name,
|
||||||
body: this.state.body,
|
body: this.state.body,
|
||||||
@ -379,6 +384,10 @@ class CarouselSidebar extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleSaveCopy = async (data) => {
|
_handleSaveCopy = async (data) => {
|
||||||
|
if (!this.props.viewer) {
|
||||||
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.setState({ loading: "savingCopy" });
|
this.setState({ loading: "savingCopy" });
|
||||||
|
|
||||||
await UserBehaviors.saveCopy({ files: [data] });
|
await UserBehaviors.saveCopy({ files: [data] });
|
||||||
@ -386,10 +395,10 @@ class CarouselSidebar extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleUpload = async (e) => {
|
_handleUpload = async (e) => {
|
||||||
if (this.props.external || !this.props.isOwner) return;
|
if (this.props.external || !this.props.isOwner || !this.props.viewer) return;
|
||||||
e.persist();
|
e.persist();
|
||||||
this.setState({ isUploading: true });
|
this.setState({ isUploading: true });
|
||||||
let previousCoverId = this.props.data.data.coverImage?.id;
|
let previousCoverId = this.props.file.data.coverImage?.id;
|
||||||
if (!e || !e.target) {
|
if (!e || !e.target) {
|
||||||
this.setState({ isUploading: false });
|
this.setState({ isUploading: false });
|
||||||
return;
|
return;
|
||||||
@ -405,7 +414,7 @@ class CarouselSidebar extends React.Component {
|
|||||||
//TODO(martina): create an endpoint specifically for cover images instead of this, which will delete original cover image etc
|
//TODO(martina): create an endpoint specifically for cover images instead of this, which will delete original cover image etc
|
||||||
|
|
||||||
let updateReponse = await Actions.updateFile({
|
let updateReponse = await Actions.updateFile({
|
||||||
id: this.props.data.id,
|
id: this.props.file.id,
|
||||||
data: {
|
data: {
|
||||||
coverImage,
|
coverImage,
|
||||||
},
|
},
|
||||||
@ -422,14 +431,18 @@ class CarouselSidebar extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleDownload = () => {
|
_handleDownload = () => {
|
||||||
if (this.props.data.data.type === "application/unity") {
|
if (!this.props.viewer) {
|
||||||
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.props.file.data.type === "application/unity") {
|
||||||
this.setState({ isDownloading: true }, async () => {
|
this.setState({ isDownloading: true }, async () => {
|
||||||
const response = await UserBehaviors.downloadZip(this.props.data);
|
const response = await UserBehaviors.downloadZip(this.props.file);
|
||||||
this.setState({ isDownloading: false });
|
this.setState({ isDownloading: false });
|
||||||
Events.hasError(response);
|
Events.hasError(response);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
UserBehaviors.download(this.props.data);
|
UserBehaviors.download(this.props.file);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -439,22 +452,22 @@ class CarouselSidebar extends React.Component {
|
|||||||
this.props.onAction({
|
this.props.onAction({
|
||||||
type: "SIDEBAR",
|
type: "SIDEBAR",
|
||||||
value: "SIDEBAR_CREATE_SLATE",
|
value: "SIDEBAR_CREATE_SLATE",
|
||||||
data: { files: [this.props.data] },
|
data: { files: [this.props.file] },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleDelete = (res) => {
|
_handleDelete = (res) => {
|
||||||
if (this.props.external || !this.props.isOwner) return;
|
if (this.props.external || !this.props.isOwner || !this.props.viewer) return;
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
this.setState({ modalShow: false });
|
this.setState({ modalShow: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const id = this.props.data.id;
|
const id = this.props.file.id;
|
||||||
|
|
||||||
let updatedLibrary = this.props.viewer.library.filter((obj) => obj.id !== id);
|
let updatedLibrary = this.props.viewer.library.filter((obj) => obj.id !== id);
|
||||||
if (this.props.carouselType === "SLATE") {
|
if (this.props.carouselType === "SLATE") {
|
||||||
const slateId = this.props.current.id;
|
const slateId = this.props.data.id;
|
||||||
let slates = this.props.viewer.slates;
|
let slates = this.props.viewer.slates;
|
||||||
for (let slate of slates) {
|
for (let slate of slates) {
|
||||||
if (slate.id === slateId) {
|
if (slate.id === slateId) {
|
||||||
@ -462,9 +475,9 @@ class CarouselSidebar extends React.Component {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.props.onUpdateViewer({ library: updatedLibrary, slates });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { library: updatedLibrary, slates } });
|
||||||
} else {
|
} else {
|
||||||
this.props.onUpdateViewer({ library: updatedLibrary });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { library: updatedLibrary } });
|
||||||
}
|
}
|
||||||
|
|
||||||
UserBehaviors.deleteFiles(id);
|
UserBehaviors.deleteFiles(id);
|
||||||
@ -476,14 +489,14 @@ class CarouselSidebar extends React.Component {
|
|||||||
if (slate.isPublic) {
|
if (slate.isPublic) {
|
||||||
inPublicSlates -= 1;
|
inPublicSlates -= 1;
|
||||||
}
|
}
|
||||||
UserBehaviors.removeFromSlate({ slate, ids: [this.props.data.id] });
|
UserBehaviors.removeFromSlate({ slate, ids: [this.props.file.id] });
|
||||||
} else {
|
} else {
|
||||||
if (slate.isPublic) {
|
if (slate.isPublic) {
|
||||||
inPublicSlates += 1;
|
inPublicSlates += 1;
|
||||||
}
|
}
|
||||||
UserBehaviors.addToSlate({
|
UserBehaviors.addToSlate({
|
||||||
slate,
|
slate,
|
||||||
files: [this.props.data],
|
files: [this.props.file],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -496,12 +509,17 @@ class CarouselSidebar extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleRemove = async () => {
|
_handleRemove = async () => {
|
||||||
if (!this.props.carouselType === "SLATE" || this.props.external || !this.props.isOwner) {
|
if (
|
||||||
|
!this.props.carouselType === "SLATE" ||
|
||||||
|
this.props.external ||
|
||||||
|
!this.props.isOwner ||
|
||||||
|
!this.props.viewer
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = this.props.data.id;
|
const id = this.props.file.id;
|
||||||
const slateId = this.props.current.id;
|
const slateId = this.props.data.id;
|
||||||
let slates = this.props.viewer.slates;
|
let slates = this.props.viewer.slates;
|
||||||
for (let slate of slates) {
|
for (let slate of slates) {
|
||||||
if (slate.id === slateId) {
|
if (slate.id === slateId) {
|
||||||
@ -509,13 +527,13 @@ class CarouselSidebar extends React.Component {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.props.onUpdateViewer({ slates });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { slates } });
|
||||||
|
|
||||||
UserBehaviors.removeFromSlate({ slate: this.props.current, ids: [this.props.data.id] });
|
UserBehaviors.removeFromSlate({ slate: this.props.data, ids: [this.props.file.id] });
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleToggleVisibility = async (e) => {
|
_handleToggleVisibility = async (e) => {
|
||||||
if (this.props.external || !this.props.isOwner) return;
|
if (this.props.external || !this.props.isOwner || !this.props.viewer) return;
|
||||||
const isVisible = this.state.isPublic || this.state.inPublicSlates > 0;
|
const isVisible = this.state.isPublic || this.state.inPublicSlates > 0;
|
||||||
let selected = cloneDeep(this.state.selected);
|
let selected = cloneDeep(this.state.selected);
|
||||||
if (this.state.inPublicSlates) {
|
if (this.state.inPublicSlates) {
|
||||||
@ -538,19 +556,19 @@ class CarouselSidebar extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.carouselType === "SLATE" && this.props.current.isPublic) {
|
if (this.props.carouselType === "SLATE" && this.props.data.isPublic) {
|
||||||
const slateId = this.props.current.id;
|
const slateId = this.props.data.id;
|
||||||
let slates = cloneDeep(this.props.viewer.slates);
|
let slates = cloneDeep(this.props.viewer.slates);
|
||||||
for (let slate of slates) {
|
for (let slate of slates) {
|
||||||
if (slate.id === slateId) {
|
if (slate.id === slateId) {
|
||||||
slate.objects = slate.objects.filter((obj) => obj.id !== this.props.data.id);
|
slate.objects = slate.objects.filter((obj) => obj.id !== this.props.file.id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.props.onUpdateViewer({ slates });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { slates } });
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = await Actions.toggleFilePrivacy({ ...this.props.data, isPublic: !isVisible });
|
let response = await Actions.toggleFilePrivacy({ ...this.props.file, isPublic: !isVisible });
|
||||||
Events.hasError(response);
|
Events.hasError(response);
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
this.setState({ inPublicSlates: 0, isPublic: false, selected });
|
this.setState({ inPublicSlates: 0, isPublic: false, selected });
|
||||||
@ -561,7 +579,7 @@ class CarouselSidebar extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const isVisible = this.state.isPublic || this.state.inPublicSlates > 0 ? true : false;
|
const isVisible = this.state.isPublic || this.state.inPublicSlates > 0 ? true : false;
|
||||||
const file = this.props.data;
|
const file = this.props.file;
|
||||||
const { coverImage, type, size } = file.data;
|
const { coverImage, type, size } = file.data;
|
||||||
const editingAllowed = this.props.isOwner && !this.props.isRepost && !this.props.external;
|
const editingAllowed = this.props.isOwner && !this.props.isRepost && !this.props.external;
|
||||||
|
|
||||||
@ -679,19 +697,23 @@ class CarouselSidebar extends React.Component {
|
|||||||
{
|
{
|
||||||
this.props.carouselType === "ACTIVITY"
|
this.props.carouselType === "ACTIVITY"
|
||||||
? actions.push(
|
? actions.push(
|
||||||
<div
|
<div style={{ borderBottom: "1px solid #3c3c3c" }}>
|
||||||
key="go-to-slate"
|
<Link href={`/$/slate/${file.slateId}`} onAction={this.props.onAction}>
|
||||||
css={STYLES_ACTION}
|
<div
|
||||||
onClick={() =>
|
key="go-to-slate"
|
||||||
this.props.onAction({
|
css={STYLES_ACTION}
|
||||||
type: "NAVIGATE",
|
// onClick={() =>
|
||||||
value: "NAV_SLATE",
|
// this.props.onAction({
|
||||||
data: file.slate,
|
// type: "NAVIGATE",
|
||||||
})
|
// value: "NAV_SLATE",
|
||||||
}
|
// data: file.slate,
|
||||||
>
|
// })
|
||||||
<SVG.Slate height="24px" />
|
// }
|
||||||
<span style={{ marginLeft: 16 }}>Go to collection</span>
|
>
|
||||||
|
<SVG.Slate height="24px" />
|
||||||
|
<span style={{ marginLeft: 16 }}>Go to collection</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
@ -713,7 +735,7 @@ class CarouselSidebar extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!this.props.external && (!this.props.isOwner || this.props.isRepost)) {
|
if (!this.props.isOwner || this.props.isRepost) {
|
||||||
actions.push(
|
actions.push(
|
||||||
<div key="save-copy" css={STYLES_ACTION} onClick={() => this._handleSaveCopy(file)}>
|
<div key="save-copy" css={STYLES_ACTION} onClick={() => this._handleSaveCopy(file)}>
|
||||||
<SVG.Save height="24px" />
|
<SVG.Save height="24px" />
|
||||||
@ -843,15 +865,15 @@ class CarouselSidebar extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{this.state.modalShow && (
|
{this.state.modalShow && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
type={"DELETE"}
|
type={"DELETE"}
|
||||||
withValidation={false}
|
withValidation={false}
|
||||||
callback={this._handleDelete}
|
callback={this._handleDelete}
|
||||||
header={`Are you sure you want to delete the file “${this.state.name}”?`}
|
header={`Are you sure you want to delete the file “${this.state.name}”?`}
|
||||||
subHeader={`This file will be deleted from all connected collections and your file library. You can’t undo this action.`}
|
subHeader={`This file will be deleted from all connected collections and your file library. You can’t undo this action.`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div css={STYLES_SIDEBAR} style={{ display: this.props.display }}>
|
<div css={STYLES_SIDEBAR} style={{ display: this.props.display, paddingBottom: 96 }}>
|
||||||
{this.state.showSavedMessage && (
|
{this.state.showSavedMessage && (
|
||||||
<div css={STYLES_AUTOSAVE}>
|
<div css={STYLES_AUTOSAVE}>
|
||||||
<SVG.Check height="14px" style={{ marginRight: 4 }} />
|
<SVG.Check height="14px" style={{ marginRight: 4 }} />
|
||||||
@ -861,59 +883,55 @@ class CarouselSidebar extends React.Component {
|
|||||||
<div key="s-1" css={STYLES_DISMISS_BOX} onClick={this.props.onClose}>
|
<div key="s-1" css={STYLES_DISMISS_BOX} onClick={this.props.onClose}>
|
||||||
<SVG.Dismiss height="24px" />
|
<SVG.Dismiss height="24px" />
|
||||||
</div>
|
</div>
|
||||||
|
{elements}
|
||||||
<div key="s-2" style={{ marginBottom: 80 }}>
|
<div css={STYLES_ACTIONS}>{actions}</div>
|
||||||
{elements}
|
{privacy}
|
||||||
|
{uploadCoverImage}
|
||||||
{!this.props.external && <div css={STYLES_ACTIONS}>{actions}</div>}
|
{!this.props.external && this.props.viewer && (
|
||||||
{privacy}
|
<>
|
||||||
{uploadCoverImage}
|
<div
|
||||||
{!this.props.external && (
|
css={STYLES_SECTION_HEADER}
|
||||||
<>
|
style={{ cursor: "pointer", marginTop: 48 }}
|
||||||
<div
|
onClick={() => this._handleToggleAccordion("showConnectedSection")}
|
||||||
css={STYLES_SECTION_HEADER}
|
>
|
||||||
style={{ cursor: "pointer", marginTop: 48 }}
|
<span
|
||||||
onClick={() => this._handleToggleAccordion("showConnectedSection")}
|
style={{
|
||||||
|
marginRight: 8,
|
||||||
|
transform: this.state.showConnectedSection ? "none" : "rotate(-90deg)",
|
||||||
|
transition: "100ms ease transform",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<SVG.ChevronDown height="24px" display="block" />
|
||||||
style={{
|
</span>
|
||||||
marginRight: 8,
|
<span>Add to collection</span>
|
||||||
transform: this.state.showConnectedSection ? "none" : "rotate(-90deg)",
|
</div>
|
||||||
transition: "100ms ease transform",
|
{this.state.showConnectedSection && (
|
||||||
}}
|
<div style={{ width: "100%", margin: "24px 0 44px 0" }}>
|
||||||
>
|
<SlatePicker
|
||||||
<SVG.ChevronDown height="24px" display="block" />
|
dark
|
||||||
</span>
|
slates={this.props.viewer.slates || []}
|
||||||
<span>Add to collection</span>
|
onCreateSlate={this._handleCreateSlate}
|
||||||
|
selectedColor={Constants.system.white}
|
||||||
|
files={[this.props.file]}
|
||||||
|
selected={this.state.selected}
|
||||||
|
onAdd={this._handleAdd}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{this.state.showConnectedSection && (
|
)}
|
||||||
<div style={{ width: "100%", margin: "24px 0 44px 0" }}>
|
</>
|
||||||
<SlatePicker
|
)}
|
||||||
dark
|
|
||||||
slates={this.props.viewer.slates}
|
|
||||||
onCreateSlate={this._handleCreateSlate}
|
|
||||||
selectedColor={Constants.system.white}
|
|
||||||
files={[this.props.data]}
|
|
||||||
selected={this.state.selected}
|
|
||||||
onAdd={this._handleAdd}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{this.props.data.filename.endsWith(".md") ? (
|
{this.props.file.filename.endsWith(".md") ? (
|
||||||
<>
|
<>
|
||||||
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
<div css={STYLES_SECTION_HEADER} style={{ margin: "48px 0px 8px 0px" }}>
|
||||||
Settings
|
Settings
|
||||||
</div>
|
</div>
|
||||||
<div css={STYLES_OPTIONS_SECTION}>
|
<div css={STYLES_OPTIONS_SECTION}>
|
||||||
<div css={STYLES_TEXT}>Dark mode</div>
|
<div css={STYLES_TEXT}>Dark mode</div>
|
||||||
<Toggle dark active={this.props?.theme?.darkmode} onChange={this._handleDarkMode} />
|
<Toggle dark active={this.props?.theme?.darkmode} onChange={this._handleDarkMode} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -937,4 +955,4 @@ export default withTheme(CarouselSidebar);
|
|||||||
: "This file is currently not visible to others unless they have the link."}
|
: "This file is currently not visible to others unless they have the link."}
|
||||||
</div>
|
</div>
|
||||||
</> */
|
</> */
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import * as Window from "~/common/window";
|
|||||||
import * as UserBehaviors from "~/common/user-behaviors";
|
import * as UserBehaviors from "~/common/user-behaviors";
|
||||||
import * as Events from "~/common/custom-events";
|
import * as Events from "~/common/custom-events";
|
||||||
|
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
||||||
import { PopoverNavigation } from "~/components/system/components/PopoverNavigation";
|
import { PopoverNavigation } from "~/components/system/components/PopoverNavigation";
|
||||||
@ -457,6 +458,10 @@ export default class DataView extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleDownloadFiles = async () => {
|
_handleDownloadFiles = async () => {
|
||||||
|
if (!this.props.viewer) {
|
||||||
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
const selectedFiles = this.props.items.filter((_, i) => this.state.checked[i]);
|
const selectedFiles = this.props.items.filter((_, i) => this.state.checked[i]);
|
||||||
UserBehaviors.compressAndDownloadFiles({
|
UserBehaviors.compressAndDownloadFiles({
|
||||||
files: selectedFiles,
|
files: selectedFiles,
|
||||||
@ -483,19 +488,12 @@ export default class DataView extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let library = this.props.viewer.library.filter((obj) => !ids.includes(obj.id));
|
let library = this.props.viewer.library.filter((obj) => !ids.includes(obj.id));
|
||||||
this.props.onUpdateViewer({ library });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { library } });
|
||||||
|
|
||||||
UserBehaviors.deleteFiles(ids);
|
UserBehaviors.deleteFiles(ids);
|
||||||
this.setState({ checked: {}, modalShow: false });
|
this.setState({ checked: {}, modalShow: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleSelect = (index) => {
|
|
||||||
Events.dispatchCustomEvent({
|
|
||||||
name: "slate-global-open-carousel",
|
|
||||||
detail: { index },
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
_handleCheckBoxMouseEnter = (i) => {
|
_handleCheckBoxMouseEnter = (i) => {
|
||||||
if (this.props.isOwner) {
|
if (this.props.isOwner) {
|
||||||
this.setState({ hover: i });
|
this.setState({ hover: i });
|
||||||
@ -526,6 +524,10 @@ export default class DataView extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleAddToSlate = (e) => {
|
_handleAddToSlate = (e) => {
|
||||||
|
if (!this.props.viewer) {
|
||||||
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
let userFiles = this.props.viewer.library;
|
let userFiles = this.props.viewer.library;
|
||||||
let files = Object.keys(this.state.checked).map((index) => userFiles[index]);
|
let files = Object.keys(this.state.checked).map((index) => userFiles[index]);
|
||||||
this.props.onAction({
|
this.props.onAction({
|
||||||
@ -646,30 +648,32 @@ export default class DataView extends React.Component {
|
|||||||
>
|
>
|
||||||
Add to collection
|
Add to collection
|
||||||
</ButtonPrimary>
|
</ButtonPrimary>
|
||||||
<ButtonPrimary
|
{this.props.isOwner && (
|
||||||
transparent
|
<ButtonPrimary
|
||||||
style={{ color: Constants.system.white }}
|
transparent
|
||||||
onClick={() => {
|
style={{ color: Constants.system.white }}
|
||||||
this.props.onAction({
|
onClick={() => {
|
||||||
type: "SIDEBAR",
|
this.props.onAction({
|
||||||
value: "SIDEBAR_EDIT_TAGS",
|
type: "SIDEBAR",
|
||||||
data: {
|
value: "SIDEBAR_EDIT_TAGS",
|
||||||
numChecked,
|
data: {
|
||||||
commonTags: this.getCommonTagFromSelectedItems(),
|
numChecked,
|
||||||
objects: this.props.items,
|
commonTags: this.getCommonTagFromSelectedItems(),
|
||||||
checked: this.state.checked,
|
objects: this.props.items,
|
||||||
},
|
checked: this.state.checked,
|
||||||
});
|
},
|
||||||
}}
|
});
|
||||||
>
|
}}
|
||||||
Edit tag{numChecked > 1 ? "s" : ""}
|
>
|
||||||
</ButtonPrimary>
|
Edit tags
|
||||||
|
</ButtonPrimary>
|
||||||
|
)}
|
||||||
<ButtonWarning
|
<ButtonWarning
|
||||||
transparent
|
transparent
|
||||||
style={{ marginLeft: 8, color: Constants.system.white }}
|
style={{ marginLeft: 8, color: Constants.system.white }}
|
||||||
onClick={() => this._handleDownloadFiles()}
|
onClick={() => this._handleDownloadFiles()}
|
||||||
>
|
>
|
||||||
{Strings.pluralize("Download file", numChecked)}
|
Download
|
||||||
</ButtonWarning>
|
</ButtonWarning>
|
||||||
{this.props.isOwner && (
|
{this.props.isOwner && (
|
||||||
<ButtonWarning
|
<ButtonWarning
|
||||||
@ -677,14 +681,14 @@ export default class DataView extends React.Component {
|
|||||||
style={{ marginLeft: 8, color: Constants.system.white }}
|
style={{ marginLeft: 8, color: Constants.system.white }}
|
||||||
onClick={() => this.setState({ modalShow: true })}
|
onClick={() => this.setState({ modalShow: true })}
|
||||||
>
|
>
|
||||||
{Strings.pluralize("Delete file", numChecked)}
|
Delete
|
||||||
</ButtonWarning>
|
</ButtonWarning>
|
||||||
)}
|
)}
|
||||||
{this.state.modalShow && (
|
{this.state.modalShow && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
type={"DELETE"}
|
type={"DELETE"}
|
||||||
withValidation={false}
|
withValidation={false}
|
||||||
callback={this._handleDelete}
|
callback={this._handleDelete}
|
||||||
header={`Are you sure you want to delete the selected files?`}
|
header={`Are you sure you want to delete the selected files?`}
|
||||||
subHeader={`These files will be deleted from all connected collections and your file library. You can’t undo this action.`}
|
subHeader={`These files will be deleted from all connected collections and your file library. You can’t undo this action.`}
|
||||||
/>
|
/>
|
||||||
@ -704,129 +708,67 @@ export default class DataView extends React.Component {
|
|||||||
) : null}
|
) : null}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
if (this.props.view === 0) {
|
if (this.props.view === "grid") {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<GroupSelectable onSelection={this._handleDragAndSelect}>
|
<GroupSelectable onSelection={this._handleDragAndSelect}>
|
||||||
<div css={STYLES_IMAGE_GRID} ref={this.gridWrapperEl}>
|
<div css={STYLES_IMAGE_GRID} ref={this.gridWrapperEl}>
|
||||||
{this.props.items.slice(0, this.state.viewLimit).map((each, i) => {
|
{this.props.items.slice(0, this.state.viewLimit).map((each, i) => {
|
||||||
const cid = each.cid;
|
|
||||||
return (
|
return (
|
||||||
<Selectable
|
<Link
|
||||||
key={each.id}
|
key={each.id}
|
||||||
draggable={!numChecked}
|
redirect
|
||||||
onDragStart={(e) => {
|
params={{ ...this.props.page?.params, cid: each.cid }}
|
||||||
this._disableDragAndDropUploadEvent();
|
onAction={this.props.onAction}
|
||||||
this._handleDragToDesktop(e, each);
|
|
||||||
}}
|
|
||||||
onDragEnd={this._enableDragAndDropUploadEvent}
|
|
||||||
selectableKey={i}
|
|
||||||
css={STYLES_IMAGE_BOX}
|
|
||||||
style={{
|
|
||||||
width: this.state.imageSize,
|
|
||||||
height: this.state.imageSize,
|
|
||||||
boxShadow: numChecked
|
|
||||||
? `0px 0px 0px 1px ${Constants.system.lightBorder} inset,
|
|
||||||
0 0 40px 0 ${Constants.system.shadow}`
|
|
||||||
: "",
|
|
||||||
}}
|
|
||||||
onClick={() => this._handleSelect(i)}
|
|
||||||
onMouseEnter={() => this._handleCheckBoxMouseEnter(i)}
|
|
||||||
onMouseLeave={() => this._handleCheckBoxMouseLeave(i)}
|
|
||||||
>
|
>
|
||||||
<SlateMediaObjectPreview file={each} />
|
<Selectable
|
||||||
<span css={STYLES_MOBILE_HIDDEN} style={{ pointerEvents: "auto" }}>
|
key={each.id}
|
||||||
{numChecked || this.state.hover === i || this.state.menu === each.id ? (
|
draggable={!numChecked}
|
||||||
<React.Fragment>
|
onDragStart={(e) => {
|
||||||
{/* <div
|
this._disableDragAndDropUploadEvent();
|
||||||
css={STYLES_ICON_BOX_BACKGROUND}
|
this._handleDragToDesktop(e, each);
|
||||||
onClick={(e) => {
|
}}
|
||||||
e.stopPropagation();
|
onDragEnd={this._enableDragAndDropUploadEvent}
|
||||||
this.setState({
|
selectableKey={i}
|
||||||
menu: this.state.menu === each.id ? null : each.id,
|
css={STYLES_IMAGE_BOX}
|
||||||
});
|
style={{
|
||||||
}}
|
width: this.state.imageSize,
|
||||||
>
|
height: this.state.imageSize,
|
||||||
<SVG.MoreHorizontal height="24px" />
|
boxShadow: numChecked
|
||||||
{this.state.menu === each.id ? (
|
? `0px 0px 0px 1px ${Constants.system.lightBorder} inset,
|
||||||
<Boundary
|
0 0 40px 0 ${Constants.system.shadow}`
|
||||||
captureResize={true}
|
: "",
|
||||||
captureScroll={false}
|
}}
|
||||||
enabled
|
onMouseEnter={() => this._handleCheckBoxMouseEnter(i)}
|
||||||
onOutsideRectEvent={this._handleHide}
|
onMouseLeave={() => this._handleCheckBoxMouseLeave(i)}
|
||||||
>
|
>
|
||||||
{this.props.isOwner ? (
|
<SlateMediaObjectPreview file={each} />
|
||||||
<PopoverNavigation
|
<span css={STYLES_MOBILE_HIDDEN} style={{ pointerEvents: "auto" }}>
|
||||||
style={{
|
{numChecked || this.state.hover === i || this.state.menu === each.id ? (
|
||||||
top: "32px",
|
<React.Fragment>
|
||||||
right: "0px",
|
<div onClick={(e) => this._handleCheckBox(e, i)}>
|
||||||
}}
|
<CheckBox
|
||||||
navigation={[
|
name={i}
|
||||||
{
|
value={!!this.state.checked[i]}
|
||||||
text: "Copy CID",
|
boxStyle={{
|
||||||
onClick: (e) => this._handleCopy(e, cid),
|
height: 24,
|
||||||
},
|
width: 24,
|
||||||
{
|
backgroundColor: this.state.checked[i]
|
||||||
text: "Copy link",
|
? Constants.system.brand
|
||||||
onClick: (e) =>
|
: "rgba(255, 255, 255, 0.75)",
|
||||||
this._handleCopy(e, Strings.getURLfromCID(cid)),
|
}}
|
||||||
},
|
style={{
|
||||||
{
|
position: "absolute",
|
||||||
text: "Delete",
|
bottom: 8,
|
||||||
onClick: (e) => {
|
left: 8,
|
||||||
e.stopPropagation();
|
}}
|
||||||
this.setState({ menu: null }, () =>
|
/>
|
||||||
this._handleDelete(cid, each.id)
|
</div>
|
||||||
);
|
</React.Fragment>
|
||||||
},
|
) : null}
|
||||||
},
|
</span>
|
||||||
]}
|
</Selectable>
|
||||||
/>
|
</Link>
|
||||||
) : (
|
|
||||||
<PopoverNavigation
|
|
||||||
style={{
|
|
||||||
top: "32px",
|
|
||||||
right: "0px",
|
|
||||||
}}
|
|
||||||
navigation={[
|
|
||||||
{
|
|
||||||
text: "Copy CID",
|
|
||||||
onClick: (e) => this._handleCopy(e, cid),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Copy link",
|
|
||||||
onClick: (e) =>
|
|
||||||
this._handleCopy(e, Strings.getURLfromCID(cid)),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Boundary>
|
|
||||||
) : null}
|
|
||||||
</div> */}
|
|
||||||
|
|
||||||
<div onClick={(e) => this._handleCheckBox(e, i)}>
|
|
||||||
<CheckBox
|
|
||||||
name={i}
|
|
||||||
value={!!this.state.checked[i]}
|
|
||||||
boxStyle={{
|
|
||||||
height: 24,
|
|
||||||
width: 24,
|
|
||||||
backgroundColor: this.state.checked[i]
|
|
||||||
? Constants.system.brand
|
|
||||||
: "rgba(255, 255, 255, 0.75)",
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
bottom: 8,
|
|
||||||
left: 8,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
) : null}
|
|
||||||
</span>
|
|
||||||
</Selectable>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{[0, 1, 2, 3].map((i) => (
|
{[0, 1, 2, 3].map((i) => (
|
||||||
@ -923,18 +865,24 @@ export default class DataView extends React.Component {
|
|||||||
onDragEnd={this._enableDragAndDropUploadEvent}
|
onDragEnd={this._enableDragAndDropUploadEvent}
|
||||||
>
|
>
|
||||||
<FilePreviewBubble cid={cid} type={each.data.type}>
|
<FilePreviewBubble cid={cid} type={each.data.type}>
|
||||||
<div css={STYLES_CONTAINER_HOVER} onClick={() => this._handleSelect(index)}>
|
<Link
|
||||||
<div css={STYLES_ICON_BOX_HOVER} style={{ paddingLeft: 0, paddingRight: 18 }}>
|
redirect
|
||||||
<FileTypeIcon type={each.data.type} height="24px" />
|
params={{ ...this.props.page.params, cid: each.cid }}
|
||||||
|
onAction={this.props.onAction}
|
||||||
|
>
|
||||||
|
<div css={STYLES_CONTAINER_HOVER}>
|
||||||
|
<div css={STYLES_ICON_BOX_HOVER} style={{ paddingLeft: 0, paddingRight: 18 }}>
|
||||||
|
<FileTypeIcon type={each.data.type} height="24px" />
|
||||||
|
</div>
|
||||||
|
<div css={STYLES_LINK}>{each.data.name || each.filename}</div>
|
||||||
</div>
|
</div>
|
||||||
<div css={STYLES_LINK}>{each.data.name || each.filename}</div>
|
</Link>
|
||||||
</div>
|
|
||||||
</FilePreviewBubble>
|
</FilePreviewBubble>
|
||||||
</Selectable>
|
</Selectable>
|
||||||
),
|
),
|
||||||
tags: <>{each.data.tags?.length && <Tags tags={each.data.tags} />}</>,
|
tags: <>{each.data.tags?.length && <Tags tags={each.data.tags} />}</>,
|
||||||
size: <div css={STYLES_VALUE}>{Strings.bytesToSize(each.data.size)}</div>,
|
size: <div css={STYLES_VALUE}>{Strings.bytesToSize(each.data.size)}</div>,
|
||||||
more: (
|
more: this.props.isOwner ? (
|
||||||
<div
|
<div
|
||||||
css={STYLES_ICON_BOX_HOVER}
|
css={STYLES_ICON_BOX_HOVER}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@ -957,27 +905,29 @@ export default class DataView extends React.Component {
|
|||||||
right: "40px",
|
right: "40px",
|
||||||
}}
|
}}
|
||||||
navigation={[
|
navigation={[
|
||||||
{
|
[
|
||||||
text: "Copy CID",
|
{
|
||||||
onClick: (e) => this._handleCopy(e, cid),
|
text: "Copy CID",
|
||||||
},
|
onClick: (e) => this._handleCopy(e, cid),
|
||||||
// {
|
|
||||||
// text: "Copy link",
|
|
||||||
// onClick: (e) => this._handleCopy(e, Strings.getURLfromCID(cid)),
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
text: "Delete",
|
|
||||||
onClick: (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
this.setState({ menu: null, modalShow: true });
|
|
||||||
},
|
},
|
||||||
},
|
// {
|
||||||
|
// text: "Copy link",
|
||||||
|
// onClick: (e) => this._handleCopy(e, Strings.getURLfromCID(cid)),
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
text: "Delete",
|
||||||
|
onClick: (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.setState({ menu: null, modalShow: true });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Boundary>
|
</Boundary>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
),
|
) : null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@ const withView = (Component) => (props) => {
|
|||||||
|
|
||||||
const [isIntersecting, setIntersecting] = React.useState(false);
|
const [isIntersecting, setIntersecting] = React.useState(false);
|
||||||
|
|
||||||
const observer = new IntersectionObserver(([entry]) => {
|
|
||||||
if (entry.isIntersecting) setIntersecting(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
const observer = new IntersectionObserver(([entry]) => {
|
||||||
|
if (entry.isIntersecting) setIntersecting(true);
|
||||||
|
});
|
||||||
|
|
||||||
observer.observe(ref.current);
|
observer.observe(ref.current);
|
||||||
return () => {
|
return () => {
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
|
83
components/core/Link.js
Normal file
83
components/core/Link.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import * as Strings from "~/common/strings";
|
||||||
|
|
||||||
|
export class Link extends React.Component {
|
||||||
|
state = {
|
||||||
|
href: this.props.href
|
||||||
|
? this.props.href
|
||||||
|
: this.props.params
|
||||||
|
? Strings.getQueryStringFromParams(this.props.params)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
onUpdate: () => {
|
||||||
|
console.log(
|
||||||
|
`ERROR: onUpdate is missing from a Link object called with href ${this.props.href}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
_handleClick = (e) => {
|
||||||
|
if (!this.state.href) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLeftClick = event.button === 0; //NOTE(martina): should process right clicks normally
|
||||||
|
|
||||||
|
const isModified = event.metaKey || event.altKey || event.ctrlKey || event.shiftKey; //NOTE(martina): should process ctrl/shift/etc + click normally
|
||||||
|
|
||||||
|
const hasNoTarget = !this.props.target; //NOTE(martina): should process normally if specify opening in another tab etc.
|
||||||
|
|
||||||
|
if (!isLeftClick || isModified || !hasNoTarget) {
|
||||||
|
return; //NOTE(martina): always allow ctrl + click or right click to continue, and don't trigger an onClick
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault(); //NOTE(martina): prevents the anchor component from getting clicked, so that we can handle the behavior manually
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (this.props.onClick) {
|
||||||
|
this.props.onClick(); //NOTE(martina): onClick is triggered whether or not it is disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.disabled) {
|
||||||
|
return; //NOTE(martina): disabled = true disables the onAction from firing
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.href) {
|
||||||
|
this.props.onAction({
|
||||||
|
type: "NAVIGATE",
|
||||||
|
href: this.props.href,
|
||||||
|
callback: this.props.callback,
|
||||||
|
redirect: this.props.redirect,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.props.onAction({
|
||||||
|
type: "UPDATE_PARAMS",
|
||||||
|
params: this.props.params,
|
||||||
|
callback: this.props.callback,
|
||||||
|
redirect: this.props.redirect,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<span onClick={this._handleClick}>
|
||||||
|
<a
|
||||||
|
style={{
|
||||||
|
textDecoration: "none",
|
||||||
|
color: "inherit",
|
||||||
|
cursor: "pointer",
|
||||||
|
...this.props.style,
|
||||||
|
}}
|
||||||
|
css={this.props.css}
|
||||||
|
target={this.props.target}
|
||||||
|
href={this.state.href}
|
||||||
|
>
|
||||||
|
{this.props.children}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import * as Constants from "~/common/constants";
|
|
||||||
|
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { Logo } from "~/common/logo.js";
|
import { Logo } from "~/common/logo.js";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
|
import * as Constants from "~/common/constants";
|
||||||
|
|
||||||
const STYLES_ROOT = css`
|
const STYLES_ROOT = css`
|
||||||
position: -webkit-sticky;
|
position: -webkit-sticky;
|
||||||
@ -195,7 +196,7 @@ const NewWebsitePrototypeHeader = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const communityURL = "https://github.com/filecoin-project/slate";
|
const communityURL = "https://github.com/filecoin-project/slate";
|
||||||
const signInURL = "/_";
|
const signInURL = "/_/auth";
|
||||||
const styleMenu = open ? openMenu : null;
|
const styleMenu = open ? openMenu : null;
|
||||||
const styleBurgerBun = open ? openBurgerBun : null;
|
const styleBurgerBun = open ? openBurgerBun : null;
|
||||||
const styleBurgerBun2 = open ? openBurgerBun2 : null;
|
const styleBurgerBun2 = open ? openBurgerBun2 : null;
|
||||||
|
@ -7,6 +7,8 @@ import * as Utilities from "~/common/utilities";
|
|||||||
import * as Events from "~/common/custom-events";
|
import * as Events from "~/common/custom-events";
|
||||||
import * as Window from "~/common/window";
|
import * as Window from "~/common/window";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { ButtonPrimary, ButtonSecondary } from "~/components/system/components/Buttons";
|
import { ButtonPrimary, ButtonSecondary } from "~/components/system/components/Buttons";
|
||||||
@ -239,48 +241,236 @@ const STYLES_DIRECTORY_NAME = css`
|
|||||||
// opacity: 0;
|
// opacity: 0;
|
||||||
// `;
|
// `;
|
||||||
|
|
||||||
function UserEntry({
|
function UserEntry({ user, button, onClick, message, checkStatus }) {
|
||||||
user,
|
|
||||||
button,
|
|
||||||
onClick,
|
|
||||||
message,
|
|
||||||
external,
|
|
||||||
url,
|
|
||||||
checkStatus,
|
|
||||||
showStatusIndicator,
|
|
||||||
}) {
|
|
||||||
const isOnline = checkStatus({ id: user.id });
|
const isOnline = checkStatus({ id: user.id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={user.username} css={STYLES_USER_ENTRY}>
|
<div key={user.username} css={STYLES_USER_ENTRY}>
|
||||||
{external ? (
|
<div css={STYLES_USER} onClick={onClick}>
|
||||||
<a css={STYLES_USER} style={{ textDecoration: "none" }} href={url}>
|
<div
|
||||||
<div
|
css={STYLES_DIRECTORY_PROFILE_IMAGE}
|
||||||
css={STYLES_DIRECTORY_PROFILE_IMAGE}
|
style={{ backgroundImage: `url(${user.data.photo})` }}
|
||||||
style={{ backgroundImage: `url(${user.data.photo})` }}
|
>
|
||||||
>
|
{isOnline && <div css={STYLES_DIRECTORY_STATUS_INDICATOR} />}
|
||||||
{showStatusIndicator && isOnline && <div css={STYLES_DIRECTORY_STATUS_INDICATOR} />}
|
|
||||||
</div>
|
|
||||||
<span css={STYLES_DIRECTORY_NAME}>
|
|
||||||
{user.data.name || `@${user.username}`}
|
|
||||||
{message ? <span css={STYLES_MESSAGE}>{message}</span> : null}
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
<div css={STYLES_USER} onClick={onClick}>
|
|
||||||
<div
|
|
||||||
css={STYLES_DIRECTORY_PROFILE_IMAGE}
|
|
||||||
style={{ backgroundImage: `url(${user.data.photo})` }}
|
|
||||||
>
|
|
||||||
{isOnline && <div css={STYLES_DIRECTORY_STATUS_INDICATOR} />}
|
|
||||||
</div>
|
|
||||||
<span css={STYLES_DIRECTORY_NAME}>
|
|
||||||
{user.data.name || `@${user.username}`}
|
|
||||||
{message ? <span css={STYLES_MESSAGE}>{message}</span> : null}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span css={STYLES_DIRECTORY_NAME}>
|
||||||
|
{user.data.name || `@${user.username}`}
|
||||||
|
{message ? <span css={STYLES_MESSAGE}>{message}</span> : null}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{button}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FilesPage({
|
||||||
|
library,
|
||||||
|
isOwner,
|
||||||
|
isMobile,
|
||||||
|
viewer,
|
||||||
|
onAction,
|
||||||
|
resources,
|
||||||
|
page,
|
||||||
|
tab = "grid",
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{isMobile ? null : (
|
||||||
|
<SecondaryTabGroup
|
||||||
|
tabs={[
|
||||||
|
{
|
||||||
|
title: <SVG.GridView height="24px" style={{ display: "block" }} />,
|
||||||
|
value: { tab: "grid", subtab: "files" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: <SVG.TableView height="24px" style={{ display: "block" }} />,
|
||||||
|
value: { tab: "table", subtab: "files" },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
value={tab}
|
||||||
|
onAction={onAction}
|
||||||
|
style={{ margin: "0 0 24px 0" }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{external ? null : button}
|
{library.length ? (
|
||||||
|
<DataView
|
||||||
|
key="scene-profile"
|
||||||
|
onAction={onAction}
|
||||||
|
viewer={viewer}
|
||||||
|
isOwner={isOwner}
|
||||||
|
items={library}
|
||||||
|
view={tab}
|
||||||
|
resources={resources}
|
||||||
|
page={page}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<EmptyState>
|
||||||
|
<FileTypeGroup />
|
||||||
|
<div style={{ marginTop: 24 }}>This user does not have any public files yet</div>
|
||||||
|
</EmptyState>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CollectionsPage({
|
||||||
|
user,
|
||||||
|
viewer,
|
||||||
|
fetched,
|
||||||
|
subscriptions,
|
||||||
|
tab = "collections",
|
||||||
|
isOwner,
|
||||||
|
onAction,
|
||||||
|
}) {
|
||||||
|
let slates = [];
|
||||||
|
if (tab === "collections") {
|
||||||
|
slates = user.slates
|
||||||
|
? isOwner
|
||||||
|
? user.slates.filter((slate) => slate.isPublic === true)
|
||||||
|
: user.slates
|
||||||
|
: slates;
|
||||||
|
} else {
|
||||||
|
slates = subscriptions;
|
||||||
|
}
|
||||||
|
slates = slates || [];
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SecondaryTabGroup
|
||||||
|
tabs={[
|
||||||
|
{ title: "Collections", value: { tab: "collections", subtab: "collections" } },
|
||||||
|
{ title: "Subscribed", value: { tab: "subscribed", subtab: "collections" } },
|
||||||
|
]}
|
||||||
|
value={tab}
|
||||||
|
onAction={onAction}
|
||||||
|
style={{ margin: "0 0 24px 0" }}
|
||||||
|
/>
|
||||||
|
{slates?.length ? (
|
||||||
|
<SlatePreviewBlocks external={!viewer} slates={slates || []} onAction={onAction} />
|
||||||
|
) : (
|
||||||
|
<EmptyState>
|
||||||
|
{tab === "collections" || fetched ? (
|
||||||
|
<React.Fragment>
|
||||||
|
<SVG.Slate height="24px" style={{ marginBottom: 24 }} />
|
||||||
|
{tab === "collections"
|
||||||
|
? `This user does not have any public collections yet`
|
||||||
|
: `This user is not following any collections yet`}
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<LoaderSpinner style={{ height: 24, width: 24 }} />
|
||||||
|
)}
|
||||||
|
</EmptyState>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PeersPage({
|
||||||
|
checkStatus,
|
||||||
|
viewer,
|
||||||
|
following,
|
||||||
|
followers,
|
||||||
|
fetched,
|
||||||
|
tab = "following",
|
||||||
|
onAction,
|
||||||
|
onLoginModal,
|
||||||
|
}) {
|
||||||
|
const [selectedUser, setSelectedUser] = useState(false);
|
||||||
|
const selectUser = (e, id) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
if (!id || selectedUser === id) {
|
||||||
|
setSelectedUser(null);
|
||||||
|
} else {
|
||||||
|
setSelectedUser(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const followUser = async (e, id) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
selectUser(e, null);
|
||||||
|
if (!viewer) {
|
||||||
|
onLoginModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await Actions.createSubscription({
|
||||||
|
userId: id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let peers = tab === "following" ? following : followers;
|
||||||
|
peers = peers.map((relation) => {
|
||||||
|
const following = !!(
|
||||||
|
viewer &&
|
||||||
|
viewer.following.some((subscription) => {
|
||||||
|
return subscription.id === relation.id;
|
||||||
|
}).length
|
||||||
|
);
|
||||||
|
let button =
|
||||||
|
!viewer || relation.id !== viewer?.id ? (
|
||||||
|
<div css={STYLES_ITEM_BOX} onClick={(e) => selectUser(e, relation.id)}>
|
||||||
|
<SVG.MoreHorizontal height="24px" />
|
||||||
|
{selectedUser === relation.id ? (
|
||||||
|
<Boundary
|
||||||
|
captureResize={true}
|
||||||
|
captureScroll={false}
|
||||||
|
enabled
|
||||||
|
onOutsideRectEvent={(e) => selectUser(e)}
|
||||||
|
>
|
||||||
|
<PopoverNavigation
|
||||||
|
style={{
|
||||||
|
top: "40px",
|
||||||
|
right: "0px",
|
||||||
|
}}
|
||||||
|
navigation={[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: following ? "Unfollow" : "Follow",
|
||||||
|
onClick: (e) => followUser(e, relation.id),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Boundary>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={`/$/user/${relation.id}`} onAction={onAction}>
|
||||||
|
<UserEntry key={relation.id} user={relation} button={button} checkStatus={checkStatus} />
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<SecondaryTabGroup
|
||||||
|
tabs={[
|
||||||
|
{ title: "Following", value: { tab: "following", subtab: "peers" } },
|
||||||
|
{ title: "Followers", value: { tab: "followers", subtab: "peers" } },
|
||||||
|
]}
|
||||||
|
value={tab}
|
||||||
|
onAction={onAction}
|
||||||
|
style={{ margin: "0 0 24px 0" }}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
{peers?.length ? (
|
||||||
|
peers
|
||||||
|
) : (
|
||||||
|
<EmptyState>
|
||||||
|
{fetched ? (
|
||||||
|
<React.Fragment>
|
||||||
|
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
||||||
|
{tab === "following"
|
||||||
|
? `This user is not following anyone yet`
|
||||||
|
: `This user does not have any followers yet`}
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<LoaderSpinner style={{ height: 24, width: 24 }} />
|
||||||
|
)}
|
||||||
|
</EmptyState>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -289,12 +479,7 @@ export default class Profile extends React.Component {
|
|||||||
_ref = null;
|
_ref = null;
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
view: 0,
|
|
||||||
slateTab: 0,
|
|
||||||
peerTab: 0,
|
|
||||||
// copyValue: "",
|
|
||||||
contextMenu: null,
|
contextMenu: null,
|
||||||
slates: this.props.user.slates,
|
|
||||||
subscriptions: [],
|
subscriptions: [],
|
||||||
followers: [],
|
followers: [],
|
||||||
following: [],
|
following: [],
|
||||||
@ -305,21 +490,22 @@ export default class Profile extends React.Component {
|
|||||||
return entry.id === this.props.user.id;
|
return entry.id === this.props.user.id;
|
||||||
}),
|
}),
|
||||||
fetched: false,
|
fetched: false,
|
||||||
tab: this.props.tab || 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount = () => {
|
componentDidMount = () => {
|
||||||
this._handleUpdatePage();
|
this.fetchSocial();
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidUpdate = (prevProps) => {
|
componentDidUpdate = (prevProps) => {
|
||||||
if (this.props.page?.tab !== prevProps.page?.tab) {
|
if (!this.state.fetched && this.props.page.params !== prevProps.page.params) {
|
||||||
this.setState({ tab: this.props.page.tab });
|
this.fetchSocial();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchSocial = async () => {
|
fetchSocial = async () => {
|
||||||
if (this.state.fetched) return;
|
if (this.state.fetched) return;
|
||||||
|
if (this.props.page.params?.subtab !== "peers" && this.props.page.params?.tab !== "subscribed")
|
||||||
|
return;
|
||||||
let following, followers, subscriptions;
|
let following, followers, subscriptions;
|
||||||
if (this.props.user.id === this.props.viewer?.id) {
|
if (this.props.user.id === this.props.viewer?.id) {
|
||||||
following = this.props.viewer?.following;
|
following = this.props.viewer?.following;
|
||||||
@ -343,31 +529,23 @@ export default class Profile extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// _handleCopy = (e, value) => {
|
|
||||||
// e.stopPropagation();
|
|
||||||
// this.setState({ copyValue: value }, () => {
|
|
||||||
// this._ref.select();
|
|
||||||
// document.execCommand("copy");
|
|
||||||
// this._handleHide();
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
|
|
||||||
_handleHide = (e) => {
|
_handleHide = (e) => {
|
||||||
this.setState({ contextMenu: null });
|
this.setState({ contextMenu: null });
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleClick = (e, value) => {
|
// _handleClick = (e, value) => {
|
||||||
e.stopPropagation();
|
// e.stopPropagation();
|
||||||
if (this.state.contextMenu === value) {
|
// if (this.state.contextMenu === value) {
|
||||||
this._handleHide();
|
// this._handleHide();
|
||||||
} else {
|
// } else {
|
||||||
this.setState({ contextMenu: value });
|
// this.setState({ contextMenu: value });
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
_handleFollow = async (e, id) => {
|
_handleFollow = async (e, id) => {
|
||||||
if (this.props.external) {
|
if (this.props.external) {
|
||||||
this._handleRedirectToInternal();
|
this._handleLoginModal();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this._handleHide();
|
this._handleHide();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -376,33 +554,12 @@ export default class Profile extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleRedirectToInternal = () => {
|
_handleLoginModal = (e) => {
|
||||||
this.setState({ visible: true });
|
if (e) {
|
||||||
};
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
_handleSwitchTab = (tab) => {
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
this.setState({ tab });
|
|
||||||
window.history.pushState({ ...window.history.state, tab }, "", window.location.pathname);
|
|
||||||
}
|
}
|
||||||
if (tab === 2 && !this.state.fetched) {
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
this.fetchSocial();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_handleUpdatePage = () => {
|
|
||||||
let tab;
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
tab = window?.history?.state.tab;
|
|
||||||
}
|
|
||||||
if (typeof tab === "undefined") {
|
|
||||||
tab = 0;
|
|
||||||
}
|
|
||||||
this.setState({ tab }, () => {
|
|
||||||
if (this.state.tab === 2 || (this.state.tab === 1 && this.state.slateTab === 1)) {
|
|
||||||
this.fetchSocial();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
checkStatus = ({ id }) => {
|
checkStatus = ({ id }) => {
|
||||||
@ -411,81 +568,15 @@ export default class Profile extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let tab = this.state.tab || 0;
|
let subtab = this.props.page.params?.subtab
|
||||||
let publicFiles = this.props.user.library;
|
? this.props.page.params?.subtab
|
||||||
|
: this.props.page.params?.cid
|
||||||
|
? "files"
|
||||||
|
: "collections";
|
||||||
|
let tab = this.props.page.params?.tab;
|
||||||
|
let library = this.props.user.library;
|
||||||
let isOwner = this.props.isOwner;
|
let isOwner = this.props.isOwner;
|
||||||
let user = this.props.user;
|
let user = this.props.user;
|
||||||
let username = this.state.slateTab === 0 ? user.username : null;
|
|
||||||
let slates = [];
|
|
||||||
if (tab === 1) {
|
|
||||||
if (this.state.slateTab === 0) {
|
|
||||||
slates = user.slates
|
|
||||||
? isOwner
|
|
||||||
? user.slates.filter((slate) => slate.isPublic === true)
|
|
||||||
: user.slates
|
|
||||||
: null;
|
|
||||||
} else {
|
|
||||||
slates = this.state.subscriptions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let exploreSlates = this.props.exploreSlates;
|
|
||||||
let peers = this.state.peerTab === 0 ? this.state.following : this.state.followers;
|
|
||||||
if (tab === 2) {
|
|
||||||
peers = peers.map((relation) => {
|
|
||||||
let button = (
|
|
||||||
<div css={STYLES_ITEM_BOX} onClick={(e) => this._handleClick(e, relation.id)}>
|
|
||||||
<SVG.MoreHorizontal height="24px" />
|
|
||||||
{this.state.contextMenu === relation.id ? (
|
|
||||||
<Boundary
|
|
||||||
captureResize={true}
|
|
||||||
captureScroll={false}
|
|
||||||
enabled
|
|
||||||
onOutsideRectEvent={(e) => this._handleClick(e, relation.id)}
|
|
||||||
>
|
|
||||||
<PopoverNavigation
|
|
||||||
style={{
|
|
||||||
top: "40px",
|
|
||||||
right: "0px",
|
|
||||||
}}
|
|
||||||
navigation={[
|
|
||||||
{
|
|
||||||
text: this.props.viewer?.following.some((subscription) => {
|
|
||||||
return subscription.id === relation.id;
|
|
||||||
}).length
|
|
||||||
? "Unfollow"
|
|
||||||
: "Follow",
|
|
||||||
onClick: this.props.viewer
|
|
||||||
? (e) => this._handleFollow(e, relation.id)
|
|
||||||
: () => this.setState({ visible: true }),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Boundary>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<UserEntry
|
|
||||||
key={relation.id}
|
|
||||||
user={relation}
|
|
||||||
button={button}
|
|
||||||
checkStatus={this.checkStatus}
|
|
||||||
showStatusIndicator={this.props.isAuthenticated}
|
|
||||||
onClick={() => {
|
|
||||||
this.props.onAction({
|
|
||||||
type: "NAVIGATE",
|
|
||||||
value: this.props.sceneId,
|
|
||||||
scene: "PROFILE",
|
|
||||||
data: relation,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
external={this.props.external}
|
|
||||||
url={`/${relation.username}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const showStatusIndicator = this.props.isAuthenticated;
|
const showStatusIndicator = this.props.isAuthenticated;
|
||||||
|
|
||||||
@ -493,14 +584,14 @@ export default class Profile extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<GlobalCarousel
|
<GlobalCarousel
|
||||||
carouselType="PROFILE"
|
carouselType="PROFILE"
|
||||||
onUpdateViewer={this.props.onUpdateViewer}
|
|
||||||
resources={this.props.resources}
|
resources={this.props.resources}
|
||||||
viewer={this.props.viewer}
|
viewer={this.props.viewer}
|
||||||
objects={publicFiles}
|
objects={library}
|
||||||
isOwner={this.props.isOwner}
|
isOwner={this.props.isOwner}
|
||||||
onAction={this.props.onAction}
|
onAction={this.props.onAction}
|
||||||
isMobile={this.props.isMobile}
|
isMobile={this.props.isMobile}
|
||||||
external={this.props.external}
|
external={this.props.external}
|
||||||
|
params={this.props.page.params}
|
||||||
/>
|
/>
|
||||||
<div css={STYLES_PROFILE_BACKGROUND}>
|
<div css={STYLES_PROFILE_BACKGROUND}>
|
||||||
<div css={STYLES_PROFILE_INFO}>
|
<div css={STYLES_PROFILE_INFO}>
|
||||||
@ -547,7 +638,7 @@ export default class Profile extends React.Component {
|
|||||||
<div css={STYLES_STATS}>
|
<div css={STYLES_STATS}>
|
||||||
<div css={STYLES_STAT}>
|
<div css={STYLES_STAT}>
|
||||||
<div style={{ fontFamily: `${Constants.font.text}` }}>
|
<div style={{ fontFamily: `${Constants.font.text}` }}>
|
||||||
{publicFiles.length}{" "}
|
{library.length}{" "}
|
||||||
<span style={{ color: `${Constants.system.darkGray}` }}>Files</span>
|
<span style={{ color: `${Constants.system.darkGray}` }}>Files</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -561,162 +652,37 @@ export default class Profile extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.state.visible && (
|
|
||||||
<div>
|
|
||||||
<CTATransition
|
|
||||||
onClose={() => this.setState({ visible: false })}
|
|
||||||
viewer={this.props.viewer}
|
|
||||||
open={this.state.visible}
|
|
||||||
redirectURL={`/_${Strings.createQueryParams({
|
|
||||||
scene: "NAV_PROFILE",
|
|
||||||
user: user.username,
|
|
||||||
})}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div css={STYLES_PROFILE}>
|
<div css={STYLES_PROFILE}>
|
||||||
<TabGroup
|
<TabGroup
|
||||||
tabs={["Files", "Collections", "Peers"]}
|
tabs={[
|
||||||
value={tab}
|
{ title: "Files", value: { subtab: "files" } },
|
||||||
onChange={this._handleSwitchTab}
|
{ title: "Collections", value: { subtab: "collections" } },
|
||||||
|
{ title: "Peers", value: { subtab: "peers" } },
|
||||||
|
]}
|
||||||
|
value={subtab}
|
||||||
|
onAction={this.props.onAction}
|
||||||
style={{ marginTop: 0, marginBottom: 32 }}
|
style={{ marginTop: 0, marginBottom: 32 }}
|
||||||
itemStyle={{ margin: "0px 16px" }}
|
itemStyle={{ margin: "0px 16px" }}
|
||||||
/>
|
/>
|
||||||
{tab === 0 ? (
|
{subtab === "files" ? <FilesPage {...this.props} library={library} tab={tab} /> : null}
|
||||||
<div>
|
{subtab === "collections" ? (
|
||||||
{this.props.isMobile ? null : (
|
<CollectionsPage
|
||||||
<div style={{ display: `flex` }}>
|
{...this.props}
|
||||||
<SecondaryTabGroup
|
tab={tab}
|
||||||
tabs={[
|
fetched={this.state.fetched}
|
||||||
<SVG.GridView height="24px" style={{ display: "block" }} />,
|
subscriptions={this.state.subscriptions}
|
||||||
<SVG.TableView height="24px" style={{ display: "block" }} />,
|
/>
|
||||||
]}
|
|
||||||
value={this.state.view}
|
|
||||||
onChange={(value) => this.setState({ view: value })}
|
|
||||||
style={{ margin: "0 0 24px 0", justifyContent: "flex-end" }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{publicFiles.length ? (
|
|
||||||
<DataView
|
|
||||||
key="scene-profile"
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
viewer={this.props.viewer}
|
|
||||||
isOwner={isOwner}
|
|
||||||
items={publicFiles}
|
|
||||||
onUpdateViewer={this.props.onUpdateViewer}
|
|
||||||
view={this.state.view}
|
|
||||||
resources={this.props.resources}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<EmptyState>
|
|
||||||
<FileTypeGroup />
|
|
||||||
<div style={{ marginTop: 24 }}>This user does not have any public files yet</div>
|
|
||||||
</EmptyState>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
) : null}
|
||||||
{tab === 1 ? (
|
{subtab === "peers" ? (
|
||||||
<div>
|
<PeersPage
|
||||||
<SecondaryTabGroup
|
{...this.props}
|
||||||
tabs={["Collections", "Following"]}
|
tab={tab}
|
||||||
value={this.state.slateTab}
|
onLoginModal={this._handleLoginModal}
|
||||||
onChange={(value) => {
|
checkStatus={this.checkStatus}
|
||||||
this.setState({ slateTab: value }, () => {
|
following={this.state.following}
|
||||||
if (!this.state.fetched) {
|
followers={this.state.followers}
|
||||||
this.fetchSocial();
|
fetched={this.state.fetched}
|
||||||
}
|
/>
|
||||||
});
|
|
||||||
}}
|
|
||||||
style={{ margin: "0 0 24px 0" }}
|
|
||||||
/>
|
|
||||||
{slates?.length ? (
|
|
||||||
<SlatePreviewBlocks
|
|
||||||
isOwner={this.state.slateTab === 0 ? isOwner : false}
|
|
||||||
external={this.props.external}
|
|
||||||
slates={slates || []}
|
|
||||||
username={username}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<React.Fragment>
|
|
||||||
{this.props.external && exploreSlates.length != 0 ? (
|
|
||||||
<React.Fragment>
|
|
||||||
<EmptyState style={{ border: `none`, height: `120px` }}>
|
|
||||||
{this.state.fetched || this.state.slateTab == 0 ? (
|
|
||||||
<React.Fragment>
|
|
||||||
<SVG.Slate height="24px" style={{ marginBottom: 24 }} />
|
|
||||||
{this.state.slateTab === 0
|
|
||||||
? `This user does not have any public collections yet`
|
|
||||||
: `This user is not following any collections yet`}
|
|
||||||
</React.Fragment>
|
|
||||||
) : (
|
|
||||||
<LoaderSpinner style={{ height: 24, width: 24 }} />
|
|
||||||
)}
|
|
||||||
</EmptyState>
|
|
||||||
<div css={STYLES_EXPLORE}>Explore Collections</div>
|
|
||||||
<SlatePreviewBlocks
|
|
||||||
isOwner={false}
|
|
||||||
external={this.props.external}
|
|
||||||
slates={exploreSlates}
|
|
||||||
username={exploreSlates.username}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
/>
|
|
||||||
</React.Fragment>
|
|
||||||
) : (
|
|
||||||
<EmptyState>
|
|
||||||
{this.state.fetched || this.state.slateTab == 0 ? (
|
|
||||||
<React.Fragment>
|
|
||||||
<SVG.Slate height="24px" style={{ marginBottom: 24 }} />
|
|
||||||
{this.state.slateTab === 0
|
|
||||||
? `This user does not have any public collections yet`
|
|
||||||
: `This user is not following any collections yet`}
|
|
||||||
</React.Fragment>
|
|
||||||
) : (
|
|
||||||
<LoaderSpinner style={{ height: 24, width: 24 }} />
|
|
||||||
)}
|
|
||||||
</EmptyState>
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{tab === 2 ? (
|
|
||||||
<div>
|
|
||||||
<SecondaryTabGroup
|
|
||||||
tabs={["Following", "Followers"]}
|
|
||||||
value={this.state.peerTab}
|
|
||||||
onChange={(value) => this.setState({ peerTab: value })}
|
|
||||||
style={{ margin: "0 0 24px 0" }}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
{peers?.length ? (
|
|
||||||
peers
|
|
||||||
) : (
|
|
||||||
<EmptyState>
|
|
||||||
{this.state.fetched || this.state.slateTab == 0 ? (
|
|
||||||
<React.Fragment>
|
|
||||||
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
|
||||||
{this.state.peerTab === 0
|
|
||||||
? `This user is not following anyone yet`
|
|
||||||
: `This user does not have any followers yet`}
|
|
||||||
</React.Fragment>
|
|
||||||
) : (
|
|
||||||
<LoaderSpinner style={{ height: 24, width: 24 }} />
|
|
||||||
)}
|
|
||||||
</EmptyState>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{/* <input
|
|
||||||
readOnly
|
|
||||||
ref={(c) => {
|
|
||||||
this._ref = c;
|
|
||||||
}}
|
|
||||||
value={this.state.copyValue}
|
|
||||||
tabIndex="-1"
|
|
||||||
css={STYLES_COPY_INPUT}
|
|
||||||
/> */}
|
|
||||||
</div>
|
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,6 +9,7 @@ import * as Validations from "~/common/validations";
|
|||||||
import MiniSearch from "minisearch";
|
import MiniSearch from "minisearch";
|
||||||
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
|
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
|
||||||
|
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
||||||
@ -437,6 +438,21 @@ const STYLES_DISMISS_BOX = css`
|
|||||||
outline: 0;
|
outline: 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const getHref = (result) => {
|
||||||
|
if (result.type === "SLATE") {
|
||||||
|
return `/$/slate/${result.data.slate.id}`;
|
||||||
|
} else if (result.type === "USER") {
|
||||||
|
return `/$/user/${result.data.user.id}`;
|
||||||
|
} else if (result.type === "FILE") {
|
||||||
|
return `/$/user/${result.data.user.id}?cid=${result.data.file.cid}`;
|
||||||
|
} else if (result.type === "DATA_FILE") {
|
||||||
|
return `/_/data?cid=${result.data.file.cid}`;
|
||||||
|
} else {
|
||||||
|
console.log("GET HREF FAILED B/C RESULT WAS:");
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export class SearchModal extends React.Component {
|
export class SearchModal extends React.Component {
|
||||||
_input;
|
_input;
|
||||||
_optionRoot;
|
_optionRoot;
|
||||||
@ -484,24 +500,27 @@ export class SearchModal extends React.Component {
|
|||||||
this.debounceInstance = Window.debounce(() => {
|
this.debounceInstance = Window.debounce(() => {
|
||||||
this._handleSearch();
|
this._handleSearch();
|
||||||
}, 500);
|
}, 500);
|
||||||
let defaultResults = this.props.viewer.slates;
|
let defaultResults = this.props.viewer ? this.props.viewer.slates : [];
|
||||||
defaultResults = defaultResults.map((slate) => {
|
defaultResults = defaultResults.map((slate) => {
|
||||||
return {
|
return {
|
||||||
id: slate.id,
|
id: slate.id,
|
||||||
type: "SLATE",
|
type: "SLATE",
|
||||||
data: { slate: slate },
|
data: { slate },
|
||||||
component: <SlateEntry slate={slate} user={this.props.viewer} />,
|
component: <SlateEntry slate={slate} user={this.props.viewer} />,
|
||||||
preview: <SlatePreview slate={slate} user={this.props.viewer} />,
|
preview: <SlatePreview slate={slate} user={this.props.viewer} />,
|
||||||
|
href: `/$/slate/${slate.id}`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
this.setState({ defaultResults });
|
this.setState({ defaultResults });
|
||||||
let networkIds = [];
|
let networkIds = [];
|
||||||
let slateIds = [];
|
let slateIds = [];
|
||||||
for (let sub of this.props.viewer.subscriptions) {
|
if (this.props.viewer?.subscriptions) {
|
||||||
if (sub.target_user_id) {
|
for (let sub of this.props.viewer.subscriptions) {
|
||||||
networkIds.push(sub.target_user_id);
|
if (sub.target_user_id) {
|
||||||
} else if (sub.target_slate_id) {
|
networkIds.push(sub.target_user_id);
|
||||||
slateIds.push(sub.target_slate_id);
|
} else if (sub.target_slate_id) {
|
||||||
|
slateIds.push(sub.target_slate_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.networkIds = networkIds;
|
this.networkIds = networkIds;
|
||||||
@ -509,6 +528,9 @@ export class SearchModal extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fillLocalDirectory = () => {
|
fillLocalDirectory = () => {
|
||||||
|
if (!this.props.viewer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.localSearch = new MiniSearch({
|
this.localSearch = new MiniSearch({
|
||||||
fields: ["name", "title"],
|
fields: ["name", "title"],
|
||||||
storeFields: ["type", "data", "id"],
|
storeFields: ["type", "data", "id"],
|
||||||
@ -614,7 +636,9 @@ export class SearchModal extends React.Component {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
} else if (e.keyCode === 13) {
|
} else if (e.keyCode === 13) {
|
||||||
if (results.length > this.state.selectedIndex && this.state.selectedIndex >= 0) {
|
if (results.length > this.state.selectedIndex && this.state.selectedIndex >= 0) {
|
||||||
this._handleSelect(results[this.state.selectedIndex]);
|
let href = results[this.state.selectedIndex].href;
|
||||||
|
console.log("key down navigate");
|
||||||
|
this.props.onAction({ type: "NAVIGATE", href });
|
||||||
}
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
@ -629,7 +653,7 @@ export class SearchModal extends React.Component {
|
|||||||
let searchResults = [];
|
let searchResults = [];
|
||||||
let results = [];
|
let results = [];
|
||||||
let ids = new Set();
|
let ids = new Set();
|
||||||
if (this.state.typeFilter !== "USER") {
|
if (this.state.typeFilter !== "USER" && this.props.viewer) {
|
||||||
let filter;
|
let filter;
|
||||||
if (this.state.typeFilter === "FILE") {
|
if (this.state.typeFilter === "FILE") {
|
||||||
filter = {
|
filter = {
|
||||||
@ -686,7 +710,7 @@ export class SearchModal extends React.Component {
|
|||||||
file={item.data.file}
|
file={item.data.file}
|
||||||
slate={item.data.slate}
|
slate={item.data.slate}
|
||||||
user={this.props.viewer}
|
user={this.props.viewer}
|
||||||
viewerId={this.props.viewer.id}
|
viewerId={this.props.viewer?.id}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
@ -715,6 +739,7 @@ export class SearchModal extends React.Component {
|
|||||||
results.push({
|
results.push({
|
||||||
id,
|
id,
|
||||||
type: res.type,
|
type: res.type,
|
||||||
|
href: res.href,
|
||||||
data: res,
|
data: res,
|
||||||
component: <UserEntry user={res.user} />,
|
component: <UserEntry user={res.user} />,
|
||||||
preview: <UserPreview user={res.user} />,
|
preview: <UserPreview user={res.user} />,
|
||||||
@ -726,6 +751,7 @@ export class SearchModal extends React.Component {
|
|||||||
results.push({
|
results.push({
|
||||||
id,
|
id,
|
||||||
type: res.type,
|
type: res.type,
|
||||||
|
href: res.href,
|
||||||
data: res,
|
data: res,
|
||||||
component: <SlateEntry slate={res.slate} user={res.user} />,
|
component: <SlateEntry slate={res.slate} user={res.user} />,
|
||||||
preview: <SlatePreview slate={res.slate} user={res.user} />,
|
preview: <SlatePreview slate={res.slate} user={res.user} />,
|
||||||
@ -737,6 +763,7 @@ export class SearchModal extends React.Component {
|
|||||||
results.push({
|
results.push({
|
||||||
id,
|
id,
|
||||||
type: res.type,
|
type: res.type,
|
||||||
|
href: res.href,
|
||||||
data: res,
|
data: res,
|
||||||
component: <FileEntry file={res.file} />,
|
component: <FileEntry file={res.file} />,
|
||||||
preview: (
|
preview: (
|
||||||
@ -744,12 +771,16 @@ export class SearchModal extends React.Component {
|
|||||||
file={res.file}
|
file={res.file}
|
||||||
slate={res.slate}
|
slate={res.slate}
|
||||||
user={res.user}
|
user={res.user}
|
||||||
viewerId={this.props.viewer.id}
|
viewerId={this.props.viewer?.id}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
results = results.map((res) => {
|
||||||
|
return { ...res, href: getHref(res) };
|
||||||
|
});
|
||||||
|
console.log(results);
|
||||||
this.setState({ results, selectedIndex: 0 });
|
this.setState({ results, selectedIndex: 0 });
|
||||||
if (this._optionRoot) {
|
if (this._optionRoot) {
|
||||||
this._optionRoot.scrollTop = 0;
|
this._optionRoot.scrollTop = 0;
|
||||||
@ -757,6 +788,9 @@ export class SearchModal extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
processResults = (searchResults) => {
|
processResults = (searchResults) => {
|
||||||
|
if (!this.state.viewer) {
|
||||||
|
return searchResults;
|
||||||
|
}
|
||||||
let results = searchResults;
|
let results = searchResults;
|
||||||
if (this.state.scopeFilter === "MY") {
|
if (this.state.scopeFilter === "MY") {
|
||||||
results = results.filter((res) => {
|
results = results.filter((res) => {
|
||||||
@ -809,65 +843,65 @@ export class SearchModal extends React.Component {
|
|||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleSelect = async (res) => {
|
// _handleSelect = async (res) => {
|
||||||
if (res.type === "SLATE") {
|
// if (res.type === "SLATE") {
|
||||||
this.props.onAction({
|
// this.props.onAction({
|
||||||
type: "NAVIGATE",
|
// type: "NAVIGATE",
|
||||||
value: "NAV_SLATE",
|
// value: "NAV_SLATE",
|
||||||
data: res.data.slate,
|
// data: res.data.slate,
|
||||||
});
|
// });
|
||||||
} else if (res.type === "USER") {
|
// } else if (res.type === "USER") {
|
||||||
this.props.onAction({
|
// this.props.onAction({
|
||||||
type: "NAVIGATE",
|
// type: "NAVIGATE",
|
||||||
value: "NAV_PROFILE",
|
// value: "NAV_PROFILE",
|
||||||
data: res.data.user,
|
// data: res.data.user,
|
||||||
});
|
// });
|
||||||
} else if (res.type === "DATA_FILE" || res.data.file.ownerId === this.props.viewer.id) {
|
// } else if (res.type === "DATA_FILE" || res.data.file.ownerId === this.props.viewer?.id) {
|
||||||
await this.props.onAction({
|
// await this.props.onAction({
|
||||||
type: "NAVIGATE",
|
// type: "NAVIGATE",
|
||||||
value: "NAV_DATA",
|
// value: "NAV_DATA",
|
||||||
fileId: res.data.file.id,
|
// fileId: res.data.file.id,
|
||||||
});
|
// });
|
||||||
} else if (res.type === "FILE") {
|
// } else if (res.type === "FILE") {
|
||||||
await this.props.onAction({
|
// await this.props.onAction({
|
||||||
type: "NAVIGATE",
|
// type: "NAVIGATE",
|
||||||
value: "NAV_PROFILE",
|
// value: "NAV_PROFILE",
|
||||||
data: res.data.user,
|
// data: res.data.user,
|
||||||
fileId: res.data.file.id,
|
// fileId: res.data.file.id,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
this._handleHide();
|
// this._handleHide();
|
||||||
};
|
// };
|
||||||
|
|
||||||
_handleRedirect = async (destination) => {
|
_handleRedirect = async (destination) => {
|
||||||
if (destination === "FMU") {
|
// if (destination === "FMU") {
|
||||||
let isProd = window.location.hostname.includes("slate.host");
|
// let isProd = window.location.hostname.includes("slate.host");
|
||||||
this._handleSelect({
|
// this._handleSelect({
|
||||||
type: "FILE",
|
// type: "FILE",
|
||||||
data: {
|
// data: {
|
||||||
file: { id: "rick-roll" },
|
// file: { id: "rick-roll" },
|
||||||
slate: {
|
// slate: {
|
||||||
id: isProd
|
// id: isProd
|
||||||
? "01edcede-53c9-46b3-ac63-8f8479e10bcf"
|
// ? "01edcede-53c9-46b3-ac63-8f8479e10bcf"
|
||||||
: "60d199e7-6bf5-4994-94e8-b17547c64449",
|
// : "60d199e7-6bf5-4994-94e8-b17547c64449",
|
||||||
data: {
|
// data: {
|
||||||
objects: [
|
// objects: [
|
||||||
{
|
// {
|
||||||
id: "rick-roll",
|
// id: "rick-roll",
|
||||||
url:
|
// url:
|
||||||
"https://slate.textile.io/ipfs/bafybeifcxjvbad4lgpnbwff2dafufmnlylylmku4qoqtlkwgidupwi6f3a",
|
// "https://slate.textile.io/ipfs/bafybeifcxjvbad4lgpnbwff2dafufmnlylylmku4qoqtlkwgidupwi6f3a",
|
||||||
ownerId: "owner",
|
// ownerId: "owner",
|
||||||
name: "Never gonna give you up",
|
// name: "Never gonna give you up",
|
||||||
title: "never-gonna-give-you-up.mp4",
|
// title: "never-gonna-give-you-up.mp4",
|
||||||
type: "video/mp4",
|
// type: "video/mp4",
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
},
|
// },
|
||||||
ownerId: "owner",
|
// ownerId: "owner",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
this.props.onAction({
|
this.props.onAction({
|
||||||
type: "SIDEBAR",
|
type: "SIDEBAR",
|
||||||
value: destination,
|
value: destination,
|
||||||
@ -913,12 +947,101 @@ export class SearchModal extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_handleSelectIndex = (i) => {
|
||||||
|
if (this.state.selectedIndex === i || this.props.isMobile) {
|
||||||
|
console.log("handle hide");
|
||||||
|
this._handleHide();
|
||||||
|
} else {
|
||||||
|
console.log("set state");
|
||||||
|
// this.setState({ selectedIndex: i });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let selectedIndex = this.state.selectedIndex;
|
let selectedIndex = this.state.selectedIndex;
|
||||||
let results =
|
let results =
|
||||||
this.state.inputValue && this.state.inputValue.length
|
this.state.inputValue && this.state.inputValue.length
|
||||||
? this.state.results
|
? this.state.results
|
||||||
: this.state.defaultResults;
|
: this.state.defaultResults;
|
||||||
|
|
||||||
|
const filterDropdown = this.props.viewer ? (
|
||||||
|
<div style={{ flexShrink: 0, position: "relative" }}>
|
||||||
|
<div
|
||||||
|
css={STYLES_FILTER_BUTTON}
|
||||||
|
style={{
|
||||||
|
marginRight: 0,
|
||||||
|
marginLeft: 16,
|
||||||
|
color: this.state.scopeFilter ? Constants.system.brand : Constants.system.textGray,
|
||||||
|
}}
|
||||||
|
onClick={() => this.setState({ filterTooltip: !this.state.filterTooltip })}
|
||||||
|
>
|
||||||
|
<SVG.Filter height="16px" />
|
||||||
|
</div>
|
||||||
|
{this.state.filterTooltip ? (
|
||||||
|
<Boundary
|
||||||
|
captureResize={true}
|
||||||
|
captureScroll={false}
|
||||||
|
enabled
|
||||||
|
onOutsideRectEvent={() => this.setState({ filterTooltip: false })}
|
||||||
|
>
|
||||||
|
<PopoverNavigation
|
||||||
|
style={{
|
||||||
|
right: 0,
|
||||||
|
top: 44,
|
||||||
|
borderColor: Constants.system.bgGray,
|
||||||
|
color: Constants.system.textGray,
|
||||||
|
width: 124,
|
||||||
|
}}
|
||||||
|
navigation={[
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: this.state.scopeFilter ? "inherit" : Constants.system.brand,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
All
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
onClick: () => this._handleFilterScope(null),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color:
|
||||||
|
this.state.scopeFilter === "MY" ? Constants.system.brand : "inherit",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
My stuff
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
onClick: () => this._handleFilterScope("MY"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color:
|
||||||
|
this.state.scopeFilter === "NETWORK"
|
||||||
|
? Constants.system.brand
|
||||||
|
: "inherit",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
My network
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
onClick: () => this._handleFilterScope("NETWORK"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Boundary>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
css={STYLES_BACKGROUND}
|
css={STYLES_BACKGROUND}
|
||||||
@ -1047,87 +1170,7 @@ export class SearchModal extends React.Component {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flexShrink: 0, position: "relative" }}>
|
{filterDropdown}
|
||||||
<div
|
|
||||||
css={STYLES_FILTER_BUTTON}
|
|
||||||
style={{
|
|
||||||
marginRight: 0,
|
|
||||||
marginLeft: 16,
|
|
||||||
color: this.state.scopeFilter
|
|
||||||
? Constants.system.brand
|
|
||||||
: Constants.system.textGray,
|
|
||||||
}}
|
|
||||||
onClick={() =>
|
|
||||||
this.setState({ filterTooltip: !this.state.filterTooltip })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SVG.Filter height="16px" />
|
|
||||||
</div>
|
|
||||||
{this.state.filterTooltip ? (
|
|
||||||
<Boundary
|
|
||||||
captureResize={true}
|
|
||||||
captureScroll={false}
|
|
||||||
enabled
|
|
||||||
onOutsideRectEvent={() => this.setState({ filterTooltip: false })}
|
|
||||||
>
|
|
||||||
<PopoverNavigation
|
|
||||||
style={{
|
|
||||||
right: 0,
|
|
||||||
top: 44,
|
|
||||||
borderColor: Constants.system.bgGray,
|
|
||||||
color: Constants.system.textGray,
|
|
||||||
width: 124,
|
|
||||||
}}
|
|
||||||
navigation={[
|
|
||||||
{
|
|
||||||
text: (
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
color: this.state.scopeFilter
|
|
||||||
? "inherit"
|
|
||||||
: Constants.system.brand,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
All
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
onClick: () => this._handleFilterScope(null),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: (
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
color:
|
|
||||||
this.state.scopeFilter === "MY"
|
|
||||||
? Constants.system.brand
|
|
||||||
: "inherit",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
My stuff
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
onClick: () => this._handleFilterScope("MY"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: (
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
color:
|
|
||||||
this.state.scopeFilter === "NETWORK"
|
|
||||||
? Constants.system.brand
|
|
||||||
: "inherit",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
My network
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
onClick: () => this._handleFilterScope("NETWORK"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Boundary>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -1138,45 +1181,57 @@ export class SearchModal extends React.Component {
|
|||||||
css={STYLES_DROPDOWN}
|
css={STYLES_DROPDOWN}
|
||||||
>
|
>
|
||||||
{results.map((each, i) => (
|
{results.map((each, i) => (
|
||||||
<div
|
<Link
|
||||||
key={each.id}
|
disabled={this.props.isMobile ? false : selectedIndex !== i}
|
||||||
css={STYLES_DROPDOWN_ITEM}
|
href={each.href}
|
||||||
style={{
|
onAction={this.props.onAction}
|
||||||
background:
|
onClick={() => this._handleSelectIndex(i)}
|
||||||
selectedIndex === i
|
|
||||||
? "rgba(196, 196, 196, 0.1)"
|
|
||||||
: Constants.system.white,
|
|
||||||
paddingRight: selectedIndex === i ? "88px" : "4px",
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
selectedIndex === i || this.props.isMobile
|
|
||||||
? this._handleSelect(each)
|
|
||||||
: this.setState({ selectedIndex: i });
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{each.component}
|
<div
|
||||||
{selectedIndex === i ? (
|
key={each.id}
|
||||||
<div css={STYLES_RETURN}>
|
css={STYLES_DROPDOWN_ITEM}
|
||||||
<SVG.ArrowDownLeft height="16px" style={{ marginRight: 8 }} /> Return
|
style={{
|
||||||
</div>
|
background:
|
||||||
) : null}
|
selectedIndex === i
|
||||||
</div>
|
? "rgba(196, 196, 196, 0.1)"
|
||||||
|
: Constants.system.white,
|
||||||
|
paddingRight: selectedIndex === i ? "88px" : "4px",
|
||||||
|
}}
|
||||||
|
// onClick={() => {
|
||||||
|
// selectedIndex === i || this.props.isMobile
|
||||||
|
// ? this._handleSelect(each)
|
||||||
|
// : this.setState({ selectedIndex: i });
|
||||||
|
// }}
|
||||||
|
onClick={() => this.setState({ selectedIndex: i })}
|
||||||
|
>
|
||||||
|
{each.component}
|
||||||
|
{selectedIndex === i ? (
|
||||||
|
<div css={STYLES_RETURN}>
|
||||||
|
<SVG.ArrowDownLeft height="16px" style={{ marginRight: 8 }} />{" "}
|
||||||
|
Return
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{results &&
|
{results?.length && selectedIndex < results.length && selectedIndex >= 0 ? (
|
||||||
results.length &&
|
<Link
|
||||||
selectedIndex < results.length &&
|
href={results[selectedIndex].href}
|
||||||
selectedIndex >= 0 ? (
|
onAction={this.props.onAction}
|
||||||
<div
|
onClick={this._handleHide}
|
||||||
css={STYLES_PREVIEW_PANEL}
|
|
||||||
onClick={() => {
|
|
||||||
if (selectedIndex >= 0 && selectedIndex < results.length) {
|
|
||||||
this._handleSelect(results[selectedIndex]);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{results[selectedIndex].preview}
|
<div
|
||||||
</div>
|
css={STYLES_PREVIEW_PANEL}
|
||||||
|
// onClick={() => {
|
||||||
|
// if (selectedIndex >= 0 && selectedIndex < results.length) {
|
||||||
|
// this._handleSelect(results[selectedIndex]);
|
||||||
|
// }
|
||||||
|
// }}
|
||||||
|
>
|
||||||
|
{results[selectedIndex].preview}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
) : null}
|
) : null}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
@ -1195,9 +1250,9 @@ export class SearchModal extends React.Component {
|
|||||||
>
|
>
|
||||||
FAQ
|
FAQ
|
||||||
</span>
|
</span>
|
||||||
<span style={{ cursor: "pointer" }} onClick={() => this._handleRedirect("FMU")}>
|
{/* <span style={{ cursor: "pointer" }} onClick={() => this._handleRedirect("FMU")}>
|
||||||
I'm Feeling Lucky
|
I'm Feeling Lucky
|
||||||
</span>
|
</span> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Boundary>
|
</Boundary>
|
||||||
|
@ -146,6 +146,9 @@ export class SignIn extends React.Component {
|
|||||||
password: this.state.password,
|
password: this.state.password,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (!Events.hasError(response)) {
|
||||||
|
window.location.replace("/_/activity");
|
||||||
|
}
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -190,9 +193,7 @@ export class SignIn extends React.Component {
|
|||||||
<Logo height="36px" style={{ display: "block", margin: "56px auto 0px auto" }} />
|
<Logo height="36px" style={{ display: "block", margin: "56px auto 0px auto" }} />
|
||||||
|
|
||||||
<System.P style={{ margin: "56px 0", textAlign: "center" }}>
|
<System.P style={{ margin: "56px 0", textAlign: "center" }}>
|
||||||
{this.props.external
|
An open-source file sharing network for research and collaboration
|
||||||
? "Sign up or sign in to continue"
|
|
||||||
: "An open-source file sharing network for research and collaboration"}
|
|
||||||
</System.P>
|
</System.P>
|
||||||
|
|
||||||
<System.ButtonPrimary
|
<System.ButtonPrimary
|
||||||
|
@ -10,12 +10,12 @@ import * as Events from "~/common/custom-events";
|
|||||||
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
|
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
|
||||||
import CTATransition from "~/components/core/CTATransition";
|
import CTATransition from "~/components/core/CTATransition";
|
||||||
|
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
||||||
import { CheckBox } from "~/components/system/components/CheckBox";
|
import { CheckBox } from "~/components/system/components/CheckBox";
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||||
import { Toggle } from "~/components/system/components/Toggle";
|
import { Toggle } from "~/components/system/components/Toggle";
|
||||||
import { DynamicIcon } from "~/components/core/DynamicIcon";
|
|
||||||
import { Tooltip } from "~/components/core/Tooltip";
|
import { Tooltip } from "~/components/core/Tooltip";
|
||||||
import {
|
import {
|
||||||
ButtonPrimary,
|
ButtonPrimary,
|
||||||
@ -34,8 +34,6 @@ const MARGIN = 20;
|
|||||||
const CONTAINER_SIZE = 5 * SIZE + 4 * MARGIN;
|
const CONTAINER_SIZE = 5 * SIZE + 4 * MARGIN;
|
||||||
const TAG_HEIGHT = 20;
|
const TAG_HEIGHT = 20;
|
||||||
|
|
||||||
const SIZE_LIMIT = 1000000; //NOTE(martina): 1mb limit for twitter preview images
|
|
||||||
|
|
||||||
const generateLayout = (items) => {
|
const generateLayout = (items) => {
|
||||||
if (!items) {
|
if (!items) {
|
||||||
return [];
|
return [];
|
||||||
@ -328,7 +326,6 @@ export class SlateLayout extends React.Component {
|
|||||||
copyValue: "",
|
copyValue: "",
|
||||||
tooltip: null,
|
tooltip: null,
|
||||||
keyboardTooltip: false,
|
keyboardTooltip: false,
|
||||||
signInModal: false,
|
|
||||||
modalShowDeleteFiles: false,
|
modalShowDeleteFiles: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -964,6 +961,10 @@ export class SlateLayout extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleDownload = (e, i) => {
|
_handleDownload = (e, i) => {
|
||||||
|
if (!this.props.viewer) {
|
||||||
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (i !== undefined) {
|
if (i !== undefined) {
|
||||||
@ -972,6 +973,10 @@ export class SlateLayout extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleSaveCopy = async (e, i) => {
|
_handleSaveCopy = async (e, i) => {
|
||||||
|
if (!this.props.viewer) {
|
||||||
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let items = [];
|
let items = [];
|
||||||
@ -987,6 +992,10 @@ export class SlateLayout extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleAddToSlate = (e, i) => {
|
_handleAddToSlate = (e, i) => {
|
||||||
|
if (!this.props.viewer) {
|
||||||
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let items = [];
|
let items = [];
|
||||||
@ -1019,16 +1028,16 @@ export class SlateLayout extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let slates = this.props.viewer.slates;
|
let slates = this.props.viewer.slates;
|
||||||
let slateId = this.props.current.id;
|
let slateId = this.props.data.id;
|
||||||
for (let slate of slates) {
|
for (let slate of slates) {
|
||||||
if (slate.id === slateId) {
|
if (slate.id === slateId) {
|
||||||
slate.objects = slate.objects.filter((obj) => !ids.includes(obj.id.replace("data-", "")));
|
slate.objects = slate.objects.filter((obj) => !ids.includes(obj.id.replace("data-", "")));
|
||||||
this.props.onUpdateViewer({ slates });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { slates } });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UserBehaviors.removeFromSlate({ slate: this.props.current, ids });
|
UserBehaviors.removeFromSlate({ slate: this.props.data, ids });
|
||||||
};
|
};
|
||||||
|
|
||||||
_stopPropagation = (e) => e.stopPropagation();
|
_stopPropagation = (e) => e.stopPropagation();
|
||||||
@ -1052,8 +1061,8 @@ export class SlateLayout extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleDeleteModal = () => {
|
_handleDeleteModal = () => {
|
||||||
this.setState({ modalShowDeleteFiles: true })
|
this.setState({ modalShowDeleteFiles: true });
|
||||||
}
|
};
|
||||||
|
|
||||||
_handleDeleteFiles = async (res, i) => {
|
_handleDeleteFiles = async (res, i) => {
|
||||||
if (!res) {
|
if (!res) {
|
||||||
@ -1069,13 +1078,13 @@ export class SlateLayout extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let slates = this.props.viewer.slates;
|
let slates = this.props.viewer.slates;
|
||||||
let slateId = this.props.current.id;
|
let slateId = this.props.data.id;
|
||||||
for (let slate of slates) {
|
for (let slate of slates) {
|
||||||
if (slate.id === slateId) {
|
if (slate.id === slateId) {
|
||||||
slate.objects = slate.objects.filter(
|
slate.objects = slate.objects.filter(
|
||||||
(obj) => !ids.includes(obj.id.replace("data-", "")) && !cids.includes(obj.cid)
|
(obj) => !ids.includes(obj.id.replace("data-", "")) && !cids.includes(obj.cid)
|
||||||
);
|
);
|
||||||
this.props.onUpdateViewer({ slates });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { slates } });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1092,7 +1101,7 @@ export class SlateLayout extends React.Component {
|
|||||||
_handleLoginModal = (e) => {
|
_handleLoginModal = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.setState({ signInModal: true });
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -1137,15 +1146,20 @@ export class SlateLayout extends React.Component {
|
|||||||
Reset layout
|
Reset layout
|
||||||
</ButtonDisabled>
|
</ButtonDisabled>
|
||||||
) : (
|
) : (
|
||||||
<ButtonSecondary onClick={() => { this.setState({ modalShowResetLayout: true }) }} style={{ marginRight: 16 }}>
|
<ButtonSecondary
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({ modalShowResetLayout: true });
|
||||||
|
}}
|
||||||
|
style={{ marginRight: 16 }}
|
||||||
|
>
|
||||||
Reset layout
|
Reset layout
|
||||||
</ButtonSecondary>
|
</ButtonSecondary>
|
||||||
)}
|
)}
|
||||||
{this.state.modalShowResetLayout && (
|
{this.state.modalShowResetLayout && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
type={"CONFIRM"}
|
type={"CONFIRM"}
|
||||||
withValidation={false}
|
withValidation={false}
|
||||||
callback={this._handleResetLayout}
|
callback={this._handleResetLayout}
|
||||||
header={`Are you sure you want to reset your layout to the default column layout?`}
|
header={`Are you sure you want to reset your layout to the default column layout?`}
|
||||||
subHeader={`You can’t undo this action.`}
|
subHeader={`You can’t undo this action.`}
|
||||||
/>
|
/>
|
||||||
@ -1282,50 +1296,56 @@ export class SlateLayout extends React.Component {
|
|||||||
>
|
>
|
||||||
{this.state.show ? (
|
{this.state.show ? (
|
||||||
this.state.layout.map((pos, i) => (
|
this.state.layout.map((pos, i) => (
|
||||||
<Selectable
|
<Link
|
||||||
css={this.state.editing ? STYLES_ITEM_EDITING : STYLES_ITEM}
|
|
||||||
key={i}
|
key={i}
|
||||||
name={i}
|
redirect
|
||||||
draggable={!(numChecked || this.state.editing)}
|
params={{ ...this.props.page?.params, cid: this.state.items[i].cid }}
|
||||||
onDragStart={(e) => {
|
onAction={this.props.onAction}
|
||||||
this._disableDragAndDropUploadEvent();
|
|
||||||
this._handleDragToDesktop(e, this.state.items[i]);
|
|
||||||
}}
|
|
||||||
onDragEnd={this._enableDragAndDropUploadEvent}
|
|
||||||
selectableKey={i}
|
|
||||||
onMouseEnter={() => this.setState({ hover: i })}
|
|
||||||
onMouseLeave={() => this.setState({ hover: null })}
|
|
||||||
onMouseDown={this.state.editing ? (e) => this._handleMouseDown(e, i) : () => {}}
|
|
||||||
onClick={this.state.editing ? () => {} : () => this.props.onSelect(i)}
|
|
||||||
style={{
|
|
||||||
top: pos.y * unit,
|
|
||||||
left: pos.x * unit,
|
|
||||||
width: pos.w * unit,
|
|
||||||
height: this.state.fileNames ? (pos.h + TAG_HEIGHT) * unit : pos.h * unit,
|
|
||||||
zIndex: pos.z,
|
|
||||||
boxShadow:
|
|
||||||
this.state.dragIndex === i ? `0 0 44px 0 rgba(0, 0, 0, 0.25)` : null,
|
|
||||||
backgroundColor: Constants.system.white,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<SlateMediaObjectPreview
|
<Selectable
|
||||||
file={this.state.items[i]}
|
css={this.state.editing ? STYLES_ITEM_EDITING : STYLES_ITEM}
|
||||||
iconOnly={this.state.fileNames}
|
name={i}
|
||||||
charCap={70}
|
draggable={!(numChecked || this.state.editing)}
|
||||||
|
onDragStart={(e) => {
|
||||||
|
this._disableDragAndDropUploadEvent();
|
||||||
|
this._handleDragToDesktop(e, this.state.items[i]);
|
||||||
|
}}
|
||||||
|
onDragEnd={this._enableDragAndDropUploadEvent}
|
||||||
|
selectableKey={i}
|
||||||
|
onMouseEnter={() => this.setState({ hover: i })}
|
||||||
|
onMouseLeave={() => this.setState({ hover: null })}
|
||||||
|
onMouseDown={
|
||||||
|
this.state.editing ? (e) => this._handleMouseDown(e, i) : () => {}
|
||||||
|
}
|
||||||
|
// onClick={this.state.editing ? () => {} : () => this.props.onSelect(i)}
|
||||||
style={{
|
style={{
|
||||||
height: pos.h * unit,
|
top: pos.y * unit,
|
||||||
|
left: pos.x * unit,
|
||||||
width: pos.w * unit,
|
width: pos.w * unit,
|
||||||
background: Constants.system.white,
|
height: this.state.fileNames ? (pos.h + TAG_HEIGHT) * unit : pos.h * unit,
|
||||||
|
zIndex: pos.z,
|
||||||
|
boxShadow:
|
||||||
|
this.state.dragIndex === i ? `0 0 44px 0 rgba(0, 0, 0, 0.25)` : null,
|
||||||
|
backgroundColor: Constants.system.white,
|
||||||
}}
|
}}
|
||||||
imageStyle={{
|
>
|
||||||
width: pos.w * unit,
|
<SlateMediaObjectPreview
|
||||||
height: pos.h * unit,
|
file={this.state.items[i]}
|
||||||
maxHeight: "none",
|
iconOnly={this.state.fileNames}
|
||||||
}}
|
charCap={70}
|
||||||
/>
|
style={{
|
||||||
{numChecked || this.state.hover === i ? (
|
height: pos.h * unit,
|
||||||
<div css={STYLES_MOBILE_HIDDEN}>
|
width: pos.w * unit,
|
||||||
{this.props.external ? null : (
|
background: Constants.system.white,
|
||||||
|
}}
|
||||||
|
imageStyle={{
|
||||||
|
width: pos.w * unit,
|
||||||
|
height: pos.h * unit,
|
||||||
|
maxHeight: "none",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{numChecked || this.state.hover === i ? (
|
||||||
|
<div css={STYLES_MOBILE_HIDDEN}>
|
||||||
<div
|
<div
|
||||||
onMouseDown={this._stopProp}
|
onMouseDown={this._stopProp}
|
||||||
onMouseUp={this._stopProp}
|
onMouseUp={this._stopProp}
|
||||||
@ -1362,323 +1382,364 @@ export class SlateLayout extends React.Component {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
{this.state.hover !== i ? null : this.state.editing ? (
|
||||||
{this.state.hover !== i ? null : this.state.editing ? (
|
<React.Fragment>
|
||||||
<React.Fragment>
|
{this.state.tooltip && this.state.tooltip.startsWith(`${i}-`) ? (
|
||||||
{this.state.tooltip && this.state.tooltip.startsWith(`${i}-`) ? (
|
<Tooltip
|
||||||
<Tooltip
|
light
|
||||||
light
|
style={
|
||||||
style={
|
this.state.tooltip === `${i}-remove`
|
||||||
this.state.tooltip === `${i}-remove`
|
? {
|
||||||
? {
|
position: "absolute",
|
||||||
position: "absolute",
|
top: 36,
|
||||||
top: 36,
|
right: 8,
|
||||||
right: 8,
|
}
|
||||||
}
|
: this.state.tooltip === `${i}-view`
|
||||||
|
? {
|
||||||
|
position: "absolute",
|
||||||
|
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
||||||
|
right: "calc(50% + 28px)",
|
||||||
|
}
|
||||||
|
: this.state.tooltip === `${i}-download`
|
||||||
|
? {
|
||||||
|
position: "absolute",
|
||||||
|
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
||||||
|
right: "calc(50% - 12px)",
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
position: "absolute",
|
||||||
|
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
||||||
|
right: "calc(50% - 52px)",
|
||||||
|
color: Constants.system.red,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{this.state.tooltip === `${i}-remove`
|
||||||
|
? "Remove from collection"
|
||||||
: this.state.tooltip === `${i}-view`
|
: this.state.tooltip === `${i}-view`
|
||||||
? {
|
? "View file"
|
||||||
position: "absolute",
|
|
||||||
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
|
||||||
right: "calc(50% + 28px)",
|
|
||||||
}
|
|
||||||
: this.state.tooltip === `${i}-download`
|
: this.state.tooltip === `${i}-download`
|
||||||
? {
|
? "Download"
|
||||||
position: "absolute",
|
: "Delete file"}
|
||||||
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
</Tooltip>
|
||||||
right: "calc(50% - 12px)",
|
) : null}
|
||||||
}
|
|
||||||
: {
|
|
||||||
position: "absolute",
|
|
||||||
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
|
||||||
right: "calc(50% - 52px)",
|
|
||||||
color: Constants.system.red,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{this.state.tooltip === `${i}-remove`
|
|
||||||
? "Remove from collection"
|
|
||||||
: this.state.tooltip === `${i}-view`
|
|
||||||
? "View file"
|
|
||||||
: this.state.tooltip === `${i}-download`
|
|
||||||
? "Download"
|
|
||||||
: "Delete file"}
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
<div
|
|
||||||
onMouseDown={this._stopProp}
|
|
||||||
onMouseUp={this._stopProp}
|
|
||||||
onMouseEnter={() => this.setState({ tooltip: `${i}-remove` })}
|
|
||||||
onMouseLeave={() => this.setState({ tooltip: null })}
|
|
||||||
onClick={(e) => {
|
|
||||||
this._handleRemoveFromSlate(e, i);
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
top: 8,
|
|
||||||
right: 8,
|
|
||||||
cursor: "pointer",
|
|
||||||
margin: 0,
|
|
||||||
}}
|
|
||||||
css={STYLES_ICON_CIRCLE}
|
|
||||||
>
|
|
||||||
<SVG.DismissCircle height="24px" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
css={STYLES_ICON_ROW}
|
|
||||||
style={{
|
|
||||||
bottom: this.state.fileNames
|
|
||||||
? `calc(24px + ${TAG_HEIGHT}px)`
|
|
||||||
: "24px",
|
|
||||||
left: `calc(50% - 60px)`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
css={STYLES_ICON_CIRCLE}
|
|
||||||
onMouseDown={this._stopProp}
|
onMouseDown={this._stopProp}
|
||||||
onMouseUp={this._stopProp}
|
onMouseUp={this._stopProp}
|
||||||
onMouseEnter={() => this.setState({ tooltip: `${i}-view` })}
|
onMouseEnter={() => this.setState({ tooltip: `${i}-remove` })}
|
||||||
onMouseLeave={() => this.setState({ tooltip: null })}
|
onMouseLeave={() => this.setState({ tooltip: null })}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
this._stopProp(e);
|
this._handleRemoveFromSlate(e, i);
|
||||||
this.props.onSelect(i);
|
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<SVG.Eye height="16px" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
css={STYLES_ICON_CIRCLE}
|
|
||||||
onMouseDown={this._stopProp}
|
|
||||||
onMouseUp={this._stopProp}
|
|
||||||
onMouseEnter={() => this.setState({ tooltip: `${i}-download` })}
|
|
||||||
onMouseLeave={() => this.setState({ tooltip: null })}
|
|
||||||
onClick={(e) => {
|
|
||||||
this._handleDownload(e, i);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SVG.Download height="16px" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
css={STYLES_ICON_CIRCLE}
|
|
||||||
onMouseDown={this._stopProp}
|
|
||||||
onMouseUp={this._stopProp}
|
|
||||||
onMouseEnter={() => this.setState({ tooltip: `${i}-delete` })}
|
|
||||||
onMouseLeave={() => this.setState({ tooltip: null })}
|
|
||||||
onClick={
|
|
||||||
this.state.items[i].ownerId === this.props.viewer.id
|
|
||||||
? () => {
|
|
||||||
this.setState({ modalShowDeleteFiles: true })
|
|
||||||
}
|
|
||||||
: () => {}
|
|
||||||
}
|
|
||||||
style={{
|
style={{
|
||||||
cursor:
|
position: "absolute",
|
||||||
this.state.items[i].ownerId === this.props.viewer.id
|
top: 8,
|
||||||
? "pointer"
|
right: 8,
|
||||||
: "not-allowed",
|
cursor: "pointer",
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
css={STYLES_ICON_CIRCLE}
|
||||||
|
>
|
||||||
|
<SVG.DismissCircle height="24px" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
css={STYLES_ICON_ROW}
|
||||||
|
style={{
|
||||||
|
bottom: this.state.fileNames
|
||||||
|
? `calc(24px + ${TAG_HEIGHT}px)`
|
||||||
|
: "24px",
|
||||||
|
left: `calc(50% - 60px)`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SVG.Trash
|
<Link
|
||||||
height="16px"
|
redirect
|
||||||
style={{
|
params={{
|
||||||
color:
|
...this.props.page?.params,
|
||||||
this.state.items[i].ownerId === this.props.viewer.id
|
cid: this.state.items[i].cid,
|
||||||
? Constants.system.red
|
|
||||||
: "#999999",
|
|
||||||
}}
|
}}
|
||||||
/>
|
onAction={this.props.onAction}
|
||||||
</div>
|
>
|
||||||
|
<div
|
||||||
</div>
|
css={STYLES_ICON_CIRCLE}
|
||||||
</React.Fragment>
|
onMouseDown={this._stopProp}
|
||||||
) : (
|
onMouseUp={this._stopProp}
|
||||||
<React.Fragment>
|
onMouseEnter={() => this.setState({ tooltip: `${i}-view` })}
|
||||||
{this.state.tooltip && this.state.tooltip.startsWith(`${i}-`) ? (
|
onMouseLeave={() => this.setState({ tooltip: null })}
|
||||||
<Tooltip
|
// onClick={(e) => {
|
||||||
light
|
// this._stopProp(e);
|
||||||
style={
|
// this.props.onSelect(i);
|
||||||
this.state.tooltip === `${i}-add`
|
// }}
|
||||||
? {
|
>
|
||||||
position: "absolute",
|
<SVG.Eye height="16px" />
|
||||||
top: 36,
|
</div>
|
||||||
right: 8,
|
</Link>
|
||||||
}
|
|
||||||
: this.state.tooltip === `${i}-copy`
|
|
||||||
? {
|
|
||||||
position: "absolute",
|
|
||||||
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
|
||||||
right: "calc(50% + 28px)",
|
|
||||||
}
|
|
||||||
: this.state.tooltip === `${i}-download`
|
|
||||||
? {
|
|
||||||
position: "absolute",
|
|
||||||
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
|
||||||
right: "calc(50% - 12px)",
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
position: "absolute",
|
|
||||||
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
|
||||||
right: "calc(50% - 52px)",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{this.state.tooltip === `${i}-add`
|
|
||||||
? "Add to collection"
|
|
||||||
: this.state.tooltip === `${i}-copy`
|
|
||||||
? "Copy link"
|
|
||||||
: this.state.tooltip === `${i}-download`
|
|
||||||
? "Download"
|
|
||||||
: this.state.tooltip === `${i}-preview`
|
|
||||||
? "Make cover image"
|
|
||||||
: "Save copy"}
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
<div
|
|
||||||
onMouseDown={this._stopProp}
|
|
||||||
onMouseUp={this._stopProp}
|
|
||||||
onMouseEnter={() => this.setState({ tooltip: `${i}-add` })}
|
|
||||||
onMouseLeave={() => this.setState({ tooltip: null })}
|
|
||||||
onClick={
|
|
||||||
this.props.external
|
|
||||||
? this._handleLoginModal
|
|
||||||
: (e) => {
|
|
||||||
this._handleAddToSlate(e, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
top: 8,
|
|
||||||
right: 8,
|
|
||||||
cursor: "pointer",
|
|
||||||
margin: 0,
|
|
||||||
}}
|
|
||||||
css={STYLES_ICON_CIRCLE}
|
|
||||||
>
|
|
||||||
<SVG.PlusCircle height="24px" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
css={STYLES_ICON_ROW}
|
|
||||||
style={{
|
|
||||||
bottom: this.state.fileNames
|
|
||||||
? `calc(24px + ${TAG_HEIGHT}px)`
|
|
||||||
: "24px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
css={STYLES_ICON_CIRCLE}
|
|
||||||
onMouseDown={this._stopProp}
|
|
||||||
onMouseUp={this._stopProp}
|
|
||||||
onMouseEnter={() => this.setState({ tooltip: `${i}-download` })}
|
|
||||||
onMouseLeave={() => this.setState({ tooltip: null })}
|
|
||||||
onClick={
|
|
||||||
this.props.external
|
|
||||||
? this._handleLoginModal
|
|
||||||
: (e) => {
|
|
||||||
this._handleDownload(e, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SVG.Download height="16px" />
|
|
||||||
</div>
|
|
||||||
{this.props.isOwner ? (
|
|
||||||
<div
|
<div
|
||||||
css={STYLES_ICON_CIRCLE}
|
css={STYLES_ICON_CIRCLE}
|
||||||
onMouseDown={this._stopProp}
|
onMouseDown={this._stopProp}
|
||||||
onMouseUp={this._stopProp}
|
onMouseUp={this._stopProp}
|
||||||
onMouseEnter={() => this.setState({ tooltip: `${i}-preview` })}
|
onMouseEnter={() => this.setState({ tooltip: `${i}-download` })}
|
||||||
|
onMouseLeave={() => this.setState({ tooltip: null })}
|
||||||
|
onClick={(e) => {
|
||||||
|
this._handleDownload(e, i);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SVG.Download height="16px" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
css={STYLES_ICON_CIRCLE}
|
||||||
|
onMouseDown={this._stopProp}
|
||||||
|
onMouseUp={this._stopProp}
|
||||||
|
onMouseEnter={() => this.setState({ tooltip: `${i}-delete` })}
|
||||||
|
onMouseLeave={() => this.setState({ tooltip: null })}
|
||||||
|
onClick={
|
||||||
|
this.state.items[i].ownerId === this.props.viewer.id
|
||||||
|
? () => {
|
||||||
|
this.setState({ modalShowDeleteFiles: true });
|
||||||
|
}
|
||||||
|
: () => {}
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
cursor:
|
||||||
|
this.state.items[i].ownerId === this.props.viewer.id
|
||||||
|
? "pointer"
|
||||||
|
: "not-allowed",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SVG.Trash
|
||||||
|
height="16px"
|
||||||
|
style={{
|
||||||
|
color:
|
||||||
|
this.state.items[i].ownerId === this.props.viewer.id
|
||||||
|
? Constants.system.red
|
||||||
|
: "#999999",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<React.Fragment>
|
||||||
|
{this.state.tooltip && this.state.tooltip.startsWith(`${i}-`) ? (
|
||||||
|
<Tooltip
|
||||||
|
light
|
||||||
|
style={
|
||||||
|
this.state.tooltip === `${i}-add` ||
|
||||||
|
this.state.tooltip === `${i}-remove`
|
||||||
|
? {
|
||||||
|
position: "absolute",
|
||||||
|
top: 36,
|
||||||
|
right: 8,
|
||||||
|
}
|
||||||
|
: this.state.tooltip === `${i}-copy`
|
||||||
|
? {
|
||||||
|
position: "absolute",
|
||||||
|
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
||||||
|
right: "calc(50% + 28px)",
|
||||||
|
}
|
||||||
|
: this.state.tooltip === `${i}-download`
|
||||||
|
? {
|
||||||
|
position: "absolute",
|
||||||
|
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
||||||
|
right: "calc(50% - 12px)",
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
position: "absolute",
|
||||||
|
bottom: this.state.fileNames ? 52 + TAG_HEIGHT : 52,
|
||||||
|
right: "calc(50% - 52px)",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{this.state.tooltip === `${i}-add`
|
||||||
|
? "Add to collection"
|
||||||
|
: this.state.tooltip === `${i}-copy`
|
||||||
|
? "Copy link"
|
||||||
|
: this.state.tooltip === `${i}-download`
|
||||||
|
? "Download"
|
||||||
|
: this.state.tooltip === `${i}-preview`
|
||||||
|
? "Make cover image"
|
||||||
|
: this.state.tooltip === `${i}-remove`
|
||||||
|
? "Remove from collection"
|
||||||
|
: "Save copy"}
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
{this.props.isOwner ? (
|
||||||
|
<div
|
||||||
|
onMouseDown={this._stopProp}
|
||||||
|
onMouseUp={this._stopProp}
|
||||||
|
onMouseEnter={() => this.setState({ tooltip: `${i}-remove` })}
|
||||||
onMouseLeave={() => this.setState({ tooltip: null })}
|
onMouseLeave={() => this.setState({ tooltip: null })}
|
||||||
onClick={
|
onClick={
|
||||||
this.props.external
|
this.props.external
|
||||||
? this._handleLoginModal
|
? this._handleLoginModal
|
||||||
: this.state.items[i].data.type &&
|
: (e) => {
|
||||||
Validations.isPreviewableImage(
|
this._handleRemoveFromSlate(e, i);
|
||||||
this.state.items[i].data.type
|
|
||||||
) &&
|
|
||||||
this.state.items[i].data.size &&
|
|
||||||
this.state.items[i].data.size < SIZE_LIMIT
|
|
||||||
? (e) => this._handleSetPreview(e, i)
|
|
||||||
: () => {}
|
|
||||||
}
|
|
||||||
style={
|
|
||||||
this.props.preview ===
|
|
||||||
Strings.getURLfromCID(this.state.items[i].cid)
|
|
||||||
? {
|
|
||||||
backgroundColor: "rgba(0, 97, 187, 0.75)",
|
|
||||||
}
|
|
||||||
: this.state.items[i].data.type &&
|
|
||||||
Validations.isPreviewableImage(
|
|
||||||
this.state.items[i].data.type
|
|
||||||
) &&
|
|
||||||
this.state.items[i].data.size &&
|
|
||||||
this.state.items[i].data.size < SIZE_LIMIT
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
color: "#999999",
|
|
||||||
cursor: "not-allowed",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 8,
|
||||||
|
right: 8,
|
||||||
|
cursor: "pointer",
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
css={STYLES_ICON_CIRCLE}
|
||||||
>
|
>
|
||||||
{this.props.preview ===
|
<SVG.DismissCircle height="24px" />
|
||||||
Strings.getURLfromCID(this.state.items[i].cid) ? (
|
|
||||||
<SVG.DesktopEye
|
|
||||||
height="16px"
|
|
||||||
style={{
|
|
||||||
color: Constants.system.white,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<SVG.Desktop height="16px" />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
css={STYLES_ICON_CIRCLE}
|
|
||||||
onMouseDown={this._stopProp}
|
onMouseDown={this._stopProp}
|
||||||
onMouseUp={this._stopProp}
|
onMouseUp={this._stopProp}
|
||||||
onMouseEnter={() => this.setState({ tooltip: `${i}-save` })}
|
onMouseEnter={() => this.setState({ tooltip: `${i}-add` })}
|
||||||
onMouseLeave={() => this.setState({ tooltip: null })}
|
onMouseLeave={() => this.setState({ tooltip: null })}
|
||||||
onClick={
|
onClick={
|
||||||
this.props.external
|
this.props.external
|
||||||
? this._handleLoginModal
|
? this._handleLoginModal
|
||||||
: (e) => this._handleSaveCopy(e, i)
|
: (e) => {
|
||||||
|
this._handleAddToSlate(e, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 8,
|
||||||
|
right: 8,
|
||||||
|
cursor: "pointer",
|
||||||
|
margin: 0,
|
||||||
|
}}
|
||||||
|
css={STYLES_ICON_CIRCLE}
|
||||||
>
|
>
|
||||||
<SVG.Save height="16px" />
|
<SVG.PlusCircle height="24px" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
<div
|
||||||
</React.Fragment>
|
css={STYLES_ICON_ROW}
|
||||||
)}
|
style={{
|
||||||
</div>
|
bottom: this.state.fileNames
|
||||||
) : null}
|
? `calc(24px + ${TAG_HEIGHT}px)`
|
||||||
{this.state.fileNames ? (
|
: "24px",
|
||||||
<div
|
}}
|
||||||
css={STYLES_FILE_TAG}
|
>
|
||||||
style={{
|
<div
|
||||||
fontSize: `${Math.min(TAG_HEIGHT * unit * 0.7, 14)}px`,
|
css={STYLES_ICON_CIRCLE}
|
||||||
height: `${TAG_HEIGHT * unit}px`,
|
onMouseDown={this._stopProp}
|
||||||
}}
|
onMouseUp={this._stopProp}
|
||||||
>
|
onMouseEnter={() => this.setState({ tooltip: `${i}-download` })}
|
||||||
<span css={STYLES_FILE_NAME}>
|
onMouseLeave={() => this.setState({ tooltip: null })}
|
||||||
{this.state.items[i].data.name || this.state.items[i].filename}
|
onClick={
|
||||||
</span>
|
this.props.external
|
||||||
<span css={STYLES_FILE_TYPE}>
|
? this._handleLoginModal
|
||||||
{Strings.getFileExtension(this.state.items[i].filename)}
|
: (e) => {
|
||||||
</span>
|
this._handleDownload(e, i);
|
||||||
</div>
|
}
|
||||||
) : null}
|
}
|
||||||
{this.state.editing ? (
|
>
|
||||||
<div
|
<SVG.Download height="16px" />
|
||||||
css={STYLES_HANDLE_BOX}
|
</div>
|
||||||
onMouseDown={(e) => this._handleMouseDownResize(e, i)}
|
{this.props.isOwner ? (
|
||||||
style={{
|
<div
|
||||||
display:
|
css={STYLES_ICON_CIRCLE}
|
||||||
this.state.hover === i || this.state.dragIndex === i ? "block" : "none",
|
onMouseDown={this._stopProp}
|
||||||
}}
|
onMouseUp={this._stopProp}
|
||||||
>
|
onMouseEnter={() => this.setState({ tooltip: `${i}-preview` })}
|
||||||
<SVG.DragHandle height="24px" />
|
onMouseLeave={() => this.setState({ tooltip: null })}
|
||||||
</div>
|
onClick={
|
||||||
) : null}
|
this.props.external
|
||||||
</Selectable>
|
? this._handleLoginModal
|
||||||
|
: this.state.items[i].data.type &&
|
||||||
|
Validations.isPreviewableImage(
|
||||||
|
this.state.items[i].data.type
|
||||||
|
) &&
|
||||||
|
this.state.items[i].data.size &&
|
||||||
|
this.state.items[i].data.size <
|
||||||
|
Constants.linkPreviewSizeLimit
|
||||||
|
? (e) => this._handleSetPreview(e, i)
|
||||||
|
: () => {}
|
||||||
|
}
|
||||||
|
style={
|
||||||
|
this.props.preview ===
|
||||||
|
Strings.getURLfromCID(this.state.items[i].cid)
|
||||||
|
? {
|
||||||
|
backgroundColor: "rgba(0, 97, 187, 0.75)",
|
||||||
|
}
|
||||||
|
: this.state.items[i].data.type &&
|
||||||
|
Validations.isPreviewableImage(
|
||||||
|
this.state.items[i].data.type
|
||||||
|
) &&
|
||||||
|
this.state.items[i].data.size &&
|
||||||
|
this.state.items[i].data.size <
|
||||||
|
Constants.linkPreviewSizeLimit
|
||||||
|
? {}
|
||||||
|
: {
|
||||||
|
color: "#999999",
|
||||||
|
cursor: "not-allowed",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{this.props.preview ===
|
||||||
|
Strings.getURLfromCID(this.state.items[i].cid) ? (
|
||||||
|
<SVG.DesktopEye
|
||||||
|
height="16px"
|
||||||
|
style={{
|
||||||
|
color: Constants.system.white,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<SVG.Desktop height="16px" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
css={STYLES_ICON_CIRCLE}
|
||||||
|
onMouseDown={this._stopProp}
|
||||||
|
onMouseUp={this._stopProp}
|
||||||
|
onMouseEnter={() => this.setState({ tooltip: `${i}-save` })}
|
||||||
|
onMouseLeave={() => this.setState({ tooltip: null })}
|
||||||
|
onClick={
|
||||||
|
this.props.external
|
||||||
|
? this._handleLoginModal
|
||||||
|
: (e) => this._handleSaveCopy(e, i)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SVG.Save height="16px" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{this.state.fileNames ? (
|
||||||
|
<div
|
||||||
|
css={STYLES_FILE_TAG}
|
||||||
|
style={{
|
||||||
|
fontSize: `${Math.min(TAG_HEIGHT * unit * 0.7, 14)}px`,
|
||||||
|
height: `${TAG_HEIGHT * unit}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span css={STYLES_FILE_NAME}>
|
||||||
|
{this.state.items[i].data.name || this.state.items[i].filename}
|
||||||
|
</span>
|
||||||
|
<span css={STYLES_FILE_TYPE}>
|
||||||
|
{Strings.getFileExtension(this.state.items[i].filename)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{this.state.editing ? (
|
||||||
|
<div
|
||||||
|
css={STYLES_HANDLE_BOX}
|
||||||
|
onMouseDown={(e) => this._handleMouseDownResize(e, i)}
|
||||||
|
style={{
|
||||||
|
display:
|
||||||
|
this.state.hover === i || this.state.dragIndex === i
|
||||||
|
? "block"
|
||||||
|
: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SVG.DragHandle height="24px" />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</Selectable>
|
||||||
|
</Link>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div css={STYLES_LOADER}>
|
<div css={STYLES_LOADER}>
|
||||||
@ -1688,7 +1749,7 @@ export class SlateLayout extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.state.modalShowDeleteFiles && (
|
{this.state.modalShowDeleteFiles && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
type={"DELETE"}
|
type={"DELETE"}
|
||||||
withValidation={false}
|
withValidation={false}
|
||||||
callback={this._handleDeleteFiles}
|
callback={this._handleDeleteFiles}
|
||||||
@ -1772,20 +1833,6 @@ export class SlateLayout extends React.Component {
|
|||||||
value={this.state.copyValue}
|
value={this.state.copyValue}
|
||||||
css={STYLES_COPY_INPUT}
|
css={STYLES_COPY_INPUT}
|
||||||
/>
|
/>
|
||||||
{this.props.external && this.state.signInModal && (
|
|
||||||
<div>
|
|
||||||
<CTATransition
|
|
||||||
onClose={() => this.setState({ signInModal: false })}
|
|
||||||
viewer={this.props.viewer}
|
|
||||||
open={this.state.signInModal}
|
|
||||||
redirectURL={`/_${Strings.createQueryParams({
|
|
||||||
scene: "NAV_SLATE",
|
|
||||||
user: this.props.creator.username,
|
|
||||||
slate: this.props.slate.slatename,
|
|
||||||
})}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</GroupSelectable>
|
</GroupSelectable>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -12,17 +12,20 @@ import { endsWithAny } from "~/common/utilities";
|
|||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
|
|
||||||
const STYLES_FAILURE = css`
|
const STYLES_FAILURE = css`
|
||||||
background-color: ${Constants.system.pitchBlack};
|
|
||||||
color: ${Constants.system.white};
|
color: ${Constants.system.white};
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 88px;
|
font-size: 24px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 24px 36px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 4px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 10%;
|
min-height: 10%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: rgba(20, 20, 20, 0.8);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_OBJECT = css`
|
const STYLES_OBJECT = css`
|
||||||
@ -94,7 +97,7 @@ export default class SlateMediaObject extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isMobile ? (
|
{isMobile ? (
|
||||||
<a href={url} target="_blank">
|
<a href={url} target="_blank" style={{ textDecoration: "none" }}>
|
||||||
<div css={STYLES_FAILURE}>Tap to open PDF in new tab</div>
|
<div css={STYLES_FAILURE}>Tap to open PDF in new tab</div>
|
||||||
</a>
|
</a>
|
||||||
) : (
|
) : (
|
||||||
|
@ -8,6 +8,7 @@ import { Logo } from "~/common/logo";
|
|||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
||||||
import { PopoverNavigation } from "~/components/system/components/PopoverNavigation";
|
import { PopoverNavigation } from "~/components/system/components/PopoverNavigation";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
import ProcessedText from "~/components/core/ProcessedText";
|
import ProcessedText from "~/components/core/ProcessedText";
|
||||||
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
|
import SlateMediaObjectPreview from "~/components/core/SlateMediaObjectPreview";
|
||||||
@ -277,10 +278,12 @@ export class SlatePreviewBlock extends React.Component {
|
|||||||
right: "-12px",
|
right: "-12px",
|
||||||
}}
|
}}
|
||||||
navigation={[
|
navigation={[
|
||||||
{
|
[
|
||||||
text: "Copy collection ID",
|
{
|
||||||
onClick: (e) => this._handleCopy(e, this.props.slate.id),
|
text: "Copy collection ID",
|
||||||
},
|
onClick: (e) => this._handleCopy(e, this.props.slate.id),
|
||||||
|
},
|
||||||
|
],
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Boundary>
|
</Boundary>
|
||||||
@ -437,44 +440,15 @@ export default class SlatePreviewBlocks extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div css={STYLES_SLATES}>
|
<div css={STYLES_SLATES}>
|
||||||
{this.props.external
|
{this.props.slates?.map((slate) => (
|
||||||
? this.props.slates?.map((slate) => (
|
<Link key={slate.id} href={`/$/slate/${slate.id}`} onAction={this.props.onAction}>
|
||||||
<a
|
<SlatePreviewBlock
|
||||||
key={slate.id}
|
isOwner={this.props.isOwner}
|
||||||
style={{ textDecoration: "none", color: Constants.system.black }}
|
slate={slate}
|
||||||
href={
|
external={this.props.external}
|
||||||
!!this.props.username
|
/>
|
||||||
? `/${this.props.username}/${slate.slatename}`
|
</Link>
|
||||||
: `/$/slate/${slate.id}`
|
))}
|
||||||
}
|
|
||||||
>
|
|
||||||
<SlatePreviewBlock
|
|
||||||
isOwner={this.props.isOwner}
|
|
||||||
username={this.props.username}
|
|
||||||
slate={slate}
|
|
||||||
external={this.props.external}
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
))
|
|
||||||
: this.props.slates?.map((slate) => (
|
|
||||||
<div
|
|
||||||
key={slate.id}
|
|
||||||
onClick={() =>
|
|
||||||
this.props.onAction({
|
|
||||||
type: "NAVIGATE",
|
|
||||||
value: "NAV_SLATE",
|
|
||||||
data: { decorator: "SLATE", ...slate },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SlatePreviewBlock
|
|
||||||
isOwner={this.props.isOwner}
|
|
||||||
username={this.props.username}
|
|
||||||
slate={slate}
|
|
||||||
external={this.props.external}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import * as React from "react";
|
|||||||
import * as Constants from "~/common/constants";
|
import * as Constants from "~/common/constants";
|
||||||
|
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
const STYLES_PRIMARY_TAB_GROUP = css`
|
const STYLES_PRIMARY_TAB_GROUP = css`
|
||||||
font-size: ${Constants.typescale.lvl2};
|
font-size: ${Constants.typescale.lvl2};
|
||||||
@ -81,7 +82,8 @@ const STYLES_NAVTAB = css`
|
|||||||
|
|
||||||
@media (max-width: ${Constants.sizes.mobile}px) {
|
@media (max-width: ${Constants.sizes.mobile}px) {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
font-size: ${Constants.typescale.lvl1};
|
height: 40px;
|
||||||
|
font-size: ${Constants.typescale.lvl0};
|
||||||
}
|
}
|
||||||
|
|
||||||
:last-child {
|
:last-child {
|
||||||
@ -91,99 +93,94 @@ const STYLES_NAVTAB = css`
|
|||||||
|
|
||||||
export class SecondaryTabGroup extends React.Component {
|
export class SecondaryTabGroup extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
|
const value = this.props.value || this.props.tabs[0].value;
|
||||||
|
console.log(this.props.value);
|
||||||
|
const disabled = this.props.disabled;
|
||||||
return (
|
return (
|
||||||
<div css={STYLES_SECONDARY_TAB_GROUP} style={this.props.style}>
|
<div css={STYLES_SECONDARY_TAB_GROUP} style={this.props.style}>
|
||||||
{this.props.tabs.map((tab, i) => (
|
{this.props.tabs.map((tab, i) => {
|
||||||
<div
|
const selected = value === tab.value?.tab;
|
||||||
css={STYLES_TAB}
|
return (
|
||||||
key={this.props.onAction ? tab.title : i}
|
<Link key={i} params={tab.value} onAction={this.props.onAction}>
|
||||||
style={{
|
<div
|
||||||
color:
|
css={STYLES_TAB}
|
||||||
this.props.disabled || this.props.value === i
|
style={{
|
||||||
? Constants.system.black
|
color: disabled || selected ? Constants.system.black : "rgba(0,0,0,0.25)",
|
||||||
: "rgba(0,0,0,0.25)",
|
cursor: disabled ? "auto" : "pointer",
|
||||||
cursor: this.props.disabled ? "auto" : "pointer",
|
...this.props.itemStyle,
|
||||||
...this.props.itemStyle,
|
backgroundColor: selected ? Constants.system.white : "transparent",
|
||||||
backgroundColor: this.props.value === i ? Constants.system.white : "transparent",
|
}}
|
||||||
}}
|
// onClick={disabled || selected ? () => {} : () => this.props.onChange(tab.value)}
|
||||||
onClick={
|
>
|
||||||
this.props.disabled || this.props.value === i
|
{tab.title}
|
||||||
? () => {}
|
</div>
|
||||||
: this.props.onAction
|
</Link>
|
||||||
? () => this.props.onAction({ type: "NAVIGATE", value: tab.value })
|
);
|
||||||
: () => this.props.onChange(i)
|
})}
|
||||||
}
|
|
||||||
>
|
|
||||||
{this.props.onAction ? tab.title : tab}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PrimaryTabGroup extends React.Component {
|
// export class PrimaryTabGroup extends React.Component {
|
||||||
render() {
|
// render() {
|
||||||
return (
|
// return (
|
||||||
<div css={STYLES_PRIMARY_TAB_GROUP} style={this.props.style}>
|
// <div css={STYLES_PRIMARY_TAB_GROUP} style={this.props.style}>
|
||||||
{this.props.tabs.map((tab, i) => (
|
// {this.props.tabs.map((tab, i) => (
|
||||||
<div
|
// <div
|
||||||
css={STYLES_TAB}
|
// css={STYLES_TAB}
|
||||||
key={this.props.onAction ? tab.title : tab}
|
// key={this.props.onAction ? tab.title : tab}
|
||||||
style={{
|
// style={{
|
||||||
padding: "8px 16px 8px 0",
|
// padding: "8px 16px 8px 0",
|
||||||
color:
|
// color:
|
||||||
this.props.disabled || this.props.value === i
|
// this.props.disabled || this.props.value === i
|
||||||
? Constants.system.black
|
// ? Constants.system.black
|
||||||
: "rgba(0,0,0,0.25)",
|
// : "rgba(0,0,0,0.25)",
|
||||||
cursor: this.props.disabled ? "auto" : "pointer",
|
// cursor: this.props.disabled ? "auto" : "pointer",
|
||||||
fontFamily: Constants.font.medium,
|
// fontFamily: Constants.font.medium,
|
||||||
...this.props.itemStyle,
|
// ...this.props.itemStyle,
|
||||||
}}
|
// }}
|
||||||
onClick={
|
// onClick={
|
||||||
this.props.disabled || this.props.value === i
|
// this.props.disabled || this.props.value === i
|
||||||
? () => {}
|
// ? () => {}
|
||||||
: this.props.onAction
|
// : this.props.onAction
|
||||||
? () => this.props.onAction({ type: "NAVIGATE", value: tab.value })
|
// ? () => this.props.onAction({ type: "NAVIGATE", value: tab.value })
|
||||||
: () => this.props.onChange(i)
|
// : () => this.props.onChange(i)
|
||||||
}
|
// }
|
||||||
>
|
// >
|
||||||
{this.props.onAction ? tab.title : tab}
|
// {this.props.onAction ? tab.title : tab}
|
||||||
</div>
|
// </div>
|
||||||
))}
|
// ))}
|
||||||
</div>
|
// </div>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
export class TabGroup extends React.Component {
|
export class TabGroup extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
|
const value = this.props.value || this.props.tabs[0].value;
|
||||||
|
const disabled = this.props.disabled;
|
||||||
return (
|
return (
|
||||||
<div css={STYLES_TAB_GROUP} style={this.props.style}>
|
<div css={STYLES_TAB_GROUP} style={this.props.style}>
|
||||||
{this.props.tabs.map((tab, i) => (
|
{this.props.tabs.map((tab, i) => {
|
||||||
<div
|
const selected = value === tab.value?.subtab;
|
||||||
css={STYLES_NAVTAB}
|
return (
|
||||||
key={this.props.onAction ? tab.title : tab}
|
<Link key={i} params={tab.value} onAction={this.props.onAction}>
|
||||||
style={{
|
<div
|
||||||
color:
|
css={STYLES_NAVTAB}
|
||||||
this.props.disabled || this.props.value === i
|
style={{
|
||||||
? Constants.system.black
|
color: disabled || selected ? Constants.system.black : "rgba(0,0,0,0.25)",
|
||||||
: "rgba(0,0,0,0.25)",
|
cursor: disabled ? "auto" : "pointer",
|
||||||
cursor: this.props.disabled ? "auto" : "pointer",
|
...this.props.itemStyle,
|
||||||
borderBottom: this.props.value === i ? `1px solid ${Constants.system.black}` : "none",
|
borderBottom: selected ? `1px solid ${Constants.system.black}` : "none",
|
||||||
...this.props.itemStyle,
|
}}
|
||||||
}}
|
// onClick={disabled || selected ? () => {} : () => this.props.onChange(tab.value)}
|
||||||
onClick={
|
>
|
||||||
this.props.disabled || this.props.value === i
|
{tab.title}
|
||||||
? () => {}
|
</div>
|
||||||
: this.props.onAction
|
</Link>
|
||||||
? () => this.props.onAction({ type: "NAVIGATE", value: tab.value })
|
);
|
||||||
: () => this.props.onChange(i)
|
})}
|
||||||
}
|
|
||||||
>
|
|
||||||
{this.props.onAction ? tab.title : tab}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -62,17 +62,17 @@ const WebsitePrototypeHeader = (props) => {
|
|||||||
<div css={STYLES_CONTAINER} style={props.style}>
|
<div css={STYLES_CONTAINER} style={props.style}>
|
||||||
<div css={STYLES_LEFT}>
|
<div css={STYLES_LEFT}>
|
||||||
<a css={STYLES_LINK} href="/" style={{ marginRight: 16, position: "relative", top: "1px" }}>
|
<a css={STYLES_LINK} href="/" style={{ marginRight: 16, position: "relative", top: "1px" }}>
|
||||||
<Logo style={{ height: 16 }} />
|
<Logo style={{ height: 20 }} />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div css={STYLES_RIGHT}>
|
{/* <div css={STYLES_RIGHT}>
|
||||||
<a css={STYLES_LINK} href="/_" style={{ marginRight: 24 }}>
|
<a css={STYLES_LINK} href="/_" style={{ marginRight: 24 }}>
|
||||||
Sign up
|
Sign up
|
||||||
</a>
|
</a>
|
||||||
<a css={STYLES_LINK} href="/_">
|
<a css={STYLES_LINK} href="/_">
|
||||||
Sign in
|
Sign in
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -87,10 +87,10 @@ const WebsitePrototypeHeaderGeneric = (props) => {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div css={STYLES_RIGHT}>
|
<div css={STYLES_RIGHT}>
|
||||||
<a css={STYLES_LINK} href="/_" style={{ marginRight: 24 }}>
|
<a css={STYLES_LINK} href="/_/auth?tab=signup" style={{ marginRight: 24 }}>
|
||||||
Sign up
|
Sign up
|
||||||
</a>
|
</a>
|
||||||
<a css={STYLES_LINK} href="/_">
|
<a css={STYLES_LINK} href="/_/auth?tab=signin">
|
||||||
Sign in
|
Sign in
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,6 +6,9 @@ export default class WebsitePrototypeWrapper extends React.Component {
|
|||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
image:
|
image:
|
||||||
"https://slate.textile.io/ipfs/bafybeihtmqpx2lnlvaerfhq5imi2y3jzuf4jqspmmqbth3ebim4ebc2lqy",
|
"https://slate.textile.io/ipfs/bafybeihtmqpx2lnlvaerfhq5imi2y3jzuf4jqspmmqbth3ebim4ebc2lqy",
|
||||||
|
title: "Slate",
|
||||||
|
url: "https://slate.host/_",
|
||||||
|
description: "An open-source file sharing network for research and collaboration",
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -80,8 +80,8 @@ export default class SidebarAddFileToBucket extends React.Component {
|
|||||||
this.props.onUpload({
|
this.props.onUpload({
|
||||||
files: e.target.files,
|
files: e.target.files,
|
||||||
slate:
|
slate:
|
||||||
this.props.current && this.props.current.slateId
|
this.props.page.id === "NAV_SLATE" && this.props.data?.id
|
||||||
? { id: this.props.current.slateId }
|
? { id: this.props.data.id }
|
||||||
: null,
|
: null,
|
||||||
});
|
});
|
||||||
this.props.onCancel();
|
this.props.onCancel();
|
||||||
@ -134,12 +134,10 @@ export default class SidebarAddFileToBucket extends React.Component {
|
|||||||
|
|
||||||
<System.P>
|
<System.P>
|
||||||
Click below or drop a file anywhere on the page to upload a file
|
Click below or drop a file anywhere on the page to upload a file
|
||||||
{this.props.current &&
|
{this.props.data?.slatename || this.props.data?.data.name ? (
|
||||||
(this.props.current.slatename ||
|
|
||||||
(this.props.current.data && this.props.current.data.name)) ? (
|
|
||||||
<span>
|
<span>
|
||||||
{" "}
|
{" "}
|
||||||
to <strong>{Strings.getPresentationSlateName(this.props.current)}</strong>
|
to <strong>{Strings.getPresentationSlateName(this.props.data)}</strong>
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
""
|
""
|
||||||
|
@ -9,6 +9,7 @@ import * as SVG from "~/common/svg";
|
|||||||
|
|
||||||
import { RadioGroup } from "~/components/system/components/RadioGroup";
|
import { RadioGroup } from "~/components/system/components/RadioGroup";
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
const STYLES_TEXT = css`
|
const STYLES_TEXT = css`
|
||||||
color: ${Constants.system.textGray};
|
color: ${Constants.system.textGray};
|
||||||
@ -91,10 +92,7 @@ export default class SidebarCreateSlate extends React.Component {
|
|||||||
() =>
|
() =>
|
||||||
this.props.onAction({
|
this.props.onAction({
|
||||||
type: "NAVIGATE",
|
type: "NAVIGATE",
|
||||||
value: "NAV_SLATE",
|
href: `/$/slate/${response.slate.id}`,
|
||||||
data: {
|
|
||||||
id: response.slate.id,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
200
|
200
|
||||||
);
|
);
|
||||||
|
@ -86,7 +86,7 @@ export default class SidebarEditTags extends React.Component {
|
|||||||
this._handleSave();
|
this._handleSave();
|
||||||
|
|
||||||
let newSuggestions = new Set([...this.state.suggestions, ...this.state.tags]);
|
let newSuggestions = new Set([...this.state.suggestions, ...this.state.tags]);
|
||||||
this.props.onUpdateViewer({ tags: Array.from(newSuggestions) });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { tags: Array.from(newSuggestions) } });
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -27,7 +27,7 @@ const STYLES_TEXT = css`
|
|||||||
|
|
||||||
export default class SidebarCreateSlate extends React.Component {
|
export default class SidebarCreateSlate extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
name: this.props.viewer.data && this.props.viewer.data.name ? this.props.viewer.data.name : "",
|
name: this.props.viewer?.data?.name ? this.props.viewer.data.name : "",
|
||||||
email: "",
|
email: "",
|
||||||
twitter: "",
|
twitter: "",
|
||||||
message: "",
|
message: "",
|
||||||
@ -51,12 +51,12 @@ export default class SidebarCreateSlate extends React.Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const response = await Actions.createSupportMessage({
|
const response = await Actions.createSupportMessage({
|
||||||
username: this.props.viewer.username,
|
username: this.props.viewer?.username || "",
|
||||||
name: this.state.name,
|
name: this.state.name,
|
||||||
email: this.state.email,
|
email: this.state.email,
|
||||||
twitter: this.state.twitter,
|
twitter: this.state.twitter,
|
||||||
message: this.state.message,
|
message: this.state.message,
|
||||||
stored: Strings.bytesToSize(this.props.viewer.stats.bytes),
|
stored: Strings.bytesToSize(this.props.viewer?.stats.bytes || 0),
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.hasError(response);
|
Events.hasError(response);
|
||||||
|
@ -11,8 +11,8 @@ import * as UserBehaviors from "~/common/user-behaviors";
|
|||||||
import { RadioGroup } from "~/components/system/components/RadioGroup";
|
import { RadioGroup } from "~/components/system/components/RadioGroup";
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { ConfirmationModal } from "~/components/core/ConfirmationModal";
|
import { ConfirmationModal } from "~/components/core/ConfirmationModal";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
const SIZE_LIMIT = 1000000;
|
|
||||||
const DEFAULT_IMAGE =
|
const DEFAULT_IMAGE =
|
||||||
"https://slate.textile.io/ipfs/bafkreiaow45dlq5xaydaeqocdxvffudibrzh2c6qandpqkb6t3ahbvh6re";
|
"https://slate.textile.io/ipfs/bafkreiaow45dlq5xaydaeqocdxvffudibrzh2c6qandpqkb6t3ahbvh6re";
|
||||||
|
|
||||||
@ -75,7 +75,10 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
|||||||
slate.data.tags = this.state.tags;
|
slate.data.tags = this.state.tags;
|
||||||
|
|
||||||
let newSuggestions = new Set([...this.state.suggestions, ...this.state.tags]);
|
let newSuggestions = new Set([...this.state.suggestions, ...this.state.tags]);
|
||||||
this.props.onUpdateViewer({ slates, tags: Array.from(newSuggestions) });
|
this.props.onAction({
|
||||||
|
type: "UPDATE_VIEWER",
|
||||||
|
viewer: { slates, tags: Array.from(newSuggestions) },
|
||||||
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -111,16 +114,16 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
|||||||
|
|
||||||
_handleDelete = async (res) => {
|
_handleDelete = async (res) => {
|
||||||
if (!res) {
|
if (!res) {
|
||||||
this.setState({ modalShow: false })
|
this.setState({ modalShow: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let slates = this.props.viewer.slates.filter((slate) => slate.id !== this.props.data.id);
|
let slates = this.props.viewer.slates.filter((slate) => slate.id !== this.props.data.id);
|
||||||
this.props.onUpdateViewer({ slates });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { slates } });
|
||||||
|
|
||||||
this.props.onAction({
|
this.props.onAction({
|
||||||
type: "NAVIGATE",
|
type: "NAVIGATE",
|
||||||
value: "NAV_SLATES",
|
value: "/_/collections",
|
||||||
});
|
});
|
||||||
const response = await Actions.deleteSlate({
|
const response = await Actions.deleteSlate({
|
||||||
id: this.props.data.id,
|
id: this.props.data.id,
|
||||||
@ -130,7 +133,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ modalShow: false })
|
this.setState({ modalShow: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -143,7 +146,7 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
|||||||
object.data.type &&
|
object.data.type &&
|
||||||
Validations.isPreviewableImage(object.data.type) &&
|
Validations.isPreviewableImage(object.data.type) &&
|
||||||
object.data.size &&
|
object.data.size &&
|
||||||
object.data.size < SIZE_LIMIT
|
object.data.size < Constants.linkPreviewSizeLimit
|
||||||
) {
|
) {
|
||||||
preview = Strings.getURLfromCID(object.cid);
|
preview = Strings.getURLfromCID(object.cid);
|
||||||
break;
|
break;
|
||||||
@ -304,16 +307,20 @@ export default class SidebarSingleSlateSettings extends React.Component {
|
|||||||
</System.ButtonPrimary>
|
</System.ButtonPrimary>
|
||||||
|
|
||||||
<div style={{ marginTop: 16 }}>
|
<div style={{ marginTop: 16 }}>
|
||||||
<System.ButtonWarning full onClick={() => this.setState({ modalShow: true })} style={{ overflow: "hidden" }}>
|
<System.ButtonWarning
|
||||||
|
full
|
||||||
|
onClick={() => this.setState({ modalShow: true })}
|
||||||
|
style={{ overflow: "hidden" }}
|
||||||
|
>
|
||||||
Delete collection
|
Delete collection
|
||||||
</System.ButtonWarning>
|
</System.ButtonWarning>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.state.modalShow && (
|
{this.state.modalShow && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
type={"DELETE"}
|
type={"DELETE"}
|
||||||
withValidation={false}
|
withValidation={false}
|
||||||
callback={this._handleDelete}
|
callback={this._handleDelete}
|
||||||
header={`Are you sure you want to delete the collection “${this.state.slatename}”?`}
|
header={`Are you sure you want to delete the collection “${this.state.slatename}”?`}
|
||||||
subHeader={`This collection will be deleted but all your files will remain in your file library. You can’t undo this action.`}
|
subHeader={`This collection will be deleted but all your files will remain in your file library. You can’t undo this action.`}
|
||||||
/>
|
/>
|
||||||
|
@ -116,31 +116,28 @@ const STYLES_MOBILE_HIDDEN = css`
|
|||||||
|
|
||||||
export class GlobalCarousel extends React.Component {
|
export class GlobalCarousel extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
index: 0,
|
|
||||||
visible: false,
|
|
||||||
showSidebar: true,
|
showSidebar: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount = () => {
|
componentDidMount = () => {
|
||||||
window.addEventListener("keydown", this._handleKeyDown);
|
window.addEventListener("keydown", this._handleKeyDown);
|
||||||
window.addEventListener("slate-global-open-carousel", this._handleOpen);
|
// window.addEventListener("slate-global-open-carousel", this._handleOpen);
|
||||||
window.addEventListener("slate-global-close-carousel", this._handleClose);
|
// window.addEventListener("slate-global-close-carousel", this._handleClose);
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillUnmount = () => {
|
componentWillUnmount = () => {
|
||||||
window.removeEventListener("keydown", this._handleKeyDown);
|
window.removeEventListener("keydown", this._handleKeyDown);
|
||||||
window.removeEventListener("slate-global-open-carousel", this._handleOpen);
|
// window.removeEventListener("slate-global-open-carousel", this._handleOpen);
|
||||||
window.removeEventListener("slate-global-close-carousel", this._handleClose);
|
// window.removeEventListener("slate-global-close-carousel", this._handleClose);
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidUpdate = (prevProps) => {
|
findSelectedIndex = () => {
|
||||||
if (
|
const cid = this.props.params?.cid;
|
||||||
!this.props.objects ||
|
if (!cid) {
|
||||||
this.props.objects.length == 0 ||
|
return -1;
|
||||||
this.state.index >= this.props.objects.length
|
|
||||||
) {
|
|
||||||
this._handleClose();
|
|
||||||
}
|
}
|
||||||
|
let index = this.props.objects.findIndex((elem) => elem.cid === cid);
|
||||||
|
return index;
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleKeyDown = (e) => {
|
_handleKeyDown = (e) => {
|
||||||
@ -173,95 +170,132 @@ export class GlobalCarousel extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
setWindowState = (data = {}) => {
|
// setWindowState = (data = {}) => {
|
||||||
const cid = data?.cid;
|
// const cid = data?.cid;
|
||||||
if (this.props.carouselType === "ACTIVITY") {
|
// if (this.props.carouselType === "ACTIVITY") {
|
||||||
window.history.replaceState(
|
// window.history.replaceState(
|
||||||
{ ...window.history.state, cid: cid },
|
// { ...window.history.state, cid: cid },
|
||||||
null,
|
// null,
|
||||||
cid ? `/${data.owner}/${data.slate.slatename}/cid:${cid}` : `/_?scene=NAV_ACTIVITY`
|
// cid ? `/${data.owner}/${data.slate.slatename}/cid:${cid}` : `/_?scene=NAV_ACTIVITY`
|
||||||
);
|
// );
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
let baseURL = window.location.pathname.split("/");
|
// let baseURL = window.location.pathname.split("/");
|
||||||
if (this.props.carouselType === "SLATE") {
|
// if (this.props.carouselType === "SLATE") {
|
||||||
baseURL.length = 3;
|
// baseURL.length = 3;
|
||||||
} else if (this.props.carouselType === "PROFILE") {
|
// } else if (this.props.carouselType === "PROFILE") {
|
||||||
baseURL.length = 2;
|
// baseURL.length = 2;
|
||||||
} else if (this.props.carouselType === "DATA") {
|
// } else if (this.props.carouselType === "DATA") {
|
||||||
baseURL.length = 2;
|
// baseURL.length = 2;
|
||||||
if (cid) {
|
// if (cid) {
|
||||||
baseURL[1] = this.props.viewer.username;
|
// baseURL[1] = this.props.viewer.username;
|
||||||
} else {
|
// } else {
|
||||||
baseURL[1] = "_?scene=NAV_DATA";
|
// baseURL[1] = "_?scene=NAV_DATA";
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
baseURL = baseURL.join("/");
|
// baseURL = baseURL.join("/");
|
||||||
|
|
||||||
window.history.replaceState(
|
// window.history.replaceState(
|
||||||
{ ...window.history.state, cid: cid },
|
// { ...window.history.state, cid: cid },
|
||||||
null,
|
// null,
|
||||||
cid ? `${baseURL}/cid:${cid}` : baseURL
|
// cid ? `${baseURL}/cid:${cid}` : baseURL
|
||||||
);
|
// );
|
||||||
};
|
// };
|
||||||
|
|
||||||
_handleOpen = (e) => {
|
// _handleOpen = (e) => {
|
||||||
let index = e.detail.index;
|
// let index = e.detail.index;
|
||||||
const objects = this.props.objects;
|
// const objects = this.props.objects;
|
||||||
if (e.detail.index === null) {
|
// if (e.detail.index === null) {
|
||||||
if (e.detail.id !== null) {
|
// if (e.detail.id !== null) {
|
||||||
index = objects.findIndex((obj) => obj.id === e.detail.id);
|
// index = objects.findIndex((obj) => obj.id === e.detail.id);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (index === null || index < 0 || index >= objects.length) {
|
// if (index === null || index < 0 || index >= objects.length) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
this.setState({
|
// this.setState({
|
||||||
visible: true,
|
// visible: true,
|
||||||
index: e.detail.index,
|
// index: e.detail.index,
|
||||||
});
|
// });
|
||||||
const data = objects[e.detail.index];
|
// const data = objects[e.detail.index];
|
||||||
this.setWindowState(data);
|
// this.setWindowState(data);
|
||||||
};
|
// };
|
||||||
|
|
||||||
_handleClose = (e) => {
|
_handleClose = (e) => {
|
||||||
if (this.state.visible) {
|
if (e) {
|
||||||
if (e) {
|
e.stopPropagation();
|
||||||
e.stopPropagation();
|
e.preventDefault();
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
this.setState({ visible: false, index: 0 });
|
|
||||||
this.setWindowState();
|
|
||||||
}
|
}
|
||||||
|
if (this.props.onChange) {
|
||||||
|
this.props.onChange(-1);
|
||||||
|
} else {
|
||||||
|
let params = { ...this.props.params };
|
||||||
|
delete params.cid;
|
||||||
|
this.props.onAction({
|
||||||
|
type: "UPDATE_PARAMS",
|
||||||
|
params,
|
||||||
|
redirect: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.setState({ visible: false, index: 0 });
|
||||||
|
// this.setWindowState();
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleNext = (e) => {
|
_handleNext = (e) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
let index = this.state.index + 1;
|
if (this.props.onChange) {
|
||||||
if (index >= this.props.objects.length) {
|
let index = this.props.index + 1;
|
||||||
return;
|
if (index >= this.props.objects.length) return;
|
||||||
|
this.props.onChange(index);
|
||||||
|
} else {
|
||||||
|
let index = this.findSelectedIndex() + 1;
|
||||||
|
if (index >= this.props.objects.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cid = this.props.objects[index].cid;
|
||||||
|
// this.setState({ index });
|
||||||
|
this.props.onAction({
|
||||||
|
type: "UPDATE_PARAMS",
|
||||||
|
params: { ...this.props.params, cid },
|
||||||
|
redirect: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.setState({ index });
|
// const data = this.props.objects[index];
|
||||||
|
// this.setWindowState(data);
|
||||||
const data = this.props.objects[index];
|
|
||||||
this.setWindowState(data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//it uses the initial cid to set which index it is, then it goes off its internal index from there and sets apge still but doesn't get from it?
|
||||||
|
//though that
|
||||||
|
//maybe the initial open is triggered by page, combined with index?
|
||||||
|
//or mayube
|
||||||
|
|
||||||
_handlePrevious = (e) => {
|
_handlePrevious = (e) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
}
|
||||||
let index = this.state.index - 1;
|
if (this.props.onChange) {
|
||||||
if (index < 0) {
|
let index = this.props.index - 1;
|
||||||
return;
|
if (index < 0) return;
|
||||||
|
this.props.onChange(index);
|
||||||
|
} else {
|
||||||
|
let index = this.findSelectedIndex() - 1;
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cid = this.props.objects[index].cid;
|
||||||
|
// this.setState({ index });
|
||||||
|
this.props.onAction({
|
||||||
|
type: "UPDATE_PARAMS",
|
||||||
|
params: { ...this.props.params, cid },
|
||||||
|
redirect: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.setState({ index });
|
// const data = this.props.objects[index];
|
||||||
|
// this.setWindowState(data);
|
||||||
const data = this.props.objects[index];
|
|
||||||
this.setWindowState(data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleToggleSidebar = (e) => {
|
_handleToggleSidebar = (e) => {
|
||||||
@ -272,27 +306,33 @@ export class GlobalCarousel extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (
|
let index;
|
||||||
!this.state.visible ||
|
if (this.props.onChange) {
|
||||||
!this.props.carouselType ||
|
index = this.props.index;
|
||||||
this.state.index < 0 ||
|
} else {
|
||||||
this.state.index >= this.props.objects.length
|
index = this.findSelectedIndex();
|
||||||
) {
|
}
|
||||||
|
if (!this.props.carouselType || index < 0 || index >= this.props.objects.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let data = this.props.objects[this.state.index];
|
let file = this.props.objects[index];
|
||||||
let { isMobile, isOwner } = this.props;
|
if (!file) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { isMobile } = this.props;
|
||||||
|
|
||||||
let isRepost = false;
|
let isRepost = false;
|
||||||
if (this.props.carouselType === "SLATE") {
|
if (this.props.carouselType === "SLATE") {
|
||||||
isRepost = this.props.current?.ownerId !== data.ownerId;
|
isRepost = this.props.data?.ownerId !== file.ownerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
let slide = <SlateMediaObject file={data} isMobile={isMobile} />;
|
let slide = <SlateMediaObject file={file} isMobile={isMobile} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div css={STYLES_ROOT}>
|
<div css={STYLES_ROOT}>
|
||||||
<Alert
|
<Alert
|
||||||
|
viewer={this.props.viewer}
|
||||||
noWarning
|
noWarning
|
||||||
id={isMobile ? "slate-mobile-alert" : null}
|
id={isMobile ? "slate-mobile-alert" : null}
|
||||||
style={
|
style={
|
||||||
@ -308,7 +348,7 @@ export class GlobalCarousel extends React.Component {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<div css={STYLES_ROOT_CONTENT} style={this.props.style} onClick={this._handleClose}>
|
<div css={STYLES_ROOT_CONTENT} style={this.props.style} onClick={this._handleClose}>
|
||||||
{this.state.index > 0 && (
|
{index > 0 && (
|
||||||
<span
|
<span
|
||||||
css={STYLES_BOX}
|
css={STYLES_BOX}
|
||||||
onClick={this._handlePrevious}
|
onClick={this._handlePrevious}
|
||||||
@ -317,7 +357,7 @@ export class GlobalCarousel extends React.Component {
|
|||||||
<SVG.ChevronLeft height="20px" />
|
<SVG.ChevronLeft height="20px" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{this.state.index < this.props.objects.length - 1 && (
|
{index < this.props.objects.length - 1 && (
|
||||||
<span
|
<span
|
||||||
css={STYLES_BOX}
|
css={STYLES_BOX}
|
||||||
onClick={this._handleNext}
|
onClick={this._handleNext}
|
||||||
@ -351,12 +391,13 @@ export class GlobalCarousel extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<span css={STYLES_MOBILE_HIDDEN}>
|
<span css={STYLES_MOBILE_HIDDEN}>
|
||||||
<CarouselSidebar
|
<CarouselSidebar
|
||||||
key={data.id}
|
key={file.id}
|
||||||
{...this.props}
|
{...this.props}
|
||||||
data={data}
|
file={file}
|
||||||
display={this.state.showSidebar ? "block" : "none"}
|
display={this.state.showSidebar ? "block" : "none"}
|
||||||
onClose={this._handleClose}
|
onClose={this._handleClose}
|
||||||
isRepost={isRepost}
|
isRepost={isRepost}
|
||||||
|
onAction={this.props.onAction}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as Constants from "~/common/constants";
|
import * as Constants from "~/common/constants";
|
||||||
|
import * as Styles from "~/common/styles";
|
||||||
|
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
|
|
||||||
@ -7,27 +8,37 @@ const STYLES_POPOVER = css`
|
|||||||
z-index: ${Constants.zindex.tooltip};
|
z-index: ${Constants.zindex.tooltip};
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: ${Constants.font.text};
|
font-family: ${Constants.font.text};
|
||||||
width: 200px;
|
min-width: 200px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: ${Constants.system.white};
|
background-color: ${Constants.system.white};
|
||||||
color: ${Constants.system.pitchBlack};
|
color: ${Constants.system.pitchBlack};
|
||||||
box-shadow: ${Constants.shadow.medium};
|
box-shadow: ${Constants.shadow.medium};
|
||||||
padding: 16px 24px;
|
padding: 16px;
|
||||||
|
border: 1px solid ${Constants.system.gray40};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const STYLES_POPOVER_SECTION = css`
|
||||||
|
border-bottom: 1px solid ${Constants.system.gray40};
|
||||||
|
padding-bottom: 6px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
width: calc(100% - 32px);
|
||||||
|
|
||||||
|
:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
margin-bottom: -6px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_POPOVER_ITEM = css`
|
const STYLES_POPOVER_ITEM = css`
|
||||||
font-family: ${Constants.font.medium};
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
top: 0;
|
padding: 6px 0px;
|
||||||
left: 0;
|
|
||||||
padding: 8px 0px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transition: 200ms ease all;
|
transition: 200ms ease all;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: ${Constants.typescale.lvlN1};
|
|
||||||
|
|
||||||
:hover {
|
:hover {
|
||||||
color: ${Constants.system.brand};
|
color: ${Constants.system.brand};
|
||||||
@ -37,15 +48,27 @@ const STYLES_POPOVER_ITEM = css`
|
|||||||
export class PopoverNavigation extends React.Component {
|
export class PopoverNavigation extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div css={STYLES_POPOVER} style={this.props.style}>
|
<div
|
||||||
{this.props.navigation.map((each, i) => (
|
css={STYLES_POPOVER}
|
||||||
<div
|
style={this.props.style}
|
||||||
key={i}
|
onClick={(e) => {
|
||||||
css={STYLES_POPOVER_ITEM}
|
e.stopPropagation();
|
||||||
style={this.props.itemStyle}
|
e.preventDefault();
|
||||||
onClick={each.onClick}
|
}}
|
||||||
>
|
>
|
||||||
{each.text}
|
{this.props.topSection}
|
||||||
|
{this.props.navigation.map((section, i) => (
|
||||||
|
<div css={STYLES_POPOVER_SECTION}>
|
||||||
|
{section.map((each, j) => (
|
||||||
|
<div
|
||||||
|
key={`${i}-${j}`}
|
||||||
|
css={STYLES_POPOVER_ITEM}
|
||||||
|
style={this.props.itemStyle}
|
||||||
|
onClick={each.onClick}
|
||||||
|
>
|
||||||
|
<div css={Styles.HEADING_05 || this.props.css}>{each.text}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,6 +98,8 @@ export class Boundary extends React.PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleOutsideRectEvent = (e) => {
|
_handleOutsideRectEvent = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
this.props.onOutsideRectEvent(e);
|
this.props.onOutsideRectEvent(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import * as Strings from "~/common/strings";
|
|||||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||||
import { CodeText } from "~/components/system/components/fragments/CodeText";
|
import { CodeText } from "~/components/system/components/fragments/CodeText";
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
import Avatar from "~/components/core/Avatar";
|
import Avatar from "~/components/core/Avatar";
|
||||||
|
|
||||||
@ -146,7 +147,7 @@ const STYLES_TABLE_CONTENT_LINK = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Link = (props) => {
|
const LinkItem = (props) => {
|
||||||
return <span css={STYLES_TABLE_CONTENT_LINK} {...props} />;
|
return <span css={STYLES_TABLE_CONTENT_LINK} {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -188,7 +189,11 @@ export const TableContent = ({ type, text, action, data = {}, onAction }) => {
|
|||||||
case "DEAL_CATEGORY":
|
case "DEAL_CATEGORY":
|
||||||
return <React.Fragment>{text == 1 ? "Storage" : "Retrieval"}</React.Fragment>;
|
return <React.Fragment>{text == 1 ? "Storage" : "Retrieval"}</React.Fragment>;
|
||||||
case "BUTTON":
|
case "BUTTON":
|
||||||
return <Link onClick={() => onAction({ type: "SIDEBAR", value: action, data })}>{text}</Link>;
|
return (
|
||||||
|
<LinkItem onClick={() => onAction({ type: "SIDEBAR", value: action, data })}>
|
||||||
|
{text}
|
||||||
|
</LinkItem>
|
||||||
|
);
|
||||||
case "TRANSACTION_DIRECTION":
|
case "TRANSACTION_DIRECTION":
|
||||||
return COMPONENTS_TRANSACTION_DIRECTION[text];
|
return COMPONENTS_TRANSACTION_DIRECTION[text];
|
||||||
case "TRANSACTION_STATUS":
|
case "TRANSACTION_STATUS":
|
||||||
@ -274,14 +279,16 @@ export const TableContent = ({ type, text, action, data = {}, onAction }) => {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Link onClick={() => window.open(text)}>{text}</Link>;
|
return <LinkItem onClick={() => window.open(text)}>{text}</LinkItem>;
|
||||||
case "SLATE_LINK":
|
case "SLATE_LINK":
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link onClick={() => onAction({ type: "NAVIGATE", value: data.id, data })}>{text}</Link>
|
<LinkItem onClick={() => onAction({ type: "NAVIGATE", value: data.id, data })}>
|
||||||
|
{text}
|
||||||
|
</LinkItem>
|
||||||
);
|
);
|
||||||
case "FILE_LINK":
|
case "FILE_LINK":
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@ -289,7 +296,9 @@ export const TableContent = ({ type, text, action, data = {}, onAction }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link onClick={() => onAction({ type: "NAVIGATE", value: "NAV_FILE", data })}>{text}</Link>
|
<LinkItem onClick={() => onAction({ type: "NAVIGATE", value: "NAV_FILE", data })}>
|
||||||
|
{text}
|
||||||
|
</LinkItem>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return text;
|
return text;
|
||||||
|
@ -28,20 +28,31 @@ export default async ({
|
|||||||
"activity.id",
|
"activity.id",
|
||||||
"activity.type",
|
"activity.type",
|
||||||
"activity.createdAt",
|
"activity.createdAt",
|
||||||
users(),
|
"activity.slateId",
|
||||||
slates(),
|
// users(),
|
||||||
|
// slates(),
|
||||||
files()
|
files()
|
||||||
)
|
)
|
||||||
.from("activity")
|
.from("activity")
|
||||||
.join("users", "users.id", "=", "activity.ownerId")
|
// .join("users", "users.id", "=", "activity.ownerId")
|
||||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
// .leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
.whereRaw("?? < ? and ?? = ? and (?? = any(?) or ?? = any(?))", [
|
||||||
.where("activity.createdAt", "<", date.toISOString())
|
"activity.createdAt",
|
||||||
.whereIn("activity.ownerId", following)
|
date.toISOString(),
|
||||||
.orWhereIn("activity.slateId", subscriptions)
|
"activity.type",
|
||||||
|
"CREATE_SLATE_OBJECT",
|
||||||
|
"activity.ownerId",
|
||||||
|
following,
|
||||||
|
"activity.slateId",
|
||||||
|
subscriptions,
|
||||||
|
])
|
||||||
|
// .where("activity.type", "CREATE_SLATE_OBJECT")
|
||||||
|
// .where("activity.createdAt", "<", date.toISOString())
|
||||||
|
// .whereIn("activity.ownerId", following)
|
||||||
|
// .orWhereIn("activity.slateId", subscriptions)
|
||||||
.orderBy("activity.createdAt", "desc")
|
.orderBy("activity.createdAt", "desc")
|
||||||
.limit(100);
|
.limit(96);
|
||||||
} else if (latestTimestamp) {
|
} else if (latestTimestamp) {
|
||||||
//NOTE(martina): for fetching new updates since the last time they loaded
|
//NOTE(martina): for fetching new updates since the last time they loaded
|
||||||
let date = new Date(latestTimestamp);
|
let date = new Date(latestTimestamp);
|
||||||
@ -50,39 +61,59 @@ export default async ({
|
|||||||
"activity.id",
|
"activity.id",
|
||||||
"activity.type",
|
"activity.type",
|
||||||
"activity.createdAt",
|
"activity.createdAt",
|
||||||
users(),
|
"activity.slateId",
|
||||||
slates(),
|
// users(),
|
||||||
|
// slates(),
|
||||||
files()
|
files()
|
||||||
)
|
)
|
||||||
.from("activity")
|
.from("activity")
|
||||||
.join("users", "users.id", "=", "activity.ownerId")
|
// .join("users", "users.id", "=", "activity.ownerId")
|
||||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
// .leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||||
.where("activity.createdAt", ">", date.toISOString())
|
.whereRaw("?? > ? and ?? = ? and (?? = any(?) or ?? = any(?))", [
|
||||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
"activity.createdAt",
|
||||||
.whereIn("activity.ownerId", following)
|
date.toISOString(),
|
||||||
.orWhereIn("activity.slateId", subscriptions)
|
"activity.type",
|
||||||
|
"CREATE_SLATE_OBJECT",
|
||||||
|
"activity.ownerId",
|
||||||
|
following,
|
||||||
|
"activity.slateId",
|
||||||
|
subscriptions,
|
||||||
|
])
|
||||||
|
// .where("activity.createdAt", ">", date.toISOString())
|
||||||
|
// .where("activity.type", "CREATE_SLATE_OBJECT")
|
||||||
|
// .whereIn("activity.ownerId", following)
|
||||||
|
// .orWhereIn("activity.slateId", subscriptions)
|
||||||
.orderBy("activity.createdAt", "desc")
|
.orderBy("activity.createdAt", "desc")
|
||||||
.limit(100);
|
.limit(96);
|
||||||
} else {
|
} else {
|
||||||
//NOTE(martina): for the first fetch they make, when they have not loaded any explore events yet
|
//NOTE(martina): for the first fetch they make, when they have not loaded any explore events yet
|
||||||
query = await DB.select(
|
query = await DB.select(
|
||||||
"activity.id",
|
"activity.id",
|
||||||
"activity.type",
|
"activity.type",
|
||||||
"activity.createdAt",
|
"activity.createdAt",
|
||||||
users(),
|
"activity.slateId",
|
||||||
slates(),
|
// users(),
|
||||||
|
// slates(),
|
||||||
files()
|
files()
|
||||||
)
|
)
|
||||||
.from("activity")
|
.from("activity")
|
||||||
.join("users", "users.id", "=", "activity.ownerId")
|
// .join("users", "users.id", "=", "activity.ownerId")
|
||||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
// .leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
.whereRaw("?? = ? and (?? = any(?) or ?? = any(?))", [
|
||||||
.whereIn("activity.ownerId", following)
|
"activity.type",
|
||||||
.orWhereIn("activity.slateId", subscriptions)
|
"CREATE_SLATE_OBJECT",
|
||||||
|
"activity.ownerId",
|
||||||
|
following,
|
||||||
|
"activity.slateId",
|
||||||
|
subscriptions,
|
||||||
|
])
|
||||||
|
// .where("activity.type", "CREATE_SLATE_OBJECT")
|
||||||
|
// .whereIn("activity.ownerId", following)
|
||||||
|
// .orWhereIn("activity.slateId", subscriptions)
|
||||||
.orderBy("activity.createdAt", "desc")
|
.orderBy("activity.createdAt", "desc")
|
||||||
.limit(100);
|
.limit(96);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!query) {
|
if (!query) {
|
||||||
|
@ -23,18 +23,19 @@ export default async ({ earliestTimestamp, latestTimestamp }) => {
|
|||||||
"activity.id",
|
"activity.id",
|
||||||
"activity.type",
|
"activity.type",
|
||||||
"activity.createdAt",
|
"activity.createdAt",
|
||||||
users(),
|
"activity.slateId",
|
||||||
slates(),
|
// users(),
|
||||||
|
// slates(),
|
||||||
files()
|
files()
|
||||||
)
|
)
|
||||||
.from("activity")
|
.from("activity")
|
||||||
.join("users", "users.id", "=", "activity.ownerId")
|
// .join("users", "users.id", "=", "activity.ownerId")
|
||||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
// .leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||||
.where("activity.createdAt", "<", date.toISOString())
|
.where("activity.createdAt", "<", date.toISOString())
|
||||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
.where("activity.type", "CREATE_SLATE_OBJECT")
|
||||||
.orderBy("activity.createdAt", "desc")
|
.orderBy("activity.createdAt", "desc")
|
||||||
.limit(100);
|
.limit(96);
|
||||||
} else if (latestTimestamp) {
|
} else if (latestTimestamp) {
|
||||||
//NOTE(martina): for fetching new updates since the last time they loaded
|
//NOTE(martina): for fetching new updates since the last time they loaded
|
||||||
let date = new Date(latestTimestamp);
|
let date = new Date(latestTimestamp);
|
||||||
@ -43,35 +44,37 @@ export default async ({ earliestTimestamp, latestTimestamp }) => {
|
|||||||
"activity.id",
|
"activity.id",
|
||||||
"activity.type",
|
"activity.type",
|
||||||
"activity.createdAt",
|
"activity.createdAt",
|
||||||
users(),
|
"activity.slateId",
|
||||||
slates(),
|
// users(),
|
||||||
|
// slates(),
|
||||||
files()
|
files()
|
||||||
)
|
)
|
||||||
.from("activity")
|
.from("activity")
|
||||||
.join("users", "users.id", "=", "activity.ownerId")
|
// .join("users", "users.id", "=", "activity.ownerId")
|
||||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
// .leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||||
.where("activity.createdAt", ">", date.toISOString())
|
.where("activity.createdAt", ">", date.toISOString())
|
||||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
.where("activity.type", "CREATE_SLATE_OBJECT")
|
||||||
.orderBy("activity.createdAt", "desc")
|
.orderBy("activity.createdAt", "desc")
|
||||||
.limit(100);
|
.limit(96);
|
||||||
} else {
|
} else {
|
||||||
//NOTE(martina): for the first fetch they make, when they have not loaded any explore events yet
|
//NOTE(martina): for the first fetch they make, when they have not loaded any explore events yet
|
||||||
query = await DB.select(
|
query = await DB.select(
|
||||||
"activity.id",
|
"activity.id",
|
||||||
"activity.type",
|
"activity.type",
|
||||||
"activity.createdAt",
|
"activity.createdAt",
|
||||||
users(),
|
"activity.slateId",
|
||||||
slates(),
|
// users(),
|
||||||
|
// slates(),
|
||||||
files()
|
files()
|
||||||
)
|
)
|
||||||
.from("activity")
|
.from("activity")
|
||||||
.join("users", "users.id", "=", "activity.ownerId")
|
// .join("users", "users.id", "=", "activity.ownerId")
|
||||||
.leftJoin("files", "files.id", "=", "activity.fileId")
|
.leftJoin("files", "files.id", "=", "activity.fileId")
|
||||||
.leftJoin("slates", "slates.id", "=", "activity.slateId")
|
// .leftJoin("slates", "slates.id", "=", "activity.slateId")
|
||||||
.where("activity.type", "CREATE_SLATE_OBJECT")
|
.where("activity.type", "CREATE_SLATE_OBJECT")
|
||||||
.orderBy("activity.createdAt", "desc")
|
.orderBy("activity.createdAt", "desc")
|
||||||
.limit(100);
|
.limit(96);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!query || query.error) {
|
if (!query || query.error) {
|
||||||
|
@ -20,10 +20,18 @@ export default async ({ ids, sanitize = false, publicOnly = false }) => {
|
|||||||
.from("files")
|
.from("files")
|
||||||
.leftJoin("slate_files", "slate_files.fileId", "=", "files.id")
|
.leftJoin("slate_files", "slate_files.fileId", "=", "files.id")
|
||||||
.leftJoin("slates", "slates.id", "=", "slate_files.slateId")
|
.leftJoin("slates", "slates.id", "=", "slate_files.slateId")
|
||||||
.whereIn("files.id", ids)
|
.whereRaw("?? = any(?) and (?? = ? or ?? = ?)", [
|
||||||
.where("files.isPublic", true)
|
"files.id",
|
||||||
.orWhereIn("files.id", ids)
|
ids,
|
||||||
.andWhere("slates.isPublic", true)
|
"files.isPublic",
|
||||||
|
true,
|
||||||
|
"slates.isPublic",
|
||||||
|
true,
|
||||||
|
])
|
||||||
|
// .whereIn("files.id", ids)
|
||||||
|
// .where("files.isPublic", true)
|
||||||
|
// .orWhereIn("files.id", ids)
|
||||||
|
// .andWhere("slates.isPublic", true)
|
||||||
.groupBy("files.id");
|
.groupBy("files.id");
|
||||||
} else {
|
} else {
|
||||||
query = await DB.select("*").from("files").whereIn("id", ids);
|
query = await DB.select("*").from("files").whereIn("id", ids);
|
||||||
|
@ -20,8 +20,16 @@ export default async ({ id, sanitize = false, publicOnly = false }) => {
|
|||||||
.from("files")
|
.from("files")
|
||||||
.leftJoin("slate_files", "slate_files.fileId", "=", "files.id")
|
.leftJoin("slate_files", "slate_files.fileId", "=", "files.id")
|
||||||
.leftJoin("slates", "slate_files.slateId", "=", "slates.id")
|
.leftJoin("slates", "slate_files.slateId", "=", "slates.id")
|
||||||
.where({ "files.ownerId": id, "slates.isPublic": true })
|
.whereRaw("?? = ? and (?? = ? or ?? = ?)", [
|
||||||
.orWhere({ "files.ownerId": id, "files.isPublic": true })
|
"files.ownerId",
|
||||||
|
id,
|
||||||
|
"files.isPublic",
|
||||||
|
true,
|
||||||
|
"slates.isPublic",
|
||||||
|
true,
|
||||||
|
])
|
||||||
|
// .where({ "files.ownerId": id, "slates.isPublic": true })
|
||||||
|
// .orWhere({ "files.ownerId": id, "files.isPublic": true })
|
||||||
.orderBy("files.createdAt", "desc")
|
.orderBy("files.createdAt", "desc")
|
||||||
.groupBy("files.id");
|
.groupBy("files.id");
|
||||||
} else {
|
} else {
|
||||||
|
@ -53,8 +53,16 @@ export default async ({ id, sanitize = false, includeFiles = false, publicOnly =
|
|||||||
.from("files")
|
.from("files")
|
||||||
.leftJoin("slate_files", "slate_files.fileId", "=", "files.id")
|
.leftJoin("slate_files", "slate_files.fileId", "=", "files.id")
|
||||||
.leftJoin("slates", "slate_files.slateId", "=", "slates.id")
|
.leftJoin("slates", "slate_files.slateId", "=", "slates.id")
|
||||||
.where({ "files.ownerId": id, "slates.isPublic": true })
|
.whereRaw("?? = ? and (?? = ? or ?? = ?)", [
|
||||||
.orWhere({ "files.ownerId": id, "files.isPublic": true })
|
"files.ownerId",
|
||||||
|
id,
|
||||||
|
"files.isPublic",
|
||||||
|
true,
|
||||||
|
"slates.isPublic",
|
||||||
|
true,
|
||||||
|
])
|
||||||
|
// .where({ "files.ownerId": id, "slates.isPublic": true })
|
||||||
|
// .orWhere({ "files.ownerId": id, "files.isPublic": true })
|
||||||
.orderBy("files.createdAt", "desc")
|
.orderBy("files.createdAt", "desc")
|
||||||
.groupBy("files.id");
|
.groupBy("files.id");
|
||||||
|
|
||||||
|
@ -64,25 +64,35 @@ export default async ({ username, sanitize = false, includeFiles = false, public
|
|||||||
// .first();
|
// .first();
|
||||||
query = await DB.select("*").from("users").where({ username }).first();
|
query = await DB.select("*").from("users").where({ username }).first();
|
||||||
|
|
||||||
const id = query.id;
|
const id = query?.id;
|
||||||
|
|
||||||
let library = await DB.select(
|
if (id) {
|
||||||
"files.id",
|
let library = await DB.select(
|
||||||
"files.ownerId",
|
"files.id",
|
||||||
"files.cid",
|
"files.ownerId",
|
||||||
"files.isPublic",
|
"files.cid",
|
||||||
"files.filename",
|
"files.isPublic",
|
||||||
"files.data"
|
"files.filename",
|
||||||
)
|
"files.data"
|
||||||
.from("files")
|
)
|
||||||
.leftJoin("slate_files", "slate_files.fileId", "=", "files.id")
|
.from("files")
|
||||||
.leftJoin("slates", "slate_files.slateId", "=", "slates.id")
|
.leftJoin("slate_files", "slate_files.fileId", "=", "files.id")
|
||||||
.where({ "files.ownerId": id, "slates.isPublic": true })
|
.leftJoin("slates", "slate_files.slateId", "=", "slates.id")
|
||||||
.orWhere({ "files.ownerId": id, "files.isPublic": true })
|
// .where({ "files.ownerId": id, "slates.isPublic": true })
|
||||||
.orderBy("files.createdAt", "desc")
|
// .orWhere({ "files.ownerId": id, "files.isPublic": true })
|
||||||
.groupBy("files.id");
|
.whereRaw("?? = ? and (?? = ? or ?? = ?)", [
|
||||||
|
"files.ownerId",
|
||||||
|
id,
|
||||||
|
"files.isPublic",
|
||||||
|
true,
|
||||||
|
"slates.isPublic",
|
||||||
|
true,
|
||||||
|
])
|
||||||
|
.orderBy("files.createdAt", "desc")
|
||||||
|
.groupBy("files.id");
|
||||||
|
|
||||||
query.library = library;
|
query.library = library;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
query = await DB.select(
|
query = await DB.select(
|
||||||
"users.id",
|
"users.id",
|
||||||
|
@ -221,7 +221,7 @@ export const getById = async ({ id }) => {
|
|||||||
pdfBytes,
|
pdfBytes,
|
||||||
},
|
},
|
||||||
tags,
|
tags,
|
||||||
userBucketCID: bucketRoot?.path,
|
userBucketCID: bucketRoot?.path || null,
|
||||||
keys,
|
keys,
|
||||||
slates,
|
slates,
|
||||||
subscriptions,
|
subscriptions,
|
||||||
|
@ -6,20 +6,19 @@ import { IncomingWebhook } from "@slack/webhook";
|
|||||||
const url = `https://hooks.slack.com/services/${Environment.SUPPORT_SLACK_WEBHOOK_KEY}`;
|
const url = `https://hooks.slack.com/services/${Environment.SUPPORT_SLACK_WEBHOOK_KEY}`;
|
||||||
const webhook = new IncomingWebhook(url);
|
const webhook = new IncomingWebhook(url);
|
||||||
|
|
||||||
export const sendSlackMessage = ({
|
export const sendSlackMessage = ({ username, name, email, twitter, message, stored }) => {
|
||||||
username,
|
|
||||||
name,
|
|
||||||
email,
|
|
||||||
twitter,
|
|
||||||
message,
|
|
||||||
stored,
|
|
||||||
}) => {
|
|
||||||
if (Strings.isEmpty(Environment.SUPPORT_SLACK_WEBHOOK_KEY)) {
|
if (Strings.isEmpty(Environment.SUPPORT_SLACK_WEBHOOK_KEY)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const userProfileURL = `https://slate.host/${username}`;
|
let userURL;
|
||||||
const userURL = `<${userProfileURL}|${username}>`;
|
if (username) {
|
||||||
|
const userProfileURL = `https://slate.host/${username}`;
|
||||||
|
userURL = `<${userProfileURL}|${username}>`;
|
||||||
|
} else {
|
||||||
|
userURL = "[Not authenticated]";
|
||||||
|
}
|
||||||
|
|
||||||
let twitterURL = "";
|
let twitterURL = "";
|
||||||
if (twitter) {
|
if (twitter) {
|
||||||
const twitterProfileURL = `https://twitter.com/${twitter.replace("@", "")}`;
|
const twitterProfileURL = `https://twitter.com/${twitter.replace("@", "")}`;
|
||||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -4556,7 +4556,11 @@
|
|||||||
"version": "2.6.12",
|
"version": "2.6.12",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
|
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
|
||||||
|
<<<<<<< HEAD
|
||||||
"deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.",
|
"deprecated": "core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.",
|
||||||
|
=======
|
||||||
|
"deprecated": "core-js@<3 is no longer maintained and not recommended for usage due to the number of issues. Please, upgrade your dependencies to the actual version of core-js@3.",
|
||||||
|
>>>>>>> added standard typography styles
|
||||||
"hasInstallScript": true
|
"hasInstallScript": true
|
||||||
},
|
},
|
||||||
"node_modules/core-js-compat": {
|
"node_modules/core-js-compat": {
|
||||||
|
@ -3,13 +3,18 @@ import * as React from "react";
|
|||||||
import Application from "~/components/core/Application";
|
import Application from "~/components/core/Application";
|
||||||
|
|
||||||
export const getServerSideProps = async ({ query }) => {
|
export const getServerSideProps = async ({ query }) => {
|
||||||
|
// return {
|
||||||
|
// props: {
|
||||||
|
// viewer: query.viewer,
|
||||||
|
// isMobile: query.isMobile,
|
||||||
|
// isMac: query.isMac,
|
||||||
|
// resources: query.resources,
|
||||||
|
// page: query.page,
|
||||||
|
// data: query.data,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
return {
|
return {
|
||||||
props: {
|
props: { ...query },
|
||||||
viewer: query.viewer,
|
|
||||||
isMobile: query.isMobile,
|
|
||||||
isMac: query.isMac,
|
|
||||||
resources: query.resources,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,10 +22,13 @@ export default class ApplicationPage extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Application
|
<Application
|
||||||
viewer={this.props.viewer}
|
{...this.props}
|
||||||
isMobile={this.props.isMobile}
|
// viewer={this.props.viewer}
|
||||||
isMac={this.props.isMac}
|
// isMobile={this.props.isMobile}
|
||||||
resources={this.props.resources}
|
// isMac={this.props.isMac}
|
||||||
|
// resources={this.props.resources}
|
||||||
|
// page={this.props.page}
|
||||||
|
// data={this.props.data}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ import WebsitePrototypeHeader from "~/components/core/WebsitePrototypeHeader";
|
|||||||
import WebsitePrototypeFooter from "~/components/core/WebsitePrototypeFooter";
|
import WebsitePrototypeFooter from "~/components/core/WebsitePrototypeFooter";
|
||||||
import CTATransition from "~/components/core/CTATransition";
|
import CTATransition from "~/components/core/CTATransition";
|
||||||
|
|
||||||
const SIZE_LIMIT = 1000000; //NOTE(martina): 1mb limit for twitter preview images
|
|
||||||
const DEFAULT_IMAGE =
|
const DEFAULT_IMAGE =
|
||||||
"https://slate.textile.io/ipfs/bafkreiaow45dlq5xaydaeqocdxvffudibrzh2c6qandpqkb6t3ahbvh6re";
|
"https://slate.textile.io/ipfs/bafkreiaow45dlq5xaydaeqocdxvffudibrzh2c6qandpqkb6t3ahbvh6re";
|
||||||
const DEFAULT_BOOK =
|
const DEFAULT_BOOK =
|
||||||
@ -233,7 +232,7 @@ export default class SlatePage extends React.Component {
|
|||||||
objects[i].data.type &&
|
objects[i].data.type &&
|
||||||
Validations.isPreviewableImage(objects[i].data.type) &&
|
Validations.isPreviewableImage(objects[i].data.type) &&
|
||||||
objects[i].data.size &&
|
objects[i].data.size &&
|
||||||
objects[i].data.size < SIZE_LIMIT
|
objects[i].data.size < Constants.linkPreviewSizeLimit
|
||||||
) {
|
) {
|
||||||
image = Strings.getURLfromCID(objects[i].cid);
|
image = Strings.getURLfromCID(objects[i].cid);
|
||||||
break;
|
break;
|
||||||
@ -320,7 +319,7 @@ export default class SlatePage extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div css={STYLES_SLATE}>
|
<div css={STYLES_SLATE}>
|
||||||
<GlobalCarousel
|
<GlobalCarousel
|
||||||
current={this.props.slate}
|
data={this.props.slate}
|
||||||
carouselType="SLATE"
|
carouselType="SLATE"
|
||||||
viewer={this.props.viewer}
|
viewer={this.props.viewer}
|
||||||
objects={objects}
|
objects={objects}
|
@ -60,5 +60,5 @@ export default async (req, res) => {
|
|||||||
return res.status(400).send({ decorator: "SERVER_GET_ACTIVITY_NOT_FOUND", error: true });
|
return res.status(400).send({ decorator: "SERVER_GET_ACTIVITY_NOT_FOUND", error: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({ decorator: "SERVER_GET_ACTIVITY", activity: response });
|
return res.status(200).send({ decorator: "SERVER_GET_ACTIVITY", data: response });
|
||||||
};
|
};
|
||||||
|
@ -29,5 +29,5 @@ export default async (req, res) => {
|
|||||||
return res.status(400).send({ decorator: "SERVER_GET_EXPLORE_NOT_FOUND", error: true });
|
return res.status(400).send({ decorator: "SERVER_GET_EXPLORE_NOT_FOUND", error: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({ decorator: "SERVER_GET_EXPLORE", explore: response });
|
return res.status(200).send({ decorator: "SERVER_GET_EXPLORE", data: response });
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,8 @@ import * as Utilities from "~/node_common/utilities";
|
|||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const id = Utilities.getIdFromCookie(req);
|
const id = Utilities.getIdFromCookie(req);
|
||||||
|
console.log(id);
|
||||||
|
console.log(req);
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return res.status(401).send({ decorator: "SERVER_NOT_AUTHENTICATED", error: true });
|
return res.status(401).send({ decorator: "SERVER_NOT_AUTHENTICATED", error: true });
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,4 @@ export default async (req, res) => {
|
|||||||
const token = JWT.sign({ id: user.id, username: user.username }, Environment.JWT_SECRET);
|
const token = JWT.sign({ id: user.id, username: user.username }, Environment.JWT_SECRET);
|
||||||
|
|
||||||
res.status(200).send({ decorator: "SERVER_SIGN_IN", success: true, token });
|
res.status(200).send({ decorator: "SERVER_SIGN_IN", success: true, token });
|
||||||
if (req.body.data.redirectURL) {
|
|
||||||
res.redirect(req.body.data.redirectURL);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -4,29 +4,6 @@ import * as Utilities from "~/node_common/utilities";
|
|||||||
import * as Strings from "~/common/strings";
|
import * as Strings from "~/common/strings";
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const id = Utilities.getIdFromCookie(req);
|
|
||||||
if (!id) {
|
|
||||||
return res.status(401).send({ decorator: "SERVER_NOT_AUTHENTICATED", error: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await Data.getUserById({
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return res.status(404).send({
|
|
||||||
decorator: "SERVER_USER_NOT_FOUND",
|
|
||||||
error: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.error) {
|
|
||||||
return res.status(500).send({
|
|
||||||
decorator: "SERVER_USER_NOT_FOUND",
|
|
||||||
error: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let slate;
|
let slate;
|
||||||
if (req.body.data.id) {
|
if (req.body.data.id) {
|
||||||
slate = await Data.getSlateById({ id: req.body.data.id, includeFiles: true, sanitize: true });
|
slate = await Data.getSlateById({ id: req.body.data.id, includeFiles: true, sanitize: true });
|
||||||
@ -46,11 +23,15 @@ export default async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!slate.isPublic && slate.ownerId !== id) {
|
if (!slate.isPublic) {
|
||||||
return res.status(403).send({
|
const id = Utilities.getIdFromCookie(req);
|
||||||
decorator: "SERVER_GET_SERIALIZED_SLATE_PRIVATE_ACCESS_DENIED",
|
|
||||||
error: true,
|
if (slate.ownerId !== id) {
|
||||||
});
|
return res.status(403).send({
|
||||||
|
decorator: "SERVER_GET_SERIALIZED_SLATE_PRIVATE_ACCESS_DENIED",
|
||||||
|
error: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let owner = await Data.getUserById({ id: slate.ownerId, sanitize: true });
|
let owner = await Data.getUserById({ id: slate.ownerId, sanitize: true });
|
||||||
@ -66,6 +47,6 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
decorator: "SERVER_GET_SERIALIZED_SLATE",
|
decorator: "SERVER_GET_SERIALIZED_SLATE",
|
||||||
slate,
|
data: slate,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -4,29 +4,6 @@ import * as Serializers from "~/node_common/serializers";
|
|||||||
import * as Strings from "~/common/strings";
|
import * as Strings from "~/common/strings";
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const id = Utilities.getIdFromCookie(req);
|
|
||||||
if (!id) {
|
|
||||||
return res.status(401).send({ decorator: "SERVER_NOT_AUTHENTICATED", error: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await Data.getUserById({
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return res.status(404).send({
|
|
||||||
decorator: "SERVER_USER_NOT_FOUND",
|
|
||||||
error: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.error) {
|
|
||||||
return res.status(500).send({
|
|
||||||
decorator: "SERVER_USER_NOT_FOUND",
|
|
||||||
error: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await Data.getSlateById({
|
const response = await Data.getSlateById({
|
||||||
id: req.body.data.id,
|
id: req.body.data.id,
|
||||||
includeFiles: true,
|
includeFiles: true,
|
||||||
@ -41,11 +18,15 @@ export default async (req, res) => {
|
|||||||
return res.status(500).send({ decorator: "SERVER_GET_SLATE_NOT_FOUND", error: true });
|
return res.status(500).send({ decorator: "SERVER_GET_SLATE_NOT_FOUND", error: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.isPublic && response.ownerId !== id) {
|
if (!response.isPublic) {
|
||||||
return res.status(403).send({
|
const id = Utilities.getIdFromCookie(req);
|
||||||
decorator: "SERVER_GET_SLATE_PRIVATE_ACCESS_DENIED",
|
|
||||||
error: true,
|
if (!ownerId || response.ownerId !== id) {
|
||||||
});
|
return res.status(403).send({
|
||||||
|
decorator: "SERVER_GET_SLATE_PRIVATE_ACCESS_DENIED",
|
||||||
|
error: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({ decorator: "SERVER_GET_SLATE", slate: response });
|
return res.status(200).send({ decorator: "SERVER_GET_SLATE", slate: response });
|
||||||
|
@ -4,22 +4,22 @@ import * as Serializers from "~/node_common/serializers";
|
|||||||
import * as Support from "~/node_common/support";
|
import * as Support from "~/node_common/support";
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const id = Utilities.getIdFromCookie(req);
|
// const id = Utilities.getIdFromCookie(req);
|
||||||
if (!id) {
|
// if (!id) {
|
||||||
return res.status(401).send({ decorator: "SERVER_NOT_AUTHENTICATED", error: true });
|
// return res.status(401).send({ decorator: "SERVER_NOT_AUTHENTICATED", error: true });
|
||||||
}
|
// }
|
||||||
|
|
||||||
const user = await Data.getUserById({
|
// const user = await Data.getUserById({
|
||||||
id,
|
// id,
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (!user) {
|
// if (!user) {
|
||||||
return res.status(404).send({ decorator: "SERVER_USER_NOT_FOUND", error: true });
|
// return res.status(404).send({ decorator: "SERVER_USER_NOT_FOUND", error: true });
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (user.error) {
|
// if (user.error) {
|
||||||
return res.status(500).send({ decorator: "SERVER_USER_NOT_FOUND", error: true });
|
// return res.status(500).send({ decorator: "SERVER_USER_NOT_FOUND", error: true });
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (!req.body.data) {
|
if (!req.body.data) {
|
||||||
return res.status(500).send({
|
return res.status(500).send({
|
||||||
@ -42,10 +42,6 @@ export default async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req.body.data.username) {
|
|
||||||
req.body.data.username = user.username;
|
|
||||||
}
|
|
||||||
|
|
||||||
let status = await Support.sendSlackMessage(req.body.data);
|
let status = await Support.sendSlackMessage(req.body.data);
|
||||||
if (status) {
|
if (status) {
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
|
@ -3,10 +3,6 @@ import * as Strings from "~/common/strings";
|
|||||||
import * as Utilities from "~/node_common/utilities";
|
import * as Utilities from "~/node_common/utilities";
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const id = Utilities.getIdFromCookie(req);
|
|
||||||
if (!id) {
|
|
||||||
return res.status(401).send({ decorator: "SERVER_NOT_AUTHENTICATED", error: true });
|
|
||||||
}
|
|
||||||
let user;
|
let user;
|
||||||
|
|
||||||
if (req.body.data.id) {
|
if (req.body.data.id) {
|
||||||
|
@ -10,6 +10,7 @@ import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
|||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { TabGroup, PrimaryTabGroup, SecondaryTabGroup } from "~/components/core/TabGroup";
|
import { TabGroup, PrimaryTabGroup, SecondaryTabGroup } from "~/components/core/TabGroup";
|
||||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
import EmptyState from "~/components/core/EmptyState";
|
import EmptyState from "~/components/core/EmptyState";
|
||||||
import ScenePage from "~/components/core/ScenePage";
|
import ScenePage from "~/components/core/ScenePage";
|
||||||
@ -64,15 +65,6 @@ const STYLES_SECONDARY = css`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const STYLES_SECONDARY_HOVERABLE = css`
|
|
||||||
${STYLES_SECONDARY}
|
|
||||||
padding: 8px 16px;
|
|
||||||
|
|
||||||
:hover {
|
|
||||||
color: ${Constants.system.brand} !important;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const STYLES_GRADIENT = css`
|
const STYLES_GRADIENT = css`
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
180deg,
|
180deg,
|
||||||
@ -112,8 +104,8 @@ class ActivitySquare extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const item = this.props.item;
|
const item = this.props.item;
|
||||||
const size = this.props.size;
|
const size = this.props.size;
|
||||||
const isImage =
|
// const isImage =
|
||||||
Validations.isPreviewableImage(item.file.data.type) || !!item.file.data.coverImage;
|
// Validations.isPreviewableImage(item.file.data.type) || !!item.file.data.coverImage;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
css={STYLES_IMAGE_BOX}
|
css={STYLES_IMAGE_BOX}
|
||||||
@ -128,42 +120,30 @@ class ActivitySquare extends React.Component {
|
|||||||
style={{ border: "none" }}
|
style={{ border: "none" }}
|
||||||
imageStyle={{ border: "none" }}
|
imageStyle={{ border: "none" }}
|
||||||
/>
|
/>
|
||||||
{this.state.showText || this.props.isMobile ? <div css={STYLES_GRADIENT} /> : null}
|
|
||||||
{this.state.showText || this.props.isMobile ? (
|
|
||||||
<div css={STYLES_TEXT_AREA} style={{ width: this.props.size }}>
|
|
||||||
{/* {isImage ? null : (
|
|
||||||
<div
|
|
||||||
css={STYLES_TITLE}
|
|
||||||
style={{
|
|
||||||
color: Constants.system.textGray,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item.file.data.name || item.file.filename}
|
|
||||||
</div>
|
|
||||||
)} */}
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
color: Constants.system.white,
|
|
||||||
}}
|
|
||||||
css={this.props.onClick ? STYLES_SECONDARY_HOVERABLE : STYLES_SECONDARY}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
this.props.onClick();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SVG.ArrowDownLeft
|
|
||||||
height="10px"
|
|
||||||
style={{ transform: "scaleX(-1)", marginRight: 4 }}
|
|
||||||
/>
|
|
||||||
{item.slate.data.name || item.slate.slatename}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// {this.state.showText || this.props.isMobile ? <div css={STYLES_GRADIENT} /> : null}
|
||||||
|
// {this.state.showText || this.props.isMobile ? (
|
||||||
|
// <div css={STYLES_TEXT_AREA} style={{ width: this.props.size }}>
|
||||||
|
// <span
|
||||||
|
// style={{
|
||||||
|
// color: Constants.system.white,
|
||||||
|
// padding: "8px 16px",
|
||||||
|
// }}
|
||||||
|
// css={STYLES_SECONDARY}
|
||||||
|
// >
|
||||||
|
// <SVG.ArrowDownLeft
|
||||||
|
// height="10px"
|
||||||
|
// style={{ transform: "scaleX(-1)", marginRight: 4 }}
|
||||||
|
// />
|
||||||
|
// {item.slate.data.name || item.slate.slatename}
|
||||||
|
// </span>
|
||||||
|
// </div>
|
||||||
|
// ) : null}
|
||||||
|
|
||||||
const ActivityRectangle = ({ item, width, height }) => {
|
const ActivityRectangle = ({ item, width, height }) => {
|
||||||
let file;
|
let file;
|
||||||
for (let obj of item.slate?.objects || []) {
|
for (let obj of item.slate?.objects || []) {
|
||||||
@ -212,21 +192,21 @@ export default class SceneActivity extends React.Component {
|
|||||||
counter = 0;
|
counter = 0;
|
||||||
state = {
|
state = {
|
||||||
imageSize: 200,
|
imageSize: 200,
|
||||||
tab: 0,
|
loading: false,
|
||||||
loading: "loading",
|
carouselIndex: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
this.fetchActivityItems(true);
|
||||||
this.calculateWidth();
|
this.calculateWidth();
|
||||||
this.debounceInstance = Window.debounce(this.calculateWidth, 200);
|
this.debounceInstance = Window.debounce(this.calculateWidth, 200);
|
||||||
this.scrollDebounceInstance = Window.debounce(this._handleScroll, 200);
|
this.scrollDebounceInstance = Window.debounce(this._handleScroll, 200);
|
||||||
window.addEventListener("resize", this.debounceInstance);
|
window.addEventListener("resize", this.debounceInstance);
|
||||||
window.addEventListener("scroll", this.scrollDebounceInstance);
|
window.addEventListener("scroll", this.scrollDebounceInstance);
|
||||||
this.fetchActivityItems(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (prevProps.tab !== this.props.tab) {
|
if (prevProps.page.params?.tab !== this.props.page.params?.tab) {
|
||||||
this.fetchActivityItems(true);
|
this.fetchActivityItems(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,10 +238,23 @@ export default class SceneActivity extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchActivityItems = async (update = false) => {
|
fetchActivityItems = async (update = false) => {
|
||||||
const isExplore = this.props.tab === 1;
|
if (this.state.loading === "loading") return;
|
||||||
|
let tab = this.props.page.params?.tab;
|
||||||
|
if (!tab) {
|
||||||
|
if (this.props.viewer) {
|
||||||
|
tab = "activity";
|
||||||
|
} else {
|
||||||
|
tab = "explore";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const isExplore = tab === "explore";
|
||||||
this.setState({ loading: "loading" });
|
this.setState({ loading: "loading" });
|
||||||
let activity = isExplore ? this.props.viewer.explore || [] : this.props.viewer.activity || [];
|
let activity;
|
||||||
|
if (this.props.viewer) {
|
||||||
|
activity = isExplore ? this.props.viewer?.explore || [] : this.props.viewer?.activity || [];
|
||||||
|
} else {
|
||||||
|
activity = this.state.explore || [];
|
||||||
|
}
|
||||||
let requestObject = {};
|
let requestObject = {};
|
||||||
if (activity.length) {
|
if (activity.length) {
|
||||||
if (update) {
|
if (update) {
|
||||||
@ -271,6 +264,7 @@ export default class SceneActivity extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("start fetching");
|
||||||
let response;
|
let response;
|
||||||
if (isExplore) {
|
if (isExplore) {
|
||||||
response = await Actions.getExplore(requestObject);
|
response = await Actions.getExplore(requestObject);
|
||||||
@ -280,13 +274,13 @@ export default class SceneActivity extends React.Component {
|
|||||||
|
|
||||||
response = await Actions.getActivity(requestObject);
|
response = await Actions.getActivity(requestObject);
|
||||||
}
|
}
|
||||||
|
console.log("finished fetching");
|
||||||
if (Events.hasError(response)) {
|
if (Events.hasError(response)) {
|
||||||
this.setState({ loading: "failed" });
|
this.setState({ loading: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let newItems = response.activity || response.explore;
|
let newItems = response.data;
|
||||||
|
|
||||||
if (update) {
|
if (update) {
|
||||||
activity.unshift(...newItems);
|
activity.unshift(...newItems);
|
||||||
@ -297,21 +291,26 @@ export default class SceneActivity extends React.Component {
|
|||||||
activity.push(...newItems);
|
activity.push(...newItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.tab === 0) {
|
if (this.props.viewer) {
|
||||||
this.props.onUpdateViewer({ activity: activity });
|
if (!isExplore) {
|
||||||
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { activity: activity } });
|
||||||
|
} else {
|
||||||
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { explore: activity } });
|
||||||
|
}
|
||||||
|
this.setState({ loading: false });
|
||||||
} else {
|
} else {
|
||||||
this.props.onUpdateViewer({ explore: activity });
|
this.setState({ explore: activity, loading: false });
|
||||||
}
|
}
|
||||||
this.setState({ loading: false });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
formatActivity = (userActivity) => {
|
formatActivity = (userActivity) => {
|
||||||
let activity = [];
|
let activity = [];
|
||||||
for (let item of userActivity) {
|
for (let item of userActivity) {
|
||||||
if (item.slate && !item.slate.isPublic) {
|
// if (item.slate && !item.slate.isPublic) {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
if (item.type === "CREATE_SLATE_OBJECT" && item.slate && item.file) {
|
if (item.type === "CREATE_SLATE_OBJECT") {
|
||||||
|
//&& item.slate && item.file
|
||||||
activity.push(item);
|
activity.push(item);
|
||||||
} else if (item.type === "CREATE_SLATE" && item.slate) {
|
} else if (item.type === "CREATE_SLATE" && item.slate) {
|
||||||
activity.push(item);
|
activity.push(item);
|
||||||
@ -345,14 +344,6 @@ export default class SceneActivity extends React.Component {
|
|||||||
// return activity;
|
// return activity;
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleCreateSlate = () => {
|
|
||||||
this.props.onAction({
|
|
||||||
type: "NAVIGATE",
|
|
||||||
value: "NAV_SLATES",
|
|
||||||
data: null,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
calculateWidth = () => {
|
calculateWidth = () => {
|
||||||
let windowWidth = window.innerWidth;
|
let windowWidth = window.innerWidth;
|
||||||
let imageSize;
|
let imageSize;
|
||||||
@ -370,83 +361,92 @@ export default class SceneActivity extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let activity =
|
let tab = this.props.page.params?.tab;
|
||||||
this.props.tab === 0 ? this.props.viewer.activity || [] : this.props.viewer.explore || [];
|
if (!tab) {
|
||||||
|
if (this.props.viewer) {
|
||||||
|
tab = "activity";
|
||||||
|
} else {
|
||||||
|
tab = "explore";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let activity;
|
||||||
|
console.log(this.props.viewer);
|
||||||
|
if (this.props.viewer) {
|
||||||
|
activity =
|
||||||
|
tab === "activity" ? this.props.viewer?.activity || [] : this.props.viewer?.explore || [];
|
||||||
|
} else {
|
||||||
|
activity = this.state.explore || [];
|
||||||
|
}
|
||||||
|
console.log(activity);
|
||||||
let items = activity
|
let items = activity
|
||||||
.filter((item) => item.type === "CREATE_SLATE_OBJECT")
|
.filter((item) => item.type === "CREATE_SLATE_OBJECT")
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
return {
|
return {
|
||||||
...item.file,
|
...item.file,
|
||||||
slate: item.slate,
|
slateId: item.slateId,
|
||||||
owner: item.owner?.username,
|
// slate: item.slate,
|
||||||
|
// owner: item.owner?.username,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScenePage>
|
<ScenePage>
|
||||||
<ScenePageHeader
|
{this.props.viewer && (
|
||||||
title={
|
<SecondaryTabGroup
|
||||||
this.props.isMobile ? (
|
tabs={[
|
||||||
<TabGroup
|
{ title: "My network", value: { tab: "activity" } },
|
||||||
tabs={[
|
{ title: "Explore", value: { tab: "explore" } },
|
||||||
{ title: "Files", value: "NAV_DATA" },
|
]}
|
||||||
{ title: "Collections", value: "NAV_SLATES" },
|
value={tab}
|
||||||
{ title: "Activity", value: "NAV_ACTIVITY" },
|
onAction={this.props.onAction}
|
||||||
]}
|
style={{ marginTop: 0 }}
|
||||||
value={2}
|
/>
|
||||||
onAction={this.props.onAction}
|
)}
|
||||||
onChange={(value) => this.setState({ tab: value })}
|
|
||||||
style={{ marginTop: 0, marginBottom: 32 }}
|
|
||||||
itemStyle={{ margin: "0px 12px" }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<PrimaryTabGroup
|
|
||||||
tabs={[
|
|
||||||
{ title: "Files", value: "NAV_DATA" },
|
|
||||||
{ title: "Collections", value: "NAV_SLATES" },
|
|
||||||
{ title: "Activity", value: "NAV_ACTIVITY" },
|
|
||||||
]}
|
|
||||||
value={2}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
actions={
|
|
||||||
<SecondaryTabGroup
|
|
||||||
tabs={[
|
|
||||||
{ title: "My network", value: "NAV_ACTIVITY" },
|
|
||||||
{ title: "Explore", value: "NAV_EXPLORE" },
|
|
||||||
]}
|
|
||||||
value={this.props.tab}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
style={{ margin: 0 }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<GlobalCarousel
|
<GlobalCarousel
|
||||||
carouselType="ACTIVITY"
|
carouselType="ACTIVITY"
|
||||||
viewer={this.props.viewer}
|
viewer={this.props.viewer}
|
||||||
objects={items}
|
objects={items}
|
||||||
onAction={this.props.onAction}
|
onAction={this.props.onAction}
|
||||||
|
index={this.state.carouselIndex}
|
||||||
|
onChange={(index) => {
|
||||||
|
if (index >= items.length - 4) {
|
||||||
|
this.fetchActivityItems();
|
||||||
|
}
|
||||||
|
this.setState({ carouselIndex: index });
|
||||||
|
}}
|
||||||
isMobile={this.props.isMobile}
|
isMobile={this.props.isMobile}
|
||||||
|
// params={this.props.page.params}
|
||||||
isOwner={false}
|
isOwner={false}
|
||||||
/>
|
/>
|
||||||
{activity.length ? (
|
{activity.length ? (
|
||||||
<div>
|
<div>
|
||||||
<div css={STYLES_ACTIVITY_GRID}>
|
<div css={STYLES_ACTIVITY_GRID}>
|
||||||
{activity.map((item, index) => {
|
{activity.map((item, i) => {
|
||||||
if (item.type === "CREATE_SLATE") {
|
if (item.type === "CREATE_SLATE") {
|
||||||
return (
|
return (
|
||||||
<span
|
<Link
|
||||||
|
redirect
|
||||||
|
key={i}
|
||||||
|
disabled={this.props.isMobile ? false : true}
|
||||||
|
// params={
|
||||||
|
// this.props.isMobile
|
||||||
|
// ? null
|
||||||
|
// : { ...this.props.page.params, cid: item.file.cid }
|
||||||
|
// }
|
||||||
|
href={`/$/slate/${item.slateId}`}
|
||||||
|
onAction={this.props.onAction}
|
||||||
|
onClick={() => this.setState({ carouselIndex: i })}
|
||||||
|
>
|
||||||
|
{/* <span
|
||||||
key={item.id}
|
key={item.id}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
this.props.onAction({
|
this.props.onAction({
|
||||||
type: "NAVIGATE",
|
type: "NAVIGATE",
|
||||||
value: "NAV_SLATE",
|
value: "NAV_SLATE",
|
||||||
data: { decorator: "SLATE", ...item.slate },
|
data: item.slate,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
> */}
|
||||||
<ActivityRectangle
|
<ActivityRectangle
|
||||||
width={
|
width={
|
||||||
this.props.isMobile ? this.state.imageSize : this.state.imageSize * 2 + 20
|
this.props.isMobile ? this.state.imageSize : this.state.imageSize * 2 + 20
|
||||||
@ -454,51 +454,40 @@ export default class SceneActivity extends React.Component {
|
|||||||
height={this.state.imageSize}
|
height={this.state.imageSize}
|
||||||
item={item}
|
item={item}
|
||||||
/>
|
/>
|
||||||
</span>
|
{/* </span> */}
|
||||||
|
</Link>
|
||||||
);
|
);
|
||||||
} else if (item.type === "CREATE_SLATE_OBJECT") {
|
} else if (item.type === "CREATE_SLATE_OBJECT") {
|
||||||
return (
|
return (
|
||||||
<span
|
<Link
|
||||||
key={item.id}
|
redirect
|
||||||
onClick={
|
key={i}
|
||||||
this.props.isMobile
|
disabled={this.props.isMobile ? false : true}
|
||||||
? () => {
|
// params={
|
||||||
this.props.onAction({
|
// this.props.isMobile
|
||||||
type: "NAVIGATE",
|
// ? null
|
||||||
value: "NAV_SLATE",
|
// : { ...this.props.page.params, cid: item.file.cid }
|
||||||
data: {
|
// }
|
||||||
decorator: "SLATE",
|
href={`/$/slate/${item.slateId}?cid=${item.file.cid}`}
|
||||||
...item.slate,
|
onAction={this.props.onAction}
|
||||||
},
|
onClick={() => this.setState({ carouselIndex: i })}
|
||||||
});
|
// onClick={
|
||||||
}
|
// this.props.isMobile
|
||||||
: () =>
|
// ? () => {}
|
||||||
Events.dispatchCustomEvent({
|
// : () =>
|
||||||
name: "slate-global-open-carousel",
|
// Events.dispatchCustomEvent({
|
||||||
detail: { index: this.getItemIndexById(items, item) },
|
// name: "slate-global-open-carousel",
|
||||||
})
|
// detail: { index: this.getItemIndexById(items, item) },
|
||||||
}
|
// })
|
||||||
|
// }
|
||||||
>
|
>
|
||||||
<ActivitySquare
|
<ActivitySquare
|
||||||
size={this.state.imageSize}
|
size={this.state.imageSize}
|
||||||
item={item}
|
item={item}
|
||||||
isMobile={this.props.isMobile}
|
isMobile={this.props.isMobile}
|
||||||
onClick={
|
onAction={this.props.onAction}
|
||||||
this.props.isMobile
|
|
||||||
? () => {}
|
|
||||||
: () => {
|
|
||||||
this.props.onAction({
|
|
||||||
type: "NAVIGATE",
|
|
||||||
value: "NAV_SLATE",
|
|
||||||
data: {
|
|
||||||
decorator: "SLATE",
|
|
||||||
...item.slate,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</Link>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -30,7 +30,6 @@ export default class SceneArchive extends React.Component {
|
|||||||
state = {
|
state = {
|
||||||
deals: [],
|
deals: [],
|
||||||
dealsLoaded: false,
|
dealsLoaded: false,
|
||||||
tab: 0,
|
|
||||||
networkViewer: null,
|
networkViewer: null,
|
||||||
allow_filecoin_directory_listing: this.props.viewer.data.settings
|
allow_filecoin_directory_listing: this.props.viewer.data.settings
|
||||||
?.allow_filecoin_directory_listing,
|
?.allow_filecoin_directory_listing,
|
||||||
@ -114,6 +113,7 @@ export default class SceneArchive extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let tab = this.props.page.params?.tab || "archive";
|
||||||
return (
|
return (
|
||||||
<ScenePage>
|
<ScenePage>
|
||||||
<ScenePageHeader title="Filecoin">
|
<ScenePageHeader title="Filecoin">
|
||||||
@ -122,14 +122,19 @@ export default class SceneArchive extends React.Component {
|
|||||||
</ScenePageHeader>
|
</ScenePageHeader>
|
||||||
|
|
||||||
<SecondaryTabGroup
|
<SecondaryTabGroup
|
||||||
tabs={["Archive Settings", "Wallet", "API", "Miners"]}
|
tabs={[
|
||||||
value={this.state.tab}
|
{ title: "Archive Settings", value: { tab: "archive" } },
|
||||||
onChange={(value) => this.setState({ tab: value })}
|
{ title: "Wallet", value: { tab: "wallet" } },
|
||||||
|
{ title: "API", value: { tab: "api" } },
|
||||||
|
{ title: "Miners", value: { tab: "miners" } },
|
||||||
|
]}
|
||||||
|
value={tab}
|
||||||
|
onAction={this.props.onAction}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{this.state.networkViewer ? (
|
{this.state.networkViewer ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{this.state.tab === 0 ? (
|
{tab === "archive" ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<ScenePageHeader>
|
<ScenePageHeader>
|
||||||
Use this section to archive all of your data on to Filecoin through a storage
|
Use this section to archive all of your data on to Filecoin through a storage
|
||||||
@ -201,7 +206,7 @@ export default class SceneArchive extends React.Component {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{this.state.tab === 1 ? (
|
{tab === "wallet" ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<SceneWallet {...this.props} networkViewer={this.state.networkViewer} />
|
<SceneWallet {...this.props} networkViewer={this.state.networkViewer} />
|
||||||
<br />
|
<br />
|
||||||
@ -216,7 +221,7 @@ export default class SceneArchive extends React.Component {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{this.state.tab === 2 ? (
|
{tab === "api" ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{this.state.routes ? (
|
{this.state.routes ? (
|
||||||
<SceneSentinel routes={this.state.routes} />
|
<SceneSentinel routes={this.state.routes} />
|
||||||
@ -228,7 +233,7 @@ export default class SceneArchive extends React.Component {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{this.state.tab === 3 ? (
|
{tab === "miners" ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{this.state.miners ? (
|
{this.state.miners ? (
|
||||||
<SceneMiners miners={this.state.miners} />
|
<SceneMiners miners={this.state.miners} />
|
||||||
|
@ -7,7 +7,7 @@ import { css } from "@emotion/react";
|
|||||||
import { SecondaryTabGroup } from "~/components/core/TabGroup";
|
import { SecondaryTabGroup } from "~/components/core/TabGroup";
|
||||||
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
import { Boundary } from "~/components/system/components/fragments/Boundary";
|
||||||
import { PopoverNavigation } from "~/components/system/components/PopoverNavigation";
|
import { PopoverNavigation } from "~/components/system/components/PopoverNavigation";
|
||||||
import { ButtonPrimary, ButtonSecondary } from "~/components/system/components/Buttons";
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
import ScenePage from "~/components/core/ScenePage";
|
import ScenePage from "~/components/core/ScenePage";
|
||||||
import ScenePageHeader from "~/components/core/ScenePageHeader";
|
import ScenePageHeader from "~/components/core/ScenePageHeader";
|
||||||
@ -211,10 +211,12 @@ export default class SceneDirectory extends React.Component {
|
|||||||
right: "0px",
|
right: "0px",
|
||||||
}}
|
}}
|
||||||
navigation={[
|
navigation={[
|
||||||
{
|
[
|
||||||
text: "Unfollow",
|
{
|
||||||
onClick: (e) => this._handleFollow(e, relation.id),
|
text: "Unfollow",
|
||||||
},
|
onClick: (e) => this._handleFollow(e, relation.id),
|
||||||
|
},
|
||||||
|
],
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Boundary>
|
</Boundary>
|
||||||
@ -222,20 +224,22 @@ export default class SceneDirectory extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<UserEntry
|
<Link href={`/$/user/${relation.id}`} onAction={this.props.onAction}>
|
||||||
key={relation.id}
|
<UserEntry
|
||||||
user={relation}
|
key={relation.id}
|
||||||
button={button}
|
user={relation}
|
||||||
checkStatus={this.checkStatus}
|
button={button}
|
||||||
onClick={() => {
|
checkStatus={this.checkStatus}
|
||||||
this.props.onAction({
|
// onClick={() => {
|
||||||
type: "NAVIGATE",
|
// this.props.onAction({
|
||||||
value: this.props.sceneId,
|
// type: "NAVIGATE",
|
||||||
scene: "PROFILE",
|
// value: "NAV_PROFILE",
|
||||||
data: relation,
|
// shallow: true,
|
||||||
});
|
// data: relation,
|
||||||
}}
|
// });
|
||||||
/>
|
// }}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -256,14 +260,16 @@ export default class SceneDirectory extends React.Component {
|
|||||||
right: "0px",
|
right: "0px",
|
||||||
}}
|
}}
|
||||||
navigation={[
|
navigation={[
|
||||||
{
|
[
|
||||||
text: this.props.viewer.following.some((user) => {
|
{
|
||||||
return user.id === relation.id;
|
text: this.props.viewer.following.some((user) => {
|
||||||
})
|
return user.id === relation.id;
|
||||||
? "Unfollow"
|
})
|
||||||
: "Follow",
|
? "Unfollow"
|
||||||
onClick: (e) => this._handleFollow(e, relation.id),
|
: "Follow",
|
||||||
},
|
onClick: (e) => this._handleFollow(e, relation.id),
|
||||||
|
},
|
||||||
|
],
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Boundary>
|
</Boundary>
|
||||||
@ -271,35 +277,38 @@ export default class SceneDirectory extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<UserEntry
|
<Link href={`/$/user/${relation.id}`} onAction={this.props.onAction}>
|
||||||
key={relation.id}
|
<UserEntry
|
||||||
user={relation}
|
key={relation.id}
|
||||||
button={button}
|
user={relation}
|
||||||
checkStatus={this.checkStatus}
|
button={button}
|
||||||
onClick={() => {
|
checkStatus={this.checkStatus}
|
||||||
this.props.onAction({
|
// onClick={() => {
|
||||||
type: "NAVIGATE",
|
// this.props.onAction({
|
||||||
value: this.props.sceneId,
|
// type: "NAVIGATE",
|
||||||
scene: "PROFILE",
|
// value: "NAV_PROFILE",
|
||||||
data: relation,
|
// shallow: true,
|
||||||
});
|
// data: relation,
|
||||||
}}
|
// });
|
||||||
/>
|
// }}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let tab = this.props.page.params?.tab || "following";
|
||||||
return (
|
return (
|
||||||
<ScenePage>
|
<ScenePage>
|
||||||
<ScenePageHeader title="Directory" />
|
<ScenePageHeader title="Directory" />
|
||||||
<SecondaryTabGroup
|
<SecondaryTabGroup
|
||||||
tabs={[
|
tabs={[
|
||||||
{ title: "Following", value: "NAV_DIRECTORY" },
|
{ title: "Following", value: { tab: "following" } },
|
||||||
{ title: "Followers", value: "NAV_DIRECTORY_FOLLOWERS" },
|
{ title: "Followers", value: { tab: "followers" } },
|
||||||
]}
|
]}
|
||||||
value={this.props.tab}
|
value={tab}
|
||||||
onAction={this.props.onAction}
|
onAction={this.props.onAction}
|
||||||
/>
|
/>
|
||||||
{this.props.tab === 0 ? (
|
{tab === "following" ? (
|
||||||
following && following.length ? (
|
following && following.length ? (
|
||||||
following
|
following
|
||||||
) : (
|
) : (
|
||||||
@ -310,7 +319,7 @@ export default class SceneDirectory extends React.Component {
|
|||||||
</EmptyState>
|
</EmptyState>
|
||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
{this.props.tab === 1 ? (
|
{tab === "followers" ? (
|
||||||
followers && followers.length ? (
|
followers && followers.length ? (
|
||||||
followers
|
followers
|
||||||
) : (
|
) : (
|
||||||
|
@ -56,7 +56,6 @@ export default class SceneEditAccount extends React.Component {
|
|||||||
changingAvatar: false,
|
changingAvatar: false,
|
||||||
savingNameBio: false,
|
savingNameBio: false,
|
||||||
changingFilecoin: false,
|
changingFilecoin: false,
|
||||||
tab: 0,
|
|
||||||
modalShow: false,
|
modalShow: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -107,7 +106,7 @@ export default class SceneEditAccount extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let data = { ...this.props.viewer.data, body: this.state.body, name: this.state.name };
|
let data = { ...this.props.viewer.data, body: this.state.body, name: this.state.name };
|
||||||
this.props.onUpdateViewer({ username: this.state.username, data });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { username: this.state.username, data } });
|
||||||
this.setState({ savingNameBio: true });
|
this.setState({ savingNameBio: true });
|
||||||
|
|
||||||
let response = await Actions.updateViewer({
|
let response = await Actions.updateViewer({
|
||||||
@ -160,12 +159,12 @@ export default class SceneEditAccount extends React.Component {
|
|||||||
}
|
}
|
||||||
this.setState({ deleting: true });
|
this.setState({ deleting: true });
|
||||||
this.setState({ modalShow: false });
|
this.setState({ modalShow: false });
|
||||||
|
|
||||||
await Window.delay(100);
|
await Window.delay(100);
|
||||||
|
|
||||||
await UserBehaviors.deleteMe({ viewer: this.props.viewer });
|
await UserBehaviors.deleteMe({ viewer: this.props.viewer });
|
||||||
|
window.location.replace("/_/auth");
|
||||||
this.setState({ deleting: false });
|
this.setState({ deleting: false });
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleChange = (e) => {
|
_handleChange = (e) => {
|
||||||
@ -173,16 +172,22 @@ export default class SceneEditAccount extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let tab = this.props.page.params?.tab || "profile";
|
||||||
return (
|
return (
|
||||||
<ScenePage>
|
<ScenePage>
|
||||||
<ScenePageHeader title="Settings" />
|
<ScenePageHeader title="Settings" />
|
||||||
<SecondaryTabGroup
|
<SecondaryTabGroup
|
||||||
tabs={["Profile", "Data Storage", "Security", "Account"]}
|
tabs={[
|
||||||
value={this.state.tab}
|
{ title: "Profile", value: { tab: "profile" } },
|
||||||
onChange={(value) => this.setState({ tab: value })}
|
{ title: "Data Storage", value: { tab: "storage" } },
|
||||||
|
{ title: "Security", value: { tab: "security" } },
|
||||||
|
{ title: "Account", value: { tab: "account" } },
|
||||||
|
]}
|
||||||
|
value={tab}
|
||||||
|
onAction={this.props.onAction}
|
||||||
style={{ marginBottom: 48 }}
|
style={{ marginBottom: 48 }}
|
||||||
/>
|
/>
|
||||||
{this.state.tab === 0 ? (
|
{tab === "profile" ? (
|
||||||
<div>
|
<div>
|
||||||
<div css={STYLES_HEADER}>Your Avatar</div>
|
<div css={STYLES_HEADER}>Your Avatar</div>
|
||||||
|
|
||||||
@ -227,7 +232,7 @@ export default class SceneEditAccount extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{this.state.tab === 1 ? (
|
{tab === "storage" ? (
|
||||||
<div style={{ maxWidth: 800 }}>
|
<div style={{ maxWidth: 800 }}>
|
||||||
<div css={STYLES_HEADER}>
|
<div css={STYLES_HEADER}>
|
||||||
Allow Slate to make Filecoin archive storage deals on your behalf
|
Allow Slate to make Filecoin archive storage deals on your behalf
|
||||||
@ -278,7 +283,7 @@ export default class SceneEditAccount extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{this.state.tab === 2 ? (
|
{tab === "security" ? (
|
||||||
<div>
|
<div>
|
||||||
<div css={STYLES_HEADER}>Change password</div>
|
<div css={STYLES_HEADER}>Change password</div>
|
||||||
<div>Passwords must be a minimum of eight characters.</div>
|
<div>Passwords must be a minimum of eight characters.</div>
|
||||||
@ -311,7 +316,7 @@ export default class SceneEditAccount extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{this.state.tab === 3 ? (
|
{tab === "account" ? (
|
||||||
<div>
|
<div>
|
||||||
<div css={STYLES_HEADER}>Change username</div>
|
<div css={STYLES_HEADER}>Change username</div>
|
||||||
<div style={{ maxWidth: 800 }}>
|
<div style={{ maxWidth: 800 }}>
|
||||||
@ -359,19 +364,18 @@ export default class SceneEditAccount extends React.Component {
|
|||||||
tabIndex="-1"
|
tabIndex="-1"
|
||||||
css={STYLES_COPY_INPUT}
|
css={STYLES_COPY_INPUT}
|
||||||
/>{" "}
|
/>{" "}
|
||||||
|
|
||||||
{this.state.modalShow && (
|
{this.state.modalShow && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
type={"DELETE"}
|
type={"DELETE"}
|
||||||
withValidation={true}
|
withValidation={true}
|
||||||
matchValue={this.state.username}
|
matchValue={this.state.username}
|
||||||
callback={this._handleDelete}
|
callback={this._handleDelete}
|
||||||
header={`Are you sure you want to delete your account @${this.state.username}?`}
|
header={`Are you sure you want to delete your account @${this.state.username}?`}
|
||||||
subHeader={`You will lose all your files and collections. You can’t undo this action.`}
|
subHeader={`You will lose all your files and collections. You can’t undo this action.`}
|
||||||
inputHeader={`Please type your username to confirm`}
|
inputHeader={`Please type your username to confirm`}
|
||||||
inputPlaceholder={`username`}
|
inputPlaceholder={`username`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ScenePage>
|
</ScenePage>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
139
scenes/SceneError.js
Normal file
139
scenes/SceneError.js
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { css } from "@emotion/react";
|
||||||
|
|
||||||
|
import ScenePage from "~/components/core/ScenePage";
|
||||||
|
import ScenePageHeader from "~/components/core/ScenePageHeader";
|
||||||
|
|
||||||
|
const STYLES_ROOT = css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 100vh;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// TODO(jim): Brand system colors.
|
||||||
|
const STYLES_GLITCH = css`
|
||||||
|
font-size: 120px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
content: "404";
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
::before {
|
||||||
|
text-shadow: 2px 0 #00ffea;
|
||||||
|
animation: slate-client-animation-glitch 3s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
::after {
|
||||||
|
text-shadow: -2px 0 #fe3a7f;
|
||||||
|
animation: slate-client-animation-glitch 2s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slate-client-animation-glitch {
|
||||||
|
0% {
|
||||||
|
clip: rect(64px, 9999px, 66px, 0);
|
||||||
|
}
|
||||||
|
5% {
|
||||||
|
clip: rect(30px, 9999px, 36px, 0);
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
clip: rect(80px, 9999px, 71px, 0);
|
||||||
|
}
|
||||||
|
15% {
|
||||||
|
clip: rect(65px, 9999px, 64px, 0);
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
clip: rect(88px, 9999px, 40px, 0);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
clip: rect(17px, 9999px, 79px, 0);
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
clip: rect(24px, 9999px, 26px, 0);
|
||||||
|
}
|
||||||
|
35% {
|
||||||
|
clip: rect(88px, 9999px, 26px, 0);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
clip: rect(88px, 9999px, 80px, 0);
|
||||||
|
}
|
||||||
|
45% {
|
||||||
|
clip: rect(28px, 9999px, 51px, 0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
clip: rect(23px, 9999px, 40px, 0);
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
clip: rect(16px, 9999px, 86px, 0);
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
clip: rect(23px, 9999px, 94px, 0);
|
||||||
|
}
|
||||||
|
65% {
|
||||||
|
clip: rect(82px, 9999px, 39px, 0);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
clip: rect(37px, 9999px, 92px, 0);
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
clip: rect(71px, 9999px, 52px, 0);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
clip: rect(28px, 9999px, 74px, 0);
|
||||||
|
}
|
||||||
|
85% {
|
||||||
|
clip: rect(67px, 9999px, 96px, 0);
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
clip: rect(40px, 9999px, 88px, 0);
|
||||||
|
}
|
||||||
|
95% {
|
||||||
|
clip: rect(99px, 9999px, 61px, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
clip: rect(76px, 9999px, 77px, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const STYLES_MIDDLE = css`
|
||||||
|
min-height: 10%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 24px;
|
||||||
|
padding-top: 20vh;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default class SceneError extends React.Component {
|
||||||
|
render() {
|
||||||
|
const title = `404`;
|
||||||
|
const description = "The page you are looking for does not exist";
|
||||||
|
const url = "https://slate.host/404";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScenePage>
|
||||||
|
<div css={STYLES_MIDDLE}>
|
||||||
|
<h1 css={STYLES_GLITCH}>404</h1>
|
||||||
|
<h2 style={{ textAlign: "center" }}>The page you are looking for does not exist</h2>
|
||||||
|
</div>
|
||||||
|
</ScenePage>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -113,7 +113,6 @@ const STYLES_COMMAND_TOOLTIP_ANCHOR = css`
|
|||||||
|
|
||||||
export default class SceneFilesFolder extends React.Component {
|
export default class SceneFilesFolder extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
view: 0,
|
|
||||||
filterTooltip: false,
|
filterTooltip: false,
|
||||||
fileTypes: {
|
fileTypes: {
|
||||||
image: false,
|
image: false,
|
||||||
@ -128,9 +127,9 @@ export default class SceneFilesFolder extends React.Component {
|
|||||||
keyboardTooltip: false,
|
keyboardTooltip: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount = () => {
|
// componentDidMount = () => {
|
||||||
this.openCarouselToItem();
|
// this.openCarouselToItem();
|
||||||
};
|
// };
|
||||||
|
|
||||||
componentDidUpdate = (prevProps, prevState) => {
|
componentDidUpdate = (prevProps, prevState) => {
|
||||||
if (prevProps.viewer.library !== this.props.viewer.library) {
|
if (prevProps.viewer.library !== this.props.viewer.library) {
|
||||||
@ -138,9 +137,9 @@ export default class SceneFilesFolder extends React.Component {
|
|||||||
this._filterFiles();
|
this._filterFiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (prevProps.page !== this.props.page) {
|
// if (prevProps.page.params !== this.props.page.params) {
|
||||||
this.openCarouselToItem();
|
// this.openCarouselToItem();
|
||||||
}
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleFilterTooltip = () => {
|
_handleFilterTooltip = () => {
|
||||||
@ -206,87 +205,50 @@ export default class SceneFilesFolder extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
openCarouselToItem = () => {
|
// openCarouselToItem = () => {
|
||||||
let index = -1;
|
// if (!this.props.page?.params || !this.props.viewer.library?.length) {
|
||||||
let page = this.props.page;
|
// return;
|
||||||
if (page?.fileId || page?.cid || page?.index) {
|
// }
|
||||||
if (page?.index) {
|
// let index = -1;
|
||||||
index = page.index;
|
// let params = this.props.page.params;
|
||||||
} else {
|
// if (params?.fileId || params?.cid || params?.index) {
|
||||||
let library = this.props.viewer.library || [];
|
// if (params?.index) {
|
||||||
for (let i = 0; i < library.length; i++) {
|
// index = params.index;
|
||||||
let obj = library[i];
|
// } else {
|
||||||
if ((obj.cid && obj.cid === page?.cid) || (obj.id && obj.id === page?.fileId)) {
|
// let library = this.props.viewer.library || [];
|
||||||
index = i;
|
// for (let i = 0; i < library.length; i++) {
|
||||||
break;
|
// let obj = library[i];
|
||||||
}
|
// if ((obj.cid && obj.cid === params?.cid) || (obj.id && obj.id === params?.fileId)) {
|
||||||
}
|
// index = i;
|
||||||
}
|
// break;
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (index !== -1) {
|
// if (index !== -1) {
|
||||||
Events.dispatchCustomEvent({
|
// Events.dispatchCustomEvent({
|
||||||
name: "slate-global-open-carousel",
|
// name: "slate-global-open-carousel",
|
||||||
detail: { index },
|
// detail: { index },
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let files = this.state.filtersActive ? this.state.filteredFiles : this.props.viewer?.library;
|
let files = this.state.filtersActive ? this.state.filteredFiles : this.props.viewer?.library;
|
||||||
files = files || [];
|
files = files || [];
|
||||||
|
const tab = this.props.page.params?.tab || "grid";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScenePage>
|
<ScenePage>
|
||||||
<ScenePageHeader
|
|
||||||
title={
|
|
||||||
this.props.isMobile ? (
|
|
||||||
<TabGroup
|
|
||||||
tabs={[
|
|
||||||
{ title: "Files", value: "NAV_DATA" },
|
|
||||||
{ title: "Collections", value: "NAV_SLATES" },
|
|
||||||
{ title: "Activity", value: "NAV_ACTIVITY" },
|
|
||||||
]}
|
|
||||||
value={0}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
onChange={(value) => this.setState({ tab: value })}
|
|
||||||
style={{ marginTop: 0, marginBottom: 32 }}
|
|
||||||
itemStyle={{ margin: "0px 12px" }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<PrimaryTabGroup
|
|
||||||
tabs={[
|
|
||||||
{ title: "Files", value: "NAV_DATA" },
|
|
||||||
{ title: "Collections", value: "NAV_SLATES" },
|
|
||||||
{ title: "Activity", value: "NAV_ACTIVITY" },
|
|
||||||
]}
|
|
||||||
value={0}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
actions={
|
|
||||||
this.props.isMobile ? null : (
|
|
||||||
<SecondaryTabGroup
|
|
||||||
tabs={[
|
|
||||||
<SVG.GridView height="24px" style={{ display: "block" }} />,
|
|
||||||
<SVG.TableView height="24px" style={{ display: "block" }} />,
|
|
||||||
]}
|
|
||||||
value={this.state.view}
|
|
||||||
onChange={(value) => this.setState({ view: value })}
|
|
||||||
style={{ margin: "0 0 24px 0" }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<GlobalCarousel
|
<GlobalCarousel
|
||||||
carouselType="DATA"
|
carouselType="DATA"
|
||||||
onUpdateViewer={this.props.onUpdateViewer}
|
|
||||||
resources={this.props.resources}
|
resources={this.props.resources}
|
||||||
viewer={this.props.viewer}
|
viewer={this.props.viewer}
|
||||||
objects={files}
|
objects={files}
|
||||||
onAction={this.props.onAction}
|
onAction={this.props.onAction}
|
||||||
isMobile={this.props.isMobile}
|
isMobile={this.props.isMobile}
|
||||||
|
params={this.props.page.params}
|
||||||
isOwner={true}
|
isOwner={true}
|
||||||
/>
|
/>
|
||||||
<DataMeter
|
<DataMeter
|
||||||
@ -307,6 +269,21 @@ export default class SceneFilesFolder extends React.Component {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<div css={STYLES_CONTAINER_WRAPPER}>
|
<div css={STYLES_CONTAINER_WRAPPER}>
|
||||||
|
<SecondaryTabGroup
|
||||||
|
tabs={[
|
||||||
|
{
|
||||||
|
title: <SVG.GridView height="24px" style={{ display: "block" }} />,
|
||||||
|
value: { tab: "grid" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: <SVG.TableView height="24px" style={{ display: "block" }} />,
|
||||||
|
value: { tab: "table" },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
value={tab}
|
||||||
|
onAction={this.props.onAction}
|
||||||
|
style={{ margin: "0 0 24px 0" }}
|
||||||
|
/>
|
||||||
<div css={STYLES_CONTAINER}>
|
<div css={STYLES_CONTAINER}>
|
||||||
<div
|
<div
|
||||||
css={STYLES_BUTTONS_ROW}
|
css={STYLES_BUTTONS_ROW}
|
||||||
@ -486,10 +463,10 @@ export default class SceneFilesFolder extends React.Component {
|
|||||||
onAction={this.props.onAction}
|
onAction={this.props.onAction}
|
||||||
viewer={this.props.viewer}
|
viewer={this.props.viewer}
|
||||||
items={files}
|
items={files}
|
||||||
onUpdateViewer={this.props.onUpdateViewer}
|
view={tab}
|
||||||
view={this.state.view}
|
|
||||||
resources={this.props.resources}
|
resources={this.props.resources}
|
||||||
isOwner={true}
|
isOwner={true}
|
||||||
|
page={this.props.page}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<EmptyState>
|
<EmptyState>
|
||||||
|
@ -13,6 +13,7 @@ import { css } from "@emotion/react";
|
|||||||
import { createState } from "~/scenes/SceneSettings";
|
import { createState } from "~/scenes/SceneSettings";
|
||||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||||
import { FilecoinNumber } from "@glif/filecoin-number";
|
import { FilecoinNumber } from "@glif/filecoin-number";
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
|
|
||||||
import Section from "~/components/core/Section";
|
import Section from "~/components/core/Section";
|
||||||
import ScenePage from "~/components/core/ScenePage";
|
import ScenePage from "~/components/core/ScenePage";
|
||||||
@ -206,7 +207,7 @@ export default class SceneMakeFilecoinDeal extends React.Component {
|
|||||||
"Your storage deal was put in the queue. This can take up to 36 hours, check back later."
|
"Your storage deal was put in the queue. This can take up to 36 hours, check back later."
|
||||||
);
|
);
|
||||||
|
|
||||||
this.props.onAction({ type: "NAVIGATE", value: "NAV_FILECOIN" });
|
this.props.onAction({ type: "NAVIGATE", href: "/_/filecoin" });
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleRemove = async (cid) => {
|
_handleRemove = async (cid) => {
|
||||||
|
@ -22,106 +22,115 @@ const STYLES_LOADER = css`
|
|||||||
|
|
||||||
export default class SceneProfile extends React.Component {
|
export default class SceneProfile extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
profile: null,
|
|
||||||
notFound: false,
|
notFound: false,
|
||||||
isOwner: false,
|
loading: false,
|
||||||
loading: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount = async () => {
|
// componentDidMount = async () => {
|
||||||
this.fetchProfile();
|
// if (this.props.data) {
|
||||||
};
|
// this.openCarouselToItem();
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
componentDidUpdate = (prevProps) => {
|
// componentDidUpdate = (prevProps) => {
|
||||||
if (this.state.isOwner && this.props.viewer.library !== prevProps.viewer.library) {
|
// // if (
|
||||||
let filteredViewer = this.getFilteredViewer();
|
// // this.state.isOwner &&
|
||||||
this.setState({ profile: filteredViewer });
|
// // this.props.viewer &&
|
||||||
} else if (this.props.page !== prevProps.page) {
|
// // this.props.viewer.library !== prevProps.viewer.library
|
||||||
this.openCarouselToItem();
|
// // ) {
|
||||||
}
|
// // let filteredViewer = this.getFilteredViewer();
|
||||||
};
|
// // this.setState({ profile: filteredViewer });
|
||||||
|
// // } else
|
||||||
|
// if (this.props.data !== prevProps.data || this.props.page.params !== prevProps.page.params) {
|
||||||
|
// this.openCarouselToItem();
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
fetchProfile = async () => {
|
// fetchProfile = async () => {
|
||||||
const username = this.props.page.user || this.props.page.data?.username;
|
// const username = this.props.page.username || this.props.page.data?.username;
|
||||||
let isOwner = false;
|
// const { userId } = this.props.page;
|
||||||
let query;
|
|
||||||
let targetUser;
|
|
||||||
if (username) {
|
|
||||||
if (username === this.props.viewer.username) {
|
|
||||||
isOwner = true;
|
|
||||||
targetUser = this.getFilteredViewer();
|
|
||||||
} else {
|
|
||||||
query = { username: username };
|
|
||||||
}
|
|
||||||
} else if (this.props.data?.id) {
|
|
||||||
if (this.props.data.id === this.props.viewer.id) {
|
|
||||||
isOwner = true;
|
|
||||||
targetUser = this.getFilteredViewer();
|
|
||||||
} else {
|
|
||||||
query = { id: this.props.data.id };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetUser) {
|
// const id = userId || this.props.data?.id;
|
||||||
let response;
|
|
||||||
if (query) {
|
|
||||||
response = await Actions.getSerializedProfile(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response || response.error) {
|
// let isOwner = false;
|
||||||
this.setState({ notFound: true });
|
// let query;
|
||||||
return;
|
// let targetUser;
|
||||||
}
|
// if (username) {
|
||||||
|
// if (this.props.viewer && username === this.props.viewer.username) {
|
||||||
|
// isOwner = true;
|
||||||
|
// targetUser = this.getFilteredViewer();
|
||||||
|
// } else {
|
||||||
|
// query = { username };
|
||||||
|
// }
|
||||||
|
// } else if (id) {
|
||||||
|
// if (this.props.viewer && id === this.props.viewer.id) {
|
||||||
|
// isOwner = true;
|
||||||
|
// targetUser = this.getFilteredViewer();
|
||||||
|
// } else {
|
||||||
|
// query = { id };
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
targetUser = response.data;
|
// if (!targetUser) {
|
||||||
}
|
// let response;
|
||||||
window.history.replaceState(
|
// if (query) {
|
||||||
{ ...window.history.state, data: targetUser },
|
// response = await Actions.getSerializedProfile(query);
|
||||||
"A slate user",
|
// }
|
||||||
`/${targetUser.username}`
|
|
||||||
);
|
|
||||||
this.props.onUpdateData(targetUser);
|
|
||||||
this.setState({ isOwner, profile: targetUser, loading: false }, this.openCarouselToItem);
|
|
||||||
};
|
|
||||||
|
|
||||||
openCarouselToItem = () => {
|
// if (!response || response.error) {
|
||||||
if (!this.state.profile?.library?.length) {
|
// this.setState({ notFound: true });
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
const { cid, fileId, index } = this.props.page;
|
|
||||||
|
|
||||||
if (Strings.isEmpty(cid) && Strings.isEmpty(fileId) && typeof index === "undefined") {
|
// targetUser = response.data;
|
||||||
return;
|
// }
|
||||||
}
|
// window.history.replaceState(
|
||||||
|
// { ...window.history.state, data: targetUser },
|
||||||
|
// "A slate user",
|
||||||
|
// `/${targetUser.username}`
|
||||||
|
// );
|
||||||
|
// this.props.onUpdateData(targetUser);
|
||||||
|
// this.setState({ isOwner, profile: targetUser, loading: false }, this.openCarouselToItem);
|
||||||
|
// };
|
||||||
|
|
||||||
const library = this.state.profile.library;
|
// openCarouselToItem = () => {
|
||||||
|
// if (!this.props.data?.library?.length || !this.props.page?.params) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// const { cid, fileId, index } = this.props.page.params;
|
||||||
|
|
||||||
let foundIndex = -1;
|
// if (Strings.isEmpty(cid) && Strings.isEmpty(fileId) && typeof index === "undefined") {
|
||||||
if (index) {
|
// return;
|
||||||
foundIndex = index;
|
// }
|
||||||
} else if (cid) {
|
|
||||||
foundIndex = library.findIndex((object) => object.cid === cid);
|
// const library = this.props.data.library;
|
||||||
} else if (fileId) {
|
|
||||||
foundIndex = library.findIndex((object) => object.id === fileId);
|
// let foundIndex = -1;
|
||||||
}
|
// if (index) {
|
||||||
if (typeof foundIndex !== "undefined" && foundIndex !== -1) {
|
// foundIndex = index;
|
||||||
Events.dispatchCustomEvent({
|
// } else if (cid) {
|
||||||
name: "slate-global-open-carousel",
|
// foundIndex = library.findIndex((object) => object.cid === cid);
|
||||||
detail: { index: foundIndex },
|
// } else if (fileId) {
|
||||||
});
|
// foundIndex = library.findIndex((object) => object.id === fileId);
|
||||||
}
|
// }
|
||||||
// else {
|
// if (typeof foundIndex !== "undefined" && foundIndex !== -1) {
|
||||||
// Events.dispatchCustomEvent({
|
// Events.dispatchCustomEvent({
|
||||||
// name: "create-alert",
|
// name: "slate-global-open-carousel",
|
||||||
// detail: {
|
// detail: { index: foundIndex },
|
||||||
// alert: {
|
// });
|
||||||
// message:
|
// }
|
||||||
// "The requested file could not be found. It could have been deleted or may be private",
|
// // else {
|
||||||
// },
|
// // Events.dispatchCustomEvent({
|
||||||
// },
|
// // name: "create-alert",
|
||||||
// });
|
// // detail: {
|
||||||
// }
|
// // alert: {
|
||||||
};
|
// // message:
|
||||||
|
// // "The requested file could not be found. It could have been deleted or may be private",
|
||||||
|
// // },
|
||||||
|
// // },
|
||||||
|
// // });
|
||||||
|
// // }
|
||||||
|
// };
|
||||||
|
|
||||||
getFilteredViewer = () => {
|
getFilteredViewer = () => {
|
||||||
let viewer = this.props.viewer;
|
let viewer = this.props.viewer;
|
||||||
@ -130,36 +139,51 @@ export default class SceneProfile extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.notFound) {
|
let user = this.props.data;
|
||||||
return (
|
if (!user) {
|
||||||
<ScenePage>
|
|
||||||
<EmptyState>
|
|
||||||
<SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
|
||||||
<div>We were unable to locate that user profile</div>
|
|
||||||
</EmptyState>
|
|
||||||
</ScenePage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.loading) {
|
|
||||||
return (
|
return (
|
||||||
<ScenePage>
|
<ScenePage>
|
||||||
<div css={STYLES_LOADER}>
|
<div css={STYLES_LOADER}>
|
||||||
<LoaderSpinner />
|
<LoaderSpinner />
|
||||||
</div>
|
</div>
|
||||||
</ScenePage>
|
</ScenePage>
|
||||||
|
// <ScenePage>
|
||||||
|
// <EmptyState>
|
||||||
|
// <SVG.Users height="24px" style={{ marginBottom: 24 }} />
|
||||||
|
// <div>We were unable to locate that user profile</div>
|
||||||
|
// </EmptyState>
|
||||||
|
// </ScenePage>
|
||||||
);
|
);
|
||||||
} else if (this.state.profile?.id) {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Profile
|
<Profile
|
||||||
{...this.props}
|
{...this.props}
|
||||||
user={this.state.profile}
|
user={user}
|
||||||
isOwner={this.state.isOwner}
|
isOwner={this.props.viewer ? user.id === this.props.viewer.id : false}
|
||||||
isAuthenticated={this.props.viewer !== null}
|
isAuthenticated={this.props.viewer !== null}
|
||||||
key={this.state.profile.id}
|
key={user.id}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
// if (this.state.loading) {
|
||||||
|
// return (
|
||||||
|
// <ScenePage>
|
||||||
|
// <div css={STYLES_LOADER}>
|
||||||
|
// <LoaderSpinner />
|
||||||
|
// </div>
|
||||||
|
// </ScenePage>
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// return (
|
||||||
|
// <Profile
|
||||||
|
// {...this.props}
|
||||||
|
// user={user}
|
||||||
|
// isOwner={this.state.isOwner}
|
||||||
|
// isAuthenticated={this.props.viewer !== null}
|
||||||
|
// key={user.id}
|
||||||
|
// />
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,15 +95,14 @@ class Key extends React.Component {
|
|||||||
</SquareButtonGray>
|
</SquareButtonGray>
|
||||||
|
|
||||||
{this.state.modalShow && (
|
{this.state.modalShow && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
type={"DELETE"}
|
type={"DELETE"}
|
||||||
withValidation={false}
|
withValidation={false}
|
||||||
callback={(e) => this._handleDelete(e, this.props.data.id)}
|
callback={(e) => this._handleDelete(e, this.props.data.id)}
|
||||||
header={`Are you sure you want to revoke this API key?`}
|
header={`Are you sure you want to revoke this API key?`}
|
||||||
subHeader={`Any services using it will no longer be able to access your Slate account.`}
|
subHeader={`Any services using it will no longer be able to access your Slate account.`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -117,7 +116,6 @@ export default class SceneSettingsDeveloper extends React.Component {
|
|||||||
language: "javascript",
|
language: "javascript",
|
||||||
docs: "GET",
|
docs: "GET",
|
||||||
copying: false,
|
copying: false,
|
||||||
tab: 0,
|
|
||||||
modalShow: false,
|
modalShow: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -154,6 +152,7 @@ export default class SceneSettingsDeveloper extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const tab = this.props.page.params?.tab || "v2";
|
||||||
let APIKey = "YOUR-API-KEY-HERE";
|
let APIKey = "YOUR-API-KEY-HERE";
|
||||||
let lang = this.state.language;
|
let lang = this.state.language;
|
||||||
if (this.props.viewer.keys) {
|
if (this.props.viewer.keys) {
|
||||||
@ -298,12 +297,15 @@ export default class SceneSettingsDeveloper extends React.Component {
|
|||||||
</ScenePageHeader>
|
</ScenePageHeader>
|
||||||
|
|
||||||
<SecondaryTabGroup
|
<SecondaryTabGroup
|
||||||
tabs={["Version 2.0", "Version 1.0"]}
|
tabs={[
|
||||||
value={this.state.tab}
|
{ title: "Version 2.0", value: { tab: "v2" } },
|
||||||
onChange={(tab) => this.setState({ tab })}
|
{ title: "Version 1.0", value: { tab: "v1" } },
|
||||||
|
]}
|
||||||
|
value={tab}
|
||||||
|
onAction={this.props.onAction}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{this.state.tab === 0 ? (
|
{tab === "v2" ? (
|
||||||
<>
|
<>
|
||||||
<APIDocsGetV2
|
<APIDocsGetV2
|
||||||
language={lang}
|
language={lang}
|
||||||
|
@ -104,24 +104,20 @@ export default class SceneSignIn extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = null;
|
|
||||||
|
|
||||||
if (this.state.scene === "CREATE_ACCOUNT") {
|
if (this.state.scene === "CREATE_ACCOUNT") {
|
||||||
response = await this.props.onCreateUser({
|
await this.props.onCreateUser({
|
||||||
username: this.state.username.toLowerCase(),
|
username: this.state.username.toLowerCase(),
|
||||||
password: this.state.password,
|
password: this.state.password,
|
||||||
accepted: this.state.accepted,
|
accepted: this.state.accepted,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
response = await this.props.onAuthenticate({
|
await this.props.onAuthenticate({
|
||||||
username: this.state.username.toLowerCase(),
|
username: this.state.username.toLowerCase(),
|
||||||
password: this.state.password,
|
password: this.state.password,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Events.hasError(response)) {
|
this.setState({ loading: false });
|
||||||
this.setState({ loading: false });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleCheckUsername = async () => {
|
_handleCheckUsername = async () => {
|
||||||
@ -160,7 +156,8 @@ export default class SceneSignIn extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div css={STYLES_ROOT}>
|
<div css={STYLES_ROOT}>
|
||||||
<WebsitePrototypeHeader style={{ background: `none` }} />
|
<div />
|
||||||
|
{/* <WebsitePrototypeHeader style={{ background: `none` }} /> */}
|
||||||
<div css={STYLES_MIDDLE}>
|
<div css={STYLES_MIDDLE}>
|
||||||
<SignIn {...this.props} />
|
<SignIn {...this.props} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,6 +8,7 @@ import * as Strings from "~/common/strings";
|
|||||||
import * as UserBehaviors from "~/common/user-behaviors";
|
import * as UserBehaviors from "~/common/user-behaviors";
|
||||||
import * as Events from "~/common/custom-events";
|
import * as Events from "~/common/custom-events";
|
||||||
|
|
||||||
|
import { Link } from "~/components/core/Link";
|
||||||
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
import { LoaderSpinner } from "~/components/system/components/Loaders";
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import { SlateLayout } from "~/components/core/SlateLayout";
|
import { SlateLayout } from "~/components/core/SlateLayout";
|
||||||
@ -63,145 +64,154 @@ export default class SceneSlate extends React.Component {
|
|||||||
accessDenied: false,
|
accessDenied: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount = async () => {
|
// componentDidMount = async () => {
|
||||||
await this.fetchSlate();
|
// if (this.props.data) {
|
||||||
};
|
// this.openCarouselToItem();
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
componentDidUpdate = async (prevProps) => {
|
// componentDidUpdate = async (prevProps) => {
|
||||||
if (!this.props.data?.objects && !this.state.notFound) {
|
// // if (!this.props.data?.objects && !this.state.notFound) {
|
||||||
await this.fetchSlate();
|
// // await this.fetchSlate();
|
||||||
} else if (this.props.page !== prevProps.page) {
|
// // } else
|
||||||
this.openCarouselToItem();
|
// if (this.props.data !== prevProps.data || this.props.page.params !== prevProps.page.params) {
|
||||||
}
|
// this.openCarouselToItem();
|
||||||
};
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
fetchSlate = async () => {
|
// fetchSlate = async () => {
|
||||||
const { user: username, slate: slatename } = this.props.page;
|
// const { username, slatename, slateId } = this.props.page;
|
||||||
|
|
||||||
if (!this.props.data && (!username || !slatename)) {
|
// if (!this.props.data && (!username || !slatename)) {
|
||||||
this.setState({ notFound: true });
|
// this.setState({ notFound: true });
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
//NOTE(martina): look for the slate in the user's slates
|
// let id = slateId || this.props.data?.id;
|
||||||
let slate;
|
|
||||||
if (this.props.data?.id) {
|
|
||||||
for (let s of this.props.viewer.slates) {
|
|
||||||
if (this.props.data.id && this.props.data.id === s.id) {
|
|
||||||
slate = s;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (slatename && username === this.props.viewer.username) {
|
|
||||||
for (let s of this.props.viewer.slates) {
|
|
||||||
if (username && slatename === s.slatename) {
|
|
||||||
slate = s;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!slate) {
|
|
||||||
Events.dispatchMessage({ message: "We're having trouble fetching that slate right now." });
|
|
||||||
this.setState({ notFound: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slate) {
|
// //NOTE(martina): look for the slate in the user's slates
|
||||||
window.history.replaceState(
|
// let slate;
|
||||||
{ ...window.history.state, data: slate },
|
// if (this.props.viewer) {
|
||||||
"Slate",
|
// if (id) {
|
||||||
`/${this.props.viewer.username}/${slate.slatename}`
|
// for (let s of this.props.viewer.slates) {
|
||||||
);
|
// if (id && id === s.id) {
|
||||||
}
|
// slate = s;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else if (slatename && username === this.props.viewer.username) {
|
||||||
|
// for (let s of this.props.viewer.slates) {
|
||||||
|
// if (username && slatename === s.slatename) {
|
||||||
|
// slate = s;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (!slate) {
|
||||||
|
// Events.dispatchMessage({
|
||||||
|
// message: "We're having trouble fetching that slate right now.",
|
||||||
|
// });
|
||||||
|
// this.setState({ notFound: true });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (!slate) {
|
// if (slate) {
|
||||||
let query;
|
// window.history.replaceState(
|
||||||
if (username && slatename) {
|
// { ...window.history.state, data: slate },
|
||||||
query = { username, slatename };
|
// "Slate",
|
||||||
} else if (this.props.data && this.props.data.id) {
|
// `/${this.props.viewer.username}/${slate.slatename}`
|
||||||
query = { id: this.props.data.id };
|
// );
|
||||||
}
|
// }
|
||||||
let response;
|
// }
|
||||||
if (query) {
|
|
||||||
response = await Actions.getSerializedSlate(query);
|
|
||||||
}
|
|
||||||
if (response?.decorator == "SLATE_PRIVATE_ACCESS_DENIED") {
|
|
||||||
this.setState({ accessDenied: true, loading: false });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Events.hasError(response)) {
|
|
||||||
this.setState({ notFound: true, loading: false });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
slate = response.slate;
|
|
||||||
window.history.replaceState(
|
|
||||||
{ ...window.history.state, data: slate },
|
|
||||||
"Slate",
|
|
||||||
`/${slate.user.username}/${slate.slatename}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.props.onUpdateData(slate, () => {
|
|
||||||
this.setState({ loading: false });
|
|
||||||
this.openCarouselToItem();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
openCarouselToItem = () => {
|
// if (!slate) {
|
||||||
if (!this.props.data?.objects?.length) {
|
// let query;
|
||||||
return;
|
// if (username && slatename) {
|
||||||
}
|
// query = { username, slatename };
|
||||||
let objects = this.props.data.objects;
|
// } else if (id) {
|
||||||
|
// query = { id };
|
||||||
|
// }
|
||||||
|
// let response;
|
||||||
|
// if (query) {
|
||||||
|
// response = await Actions.getSerializedSlate(query);
|
||||||
|
// }
|
||||||
|
// if (response?.decorator == "SLATE_PRIVATE_ACCESS_DENIED") {
|
||||||
|
// this.setState({ accessDenied: true, loading: false });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// if (Events.hasError(response)) {
|
||||||
|
// this.setState({ notFound: true, loading: false });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// slate = response.slate;
|
||||||
|
// window.history.replaceState(
|
||||||
|
// { ...window.history.state, data: slate },
|
||||||
|
// "Slate",
|
||||||
|
// `/${slate.user.username}/${slate.slatename}`
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// this.props.onUpdateData(slate, () => {
|
||||||
|
// this.setState({ loading: false });
|
||||||
|
// this.openCarouselToItem();
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
const { cid, fileId, index } = this.props.page;
|
// openCarouselToItem = () => {
|
||||||
|
// if (!this.props.data?.objects?.length || !this.props.page?.params) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// let objects = this.props.data.objects;
|
||||||
|
|
||||||
if (Strings.isEmpty(cid) && Strings.isEmpty(fileId) && typeof index === "undefined") {
|
// const { cid, fileId, index } = this.props.page.params;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let foundIndex = -1;
|
// if (Strings.isEmpty(cid) && Strings.isEmpty(fileId) && typeof index === "undefined") {
|
||||||
if (index) {
|
// return;
|
||||||
foundIndex = index;
|
// }
|
||||||
} else if (cid) {
|
|
||||||
foundIndex = objects.findIndex((object) => object.cid === cid);
|
|
||||||
} else if (fileId) {
|
|
||||||
foundIndex = objects.findIndex((object) => object.id === fileId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof foundIndex !== "undefined" && foundIndex !== -1) {
|
// let foundIndex = -1;
|
||||||
Events.dispatchCustomEvent({
|
// if (index) {
|
||||||
name: "slate-global-open-carousel",
|
// foundIndex = index;
|
||||||
detail: { index: foundIndex },
|
// } else if (cid) {
|
||||||
});
|
// foundIndex = objects.findIndex((object) => object.cid === cid);
|
||||||
}
|
// } else if (fileId) {
|
||||||
};
|
// foundIndex = objects.findIndex((object) => object.id === fileId);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (typeof foundIndex !== "undefined" && foundIndex !== -1) {
|
||||||
|
// Events.dispatchCustomEvent({
|
||||||
|
// name: "slate-global-open-carousel",
|
||||||
|
// detail: { index: foundIndex },
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.notFound || this.state.accessDenied) {
|
if (!this.props.data) {
|
||||||
return (
|
|
||||||
<ScenePage>
|
|
||||||
<EmptyState>
|
|
||||||
<SVG.Layers height="24px" style={{ marginBottom: 24 }} />
|
|
||||||
<div>
|
|
||||||
{this.state.accessDenied
|
|
||||||
? "You do not have access to that collection"
|
|
||||||
: "We were unable to locate that collection"}
|
|
||||||
</div>
|
|
||||||
</EmptyState>
|
|
||||||
</ScenePage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (this.state.loading) {
|
|
||||||
return (
|
return (
|
||||||
|
// <ScenePage>
|
||||||
|
// <EmptyState>
|
||||||
|
// <SVG.Layers height="24px" style={{ marginBottom: 24 }} />
|
||||||
|
// <div>We were unable to locate that collection</div>
|
||||||
|
// </EmptyState>
|
||||||
|
// </ScenePage>
|
||||||
<ScenePage>
|
<ScenePage>
|
||||||
<div css={STYLES_LOADER}>
|
<div css={STYLES_LOADER}>
|
||||||
<LoaderSpinner />
|
<LoaderSpinner />
|
||||||
</div>
|
</div>
|
||||||
</ScenePage>
|
</ScenePage>
|
||||||
);
|
);
|
||||||
} else if (this.props.data?.id) {
|
} else {
|
||||||
return <SlatePage {...this.props} key={this.props.data.id} current={this.props.data} />;
|
return <SlatePage {...this.props} key={this.props.data.id} data={this.props.data} />;
|
||||||
}
|
}
|
||||||
return null;
|
// if (this.state.loading) {
|
||||||
|
// return (
|
||||||
|
// <ScenePage>
|
||||||
|
// <div css={STYLES_LOADER}>
|
||||||
|
// <LoaderSpinner />
|
||||||
|
// </div>
|
||||||
|
// </ScenePage>
|
||||||
|
// );
|
||||||
|
// } else
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,11 +221,13 @@ class SlatePage extends React.Component {
|
|||||||
_remoteLock = false;
|
_remoteLock = false;
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
...(this.props.current, this.props.viewer),
|
...(this.props.data, this.props.viewer),
|
||||||
editing: false,
|
editing: false,
|
||||||
isSubscribed: this.props.viewer.subscriptions.some((subscription) => {
|
isSubscribed: this.props.viewer
|
||||||
return subscription.id === this.props.current.id;
|
? this.props.viewer.subscriptions.some((subscription) => {
|
||||||
}),
|
return subscription.id === this.props.data.id;
|
||||||
|
})
|
||||||
|
: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -229,13 +241,14 @@ class SlatePage extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = this.props.current.objects.findIndex((object) => object.cid === cid);
|
const index = this.props.data.objects.findIndex((object) => object.cid === cid);
|
||||||
if (index !== -1) {
|
// if (index !== -1) {
|
||||||
Events.dispatchCustomEvent({
|
// Events.dispatchCustomEvent({
|
||||||
name: "slate-global-open-carousel",
|
// name: "slate-global-open-carousel",
|
||||||
detail: { index },
|
// detail: { index },
|
||||||
});
|
// });
|
||||||
} else {
|
// }
|
||||||
|
if (index === -1) {
|
||||||
Events.dispatchCustomEvent({
|
Events.dispatchCustomEvent({
|
||||||
name: "create-alert",
|
name: "create-alert",
|
||||||
detail: {
|
detail: {
|
||||||
@ -249,26 +262,30 @@ class SlatePage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (this.props.viewer.subscriptions !== prevProps.viewer.subscriptions) {
|
if (this.props.viewer && this.props.viewer.subscriptions !== prevProps.viewer.subscriptions) {
|
||||||
this.setState({
|
this.setState({
|
||||||
isSubscribed: this.props.viewer.subscriptions.some((subscription) => {
|
isSubscribed: this.props.viewer.subscriptions.some((subscription) => {
|
||||||
return subscription.id === this.props.current.id;
|
return subscription.id === this.props.data.id;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleSubscribe = () => {
|
_handleSubscribe = () => {
|
||||||
|
if (!this.props.viewer) {
|
||||||
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.setState({ isSubscribed: !this.state.isSubscribed }, () => {
|
this.setState({ isSubscribed: !this.state.isSubscribed }, () => {
|
||||||
Actions.createSubscription({
|
Actions.createSubscription({
|
||||||
slateId: this.props.current.id,
|
slateId: this.props.data.id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleSaveLayout = async (layouts, autoSave) => {
|
_handleSaveLayout = async (layouts, autoSave) => {
|
||||||
const response = await Actions.updateSlateLayout({
|
const response = await Actions.updateSlateLayout({
|
||||||
id: this.props.current.id,
|
id: this.props.data.id,
|
||||||
layouts,
|
layouts,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -278,34 +295,41 @@ class SlatePage extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleSavePreview = async (preview) => {
|
_handleSavePreview = async (preview) => {
|
||||||
let updateObject = { id: this.props.current.id, data: { preview } };
|
if (!this.props.viewer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let updateObject = { id: this.props.data.id, data: { preview } };
|
||||||
|
|
||||||
let slates = this.props.viewer.slates;
|
let slates = this.props.viewer.slates;
|
||||||
let slateId = this.props.current.id;
|
let slateId = this.props.data.id;
|
||||||
for (let slate of slates) {
|
for (let slate of slates) {
|
||||||
if (slate.id === slateId) {
|
if (slate.id === slateId) {
|
||||||
slate.data.preview = preview;
|
slate.data.preview = preview;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.props.onUpdateViewer({ slates });
|
this.props.onAction({ type: "UPDATE_VIEWER", viewer: { slates } });
|
||||||
|
|
||||||
const response = await Actions.updateSlate(updateObject);
|
const response = await Actions.updateSlate(updateObject);
|
||||||
|
|
||||||
Events.hasError(response);
|
Events.hasError(response);
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleSelect = (index) =>
|
// _handleSelect = (index) =>
|
||||||
Events.dispatchCustomEvent({
|
// Events.dispatchCustomEvent({
|
||||||
name: "slate-global-open-carousel",
|
// name: "slate-global-open-carousel",
|
||||||
detail: { index },
|
// detail: { index },
|
||||||
});
|
// });
|
||||||
|
|
||||||
_handleAdd = async () => {
|
_handleAdd = async () => {
|
||||||
|
if (!this.props.viewer) {
|
||||||
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
await this.props.onAction({
|
await this.props.onAction({
|
||||||
type: "SIDEBAR",
|
type: "SIDEBAR",
|
||||||
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
|
value: "SIDEBAR_ADD_FILE_TO_BUCKET",
|
||||||
data: this.props.current,
|
data: this.props.data,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -313,7 +337,7 @@ class SlatePage extends React.Component {
|
|||||||
return this.props.onAction({
|
return this.props.onAction({
|
||||||
type: "SIDEBAR",
|
type: "SIDEBAR",
|
||||||
value: "SIDEBAR_SINGLE_SLATE_SETTINGS",
|
value: "SIDEBAR_SINGLE_SLATE_SETTINGS",
|
||||||
data: this.props.current,
|
data: this.props.data,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -330,8 +354,12 @@ class SlatePage extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_handleDownload = () => {
|
_handleDownload = () => {
|
||||||
const slateName = this.props.current.data.name;
|
if (!this.props.viewer) {
|
||||||
const slateFiles = this.props.current.objects;
|
Events.dispatchCustomEvent({ name: "slate-global-open-cta", detail: {} });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const slateName = this.props.data.data.name;
|
||||||
|
const slateFiles = this.props.data.objects;
|
||||||
UserBehaviors.compressAndDownloadFiles({
|
UserBehaviors.compressAndDownloadFiles({
|
||||||
files: slateFiles,
|
files: slateFiles,
|
||||||
name: `${slateName}.zip`,
|
name: `${slateName}.zip`,
|
||||||
@ -340,12 +368,12 @@ class SlatePage extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { user, data } = this.props.current;
|
const { user, data } = this.props.data;
|
||||||
const { body = "", preview } = data;
|
const { body = "", preview } = data;
|
||||||
let objects = this.props.current.objects;
|
let objects = this.props.data.objects;
|
||||||
let layouts = this.props.current.data.layouts;
|
let layouts = this.props.data.data.layouts;
|
||||||
const isPublic = this.props.current.isPublic;
|
const isPublic = this.props.data.isPublic;
|
||||||
const isOwner = this.props.current.ownerId === this.props.viewer.id;
|
const isOwner = this.props.viewer ? this.props.data.ownerId === this.props.viewer.id : false;
|
||||||
const tags = data.tags;
|
const tags = data.tags;
|
||||||
|
|
||||||
let actions = isOwner ? (
|
let actions = isOwner ? (
|
||||||
@ -381,19 +409,21 @@ class SlatePage extends React.Component {
|
|||||||
title={
|
title={
|
||||||
user && !isOwner ? (
|
user && !isOwner ? (
|
||||||
<span>
|
<span>
|
||||||
<span
|
<Link href={`/$/user/${user.id}`} onAction={this.props.onAction}>
|
||||||
onClick={() =>
|
<span
|
||||||
this.props.onAction({
|
// onClick={() =>
|
||||||
type: "NAVIGATE",
|
// this.props.onAction({
|
||||||
value: this.props.sceneId,
|
// type: "NAVIGATE",
|
||||||
scene: "PROFILE",
|
// value: "NAV_PROFILE",
|
||||||
data: user,
|
// shallow: true,
|
||||||
})
|
// data: user,
|
||||||
}
|
// })
|
||||||
css={STYLES_USERNAME}
|
// }
|
||||||
>
|
css={STYLES_USERNAME}
|
||||||
{user.username}
|
>
|
||||||
</span>{" "}
|
{user.username}
|
||||||
|
</span>{" "}
|
||||||
|
</Link>
|
||||||
/ {data.name}
|
/ {data.name}
|
||||||
{isOwner && !isPublic && (
|
{isOwner && !isPublic && (
|
||||||
<SVG.SecurityLock
|
<SVG.SecurityLock
|
||||||
@ -424,12 +454,12 @@ class SlatePage extends React.Component {
|
|||||||
<>
|
<>
|
||||||
<GlobalCarousel
|
<GlobalCarousel
|
||||||
carouselType="SLATE"
|
carouselType="SLATE"
|
||||||
onUpdateViewer={this.props.onUpdateViewer}
|
|
||||||
viewer={this.props.viewer}
|
viewer={this.props.viewer}
|
||||||
objects={objects}
|
objects={objects}
|
||||||
current={this.props.current}
|
data={this.props.data}
|
||||||
onAction={this.props.onAction}
|
onAction={this.props.onAction}
|
||||||
isMobile={this.props.isMobile}
|
isMobile={this.props.isMobile}
|
||||||
|
params={this.props.page.params}
|
||||||
isOwner={isOwner}
|
isOwner={isOwner}
|
||||||
external={this.props.external}
|
external={this.props.external}
|
||||||
/>
|
/>
|
||||||
@ -443,11 +473,12 @@ class SlatePage extends React.Component {
|
|||||||
) : (
|
) : (
|
||||||
<div style={{ marginTop: isOwner ? 24 : 48 }}>
|
<div style={{ marginTop: isOwner ? 24 : 48 }}>
|
||||||
<SlateLayout
|
<SlateLayout
|
||||||
key={this.props.current.id}
|
page={this.props.page}
|
||||||
current={this.props.current}
|
external={this.props.external}
|
||||||
onUpdateViewer={this.props.onUpdateViewer}
|
key={this.props.data.id}
|
||||||
|
data={this.props.data}
|
||||||
viewer={this.props.viewer}
|
viewer={this.props.viewer}
|
||||||
slateId={this.props.current.id}
|
slateId={this.props.data.id}
|
||||||
layout={layouts && layouts.ver === "2.0" ? layouts.layout || [] : null}
|
layout={layouts && layouts.ver === "2.0" ? layouts.layout || [] : null}
|
||||||
onSaveLayout={this._handleSaveLayout}
|
onSaveLayout={this._handleSaveLayout}
|
||||||
isOwner={isOwner}
|
isOwner={isOwner}
|
||||||
|
@ -31,67 +31,27 @@ export default class SceneSlates extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const tab = this.props.page.params?.tab || "collections";
|
||||||
let subscriptions = this.props.viewer.subscriptions;
|
let subscriptions = this.props.viewer.subscriptions;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScenePage>
|
<ScenePage>
|
||||||
<ScenePageHeader
|
<div style={{ display: "flex", alignItems: "center", marginBottom: 24 }}>
|
||||||
title={
|
<SecondaryTabGroup
|
||||||
this.props.isMobile ? (
|
tabs={[
|
||||||
<TabGroup
|
{ title: "My Collections", value: { tab: "collections" } },
|
||||||
tabs={[
|
{ title: "Subscribed", value: { tab: "subscribed" } },
|
||||||
{ title: "Files", value: "NAV_DATA" },
|
]}
|
||||||
{ title: "Collections", value: "NAV_SLATES" },
|
value={tab}
|
||||||
{ title: "Activity", value: "NAV_ACTIVITY" },
|
onAction={this.props.onAction}
|
||||||
]}
|
style={{ margin: 0 }}
|
||||||
value={1}
|
/>
|
||||||
onAction={this.props.onAction}
|
<SquareButtonGray onClick={this._handleAdd} style={{ marginLeft: 16 }}>
|
||||||
onChange={(value) => this.setState({ tab: value })}
|
<SVG.Plus height="16px" />
|
||||||
style={{ marginTop: 0, marginBottom: 32 }}
|
</SquareButtonGray>
|
||||||
itemStyle={{ margin: "0px 12px" }}
|
</div>
|
||||||
/>
|
{tab === "collections" ? (
|
||||||
) : (
|
this.props.viewer.slates?.length ? (
|
||||||
<PrimaryTabGroup
|
|
||||||
tabs={[
|
|
||||||
{ title: "Files", value: "NAV_DATA" },
|
|
||||||
{ title: "Collections", value: "NAV_SLATES" },
|
|
||||||
{ title: "Activity", value: "NAV_ACTIVITY" },
|
|
||||||
]}
|
|
||||||
value={1}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
actions={
|
|
||||||
<div style={{ display: "flex", alignItems: "center", marginBottom: 24 }}>
|
|
||||||
<SquareButtonGray onClick={this._handleAdd} style={{ marginRight: 16 }}>
|
|
||||||
<SVG.Plus height="16px" />
|
|
||||||
</SquareButtonGray>
|
|
||||||
<SecondaryTabGroup
|
|
||||||
tabs={[
|
|
||||||
{ title: "My Collections", value: "NAV_SLATES" },
|
|
||||||
{ title: "Subscribed", value: "NAV_SLATES_FOLLOWING" },
|
|
||||||
]}
|
|
||||||
value={this.props.tab}
|
|
||||||
onAction={this.props.onAction}
|
|
||||||
style={{ margin: 0 }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{/* <ScenePageHeader
|
|
||||||
title="Slates"
|
|
||||||
actions={
|
|
||||||
this.props.tab === 0 ? (
|
|
||||||
<SquareButtonGray onClick={this._handleAdd} style={{ marginLeft: 12 }}>
|
|
||||||
<SVG.Plus height="16px" />
|
|
||||||
</SquareButtonGray>
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
{this.props.tab === 0 ? (
|
|
||||||
this.props.viewer.slates && this.props.viewer.slates.length ? (
|
|
||||||
<SlatePreviewBlocks
|
<SlatePreviewBlocks
|
||||||
isOwner
|
isOwner
|
||||||
slates={this.props.viewer.slates}
|
slates={this.props.viewer.slates}
|
||||||
@ -111,7 +71,7 @@ export default class SceneSlates extends React.Component {
|
|||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{this.props.tab === 1 ? (
|
{tab === "subscribed" ? (
|
||||||
subscriptions && subscriptions.length ? (
|
subscriptions && subscriptions.length ? (
|
||||||
<SlatePreviewBlocks
|
<SlatePreviewBlocks
|
||||||
slates={subscriptions}
|
slates={subscriptions}
|
||||||
|
@ -12,6 +12,8 @@ console.log(`RUNNING: drop-database.js`);
|
|||||||
Promise.all([
|
Promise.all([
|
||||||
db.schema.dropTable("users"),
|
db.schema.dropTable("users"),
|
||||||
db.schema.dropTable("slates"),
|
db.schema.dropTable("slates"),
|
||||||
|
db.schema.dropTable("slate_files"),
|
||||||
|
db.schema.dropTable("files"),
|
||||||
db.schema.dropTable("activity"),
|
db.schema.dropTable("activity"),
|
||||||
db.schema.dropTable("subscriptions"),
|
db.schema.dropTable("subscriptions"),
|
||||||
db.schema.dropTable("keys"),
|
db.schema.dropTable("keys"),
|
||||||
|
@ -566,7 +566,7 @@ const runScript = async () => {
|
|||||||
// await printSlatesTable();
|
// await printSlatesTable();
|
||||||
|
|
||||||
//NOTE(martina): add tables
|
//NOTE(martina): add tables
|
||||||
// await addTables();
|
await addTables();
|
||||||
|
|
||||||
//NOTE(martina): put data into new tables
|
//NOTE(martina): put data into new tables
|
||||||
// await DB("slate_files").del();
|
// await DB("slate_files").del();
|
||||||
|
481
server.js
481
server.js
@ -8,6 +8,7 @@ import * as NodeLogging from "~/node_common/node-logging";
|
|||||||
import * as Validations from "~/common/validations";
|
import * as Validations from "~/common/validations";
|
||||||
import * as Window from "~/common/window";
|
import * as Window from "~/common/window";
|
||||||
import * as Strings from "~/common/strings";
|
import * as Strings from "~/common/strings";
|
||||||
|
import * as NavigationData from "~/common/navigation-data";
|
||||||
|
|
||||||
import limit from "express-rate-limit";
|
import limit from "express-rate-limit";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
@ -153,8 +154,8 @@ app.prepare().then(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.get("/_", async (req, res) => {
|
server.get("/_", async (req, res) => {
|
||||||
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
// let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
||||||
let isMac = Window.isMac(req.headers["user-agent"]);
|
// let isMac = Window.isMac(req.headers["user-agent"]);
|
||||||
|
|
||||||
const isBucketsAvailable = await Utilities.checkTextile();
|
const isBucketsAvailable = await Utilities.checkTextile();
|
||||||
|
|
||||||
@ -164,6 +165,37 @@ app.prepare().then(async () => {
|
|||||||
|
|
||||||
const id = Utilities.getIdFromCookie(req);
|
const id = Utilities.getIdFromCookie(req);
|
||||||
|
|
||||||
|
let viewer = null;
|
||||||
|
if (id) {
|
||||||
|
viewer = await Data.getUserById({
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewer) {
|
||||||
|
return res.redirect("/_/data");
|
||||||
|
} else {
|
||||||
|
return res.redirect("/_/explore");
|
||||||
|
}
|
||||||
|
|
||||||
|
// let page = NavigationData.getById(null, viewer);
|
||||||
|
|
||||||
|
// return app.render(req, res, "/_", {
|
||||||
|
// viewer,
|
||||||
|
// isMobile,
|
||||||
|
// isMac,
|
||||||
|
// page,
|
||||||
|
// data: null,
|
||||||
|
// resources: EXTERNAL_RESOURCES,
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
||||||
|
server.get("/_/:scene", async (req, res) => {
|
||||||
|
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
||||||
|
let isMac = Window.isMac(req.headers["user-agent"]);
|
||||||
|
|
||||||
|
const id = Utilities.getIdFromCookie(req);
|
||||||
|
|
||||||
let viewer = null;
|
let viewer = null;
|
||||||
if (id) {
|
if (id) {
|
||||||
viewer = await ViewerManager.getById({
|
viewer = await ViewerManager.getById({
|
||||||
@ -171,146 +203,93 @@ app.prepare().then(async () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let { page } = NavigationData.getByHref(req.path, viewer);
|
||||||
|
page = { ...page, params: req.query };
|
||||||
|
if (!page) {
|
||||||
|
return handler(req, res, req.url, {
|
||||||
|
isMobile,
|
||||||
|
resources: EXTERNAL_RESOURCES,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return app.render(req, res, "/_", {
|
return app.render(req, res, "/_", {
|
||||||
viewer,
|
|
||||||
isMobile,
|
isMobile,
|
||||||
isMac,
|
isMac,
|
||||||
|
viewer,
|
||||||
|
page,
|
||||||
|
data: null,
|
||||||
resources: EXTERNAL_RESOURCES,
|
resources: EXTERNAL_RESOURCES,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.get("/_/integration-page", async (req, res) => {
|
|
||||||
const id = Utilities.getIdFromCookie(req);
|
|
||||||
|
|
||||||
// let viewer = null;
|
|
||||||
// if (id) {
|
|
||||||
// viewer = await ViewerManager.getById({
|
|
||||||
// id,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
return app.render(req, res, "/_/integration-page", {
|
|
||||||
viewer: null,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
server.all("/_/:a", async (r, s) => handler(r, s, r.url));
|
server.all("/_/:a", async (r, s) => handler(r, s, r.url));
|
||||||
server.all("/_/:a/:b", async (r, s) => handler(r, s, r.url));
|
server.all("/_/:a/:b", async (r, s) => handler(r, s, r.url));
|
||||||
|
|
||||||
server.get("/[$]/slate/:id", async (req, res) => {
|
server.get("/[$]/slate/:id", async (req, res) => {
|
||||||
const slate = await Data.getSlateById({
|
const slate = await Data.getSlateById({
|
||||||
id: req.params.id,
|
id: req.params.id,
|
||||||
includeFiles: true,
|
|
||||||
sanitize: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!slate) {
|
if (!slate || slate.error) {
|
||||||
return res.redirect("/404");
|
return res.redirect("/_/404");
|
||||||
}
|
|
||||||
|
|
||||||
if (slate.error) {
|
|
||||||
return res.redirect("/404");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!slate.isPublic) {
|
|
||||||
return res.redirect("/403");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const creator = await Data.getUserById({
|
const creator = await Data.getUserById({
|
||||||
id: slate.ownerId,
|
id: slate.ownerId,
|
||||||
sanitize: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!creator) {
|
if (!creator || creator.error) {
|
||||||
return res.redirect("/404");
|
return res.redirect("/_/404");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (creator.error) {
|
let search = Strings.getQueryStringFromParams(req.query);
|
||||||
return res.redirect("/404");
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.redirect(`/${creator.username}/${slate.slatename}`);
|
return res.redirect(`/${creator.username}/${slate.slatename}${search}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
server.get("/[$]/user/:id", async (req, res) => {
|
server.get("/[$]/user/:id", async (req, res) => {
|
||||||
const creator = await Data.getUserById({
|
const creator = await Data.getUserById({
|
||||||
id: req.params.id,
|
id: req.params.id,
|
||||||
includeFiles: true,
|
|
||||||
publicOnly: true,
|
|
||||||
sanitize: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!creator) {
|
if (!creator || creator.error) {
|
||||||
return res.redirect("/404");
|
return res.redirect("/_/404");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (creator.error) {
|
let search = Strings.getQueryStringFromParams(req.query);
|
||||||
return res.redirect("/404");
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.redirect(`/${creator.username}`);
|
return res.redirect(`/${creator.username}${search}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
server.get("/[$]/:id", async (req, res) => {
|
server.get("/[$]/:id", async (req, res) => {
|
||||||
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
|
||||||
let isMac = Window.isMac(req.headers["user-agent"]);
|
|
||||||
|
|
||||||
const slate = await Data.getSlateById({
|
const slate = await Data.getSlateById({
|
||||||
id: req.params.id,
|
id: req.params.id,
|
||||||
includeFiles: true,
|
|
||||||
sanitize: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!slate) {
|
if (!slate || slate.error) {
|
||||||
return res.redirect("/404");
|
return res.redirect("/_/404");
|
||||||
}
|
|
||||||
|
|
||||||
if (slate.error) {
|
|
||||||
return res.redirect("/404");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!slate.isPublic) {
|
|
||||||
return res.redirect("/403");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const creator = await Data.getUserById({
|
const creator = await Data.getUserById({
|
||||||
id: slate.ownerId,
|
id: slate.ownerId,
|
||||||
sanitize: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!creator) {
|
if (!creator || creator.error) {
|
||||||
return res.redirect("/404");
|
return res.redirect("/_/404");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (creator.error) {
|
let search = Strings.getQueryStringFromParams(req.query);
|
||||||
return res.redirect("/404");
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = Utilities.getIdFromCookie(req);
|
return res.redirect(`/${creator.username}/${slate.slatename}${search}`);
|
||||||
|
|
||||||
// let viewer = null;
|
|
||||||
// if (id) {
|
|
||||||
// viewer = await ViewerManager.getById({
|
|
||||||
// id,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
return app.render(req, res, "/_/slate", {
|
|
||||||
viewer: null,
|
|
||||||
creator,
|
|
||||||
slate,
|
|
||||||
isMobile,
|
|
||||||
isMac,
|
|
||||||
resources: EXTERNAL_RESOURCES,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
server.get("/:username", async (req, res) => {
|
server.get("/:username", async (req, res) => {
|
||||||
|
const username = req.params.username.toLowerCase();
|
||||||
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
||||||
let isMac = Window.isMac(req.headers["user-agent"]);
|
let isMac = Window.isMac(req.headers["user-agent"]);
|
||||||
|
|
||||||
// TODO(jim): Temporary workaround
|
// TODO(jim): Temporary workaround
|
||||||
if (!Validations.userRoute(req.params.username)) {
|
if (!Validations.userRoute(username)) {
|
||||||
return handler(req, res, req.url, {
|
return handler(req, res, req.url, {
|
||||||
isMobile,
|
isMobile,
|
||||||
resources: EXTERNAL_RESOURCES,
|
resources: EXTERNAL_RESOURCES,
|
||||||
@ -318,125 +297,120 @@ app.prepare().then(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const id = Utilities.getIdFromCookie(req);
|
const id = Utilities.getIdFromCookie(req);
|
||||||
const shouldViewerRedirect = await ViewerManager.shouldRedirect({ id });
|
|
||||||
if (shouldViewerRedirect) {
|
let viewer = null;
|
||||||
return res.redirect(
|
if (id) {
|
||||||
`/_${Strings.createQueryParams({
|
viewer = await ViewerManager.getById({
|
||||||
scene: "NAV_PROFILE",
|
id,
|
||||||
user: req.params.username,
|
});
|
||||||
})}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
console.log(req.query);
|
||||||
|
let { page } = NavigationData.getByHref(req.path, viewer);
|
||||||
|
console.log(page);
|
||||||
|
page = { ...page, params: req.query };
|
||||||
|
console.log(page);
|
||||||
|
|
||||||
// let viewer = null;
|
let user = await Data.getUserByUsername({
|
||||||
// if (id) {
|
username,
|
||||||
// viewer = await ViewerManager.getById({
|
|
||||||
// id,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
let creator = await Data.getUserByUsername({
|
|
||||||
username: req.params.username.toLowerCase(),
|
|
||||||
includeFiles: true,
|
includeFiles: true,
|
||||||
sanitize: true,
|
sanitize: true,
|
||||||
publicOnly: true,
|
publicOnly: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!creator) {
|
if (!user) {
|
||||||
return res.redirect("/404");
|
return res.redirect("/_/404");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (creator.error) {
|
if (user.error) {
|
||||||
return res.redirect("/404");
|
return res.redirect("/_/404");
|
||||||
}
|
}
|
||||||
|
|
||||||
const slates = await Data.getSlatesByUserId({
|
const slates = await Data.getSlatesByUserId({
|
||||||
ownerId: creator.id,
|
ownerId: user.id,
|
||||||
sanitize: true,
|
sanitize: true,
|
||||||
includeFiles: true,
|
includeFiles: true,
|
||||||
publicOnly: true,
|
publicOnly: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
creator.slates = slates;
|
user.slates = slates;
|
||||||
|
|
||||||
return app.render(req, res, "/_/profile", {
|
return app.render(req, res, "/_", {
|
||||||
viewer: null,
|
viewer,
|
||||||
creator,
|
|
||||||
isMobile,
|
isMobile,
|
||||||
isMac,
|
isMac,
|
||||||
|
data: user,
|
||||||
|
page,
|
||||||
resources: EXTERNAL_RESOURCES,
|
resources: EXTERNAL_RESOURCES,
|
||||||
exploreSlates,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.get("/:username/cid::cid", async (req, res) => {
|
// server.get("/:username/cid::cid", async (req, res) => {
|
||||||
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
// const username = req.params.username.toLowerCase();
|
||||||
let isMac = Window.isMac(req.headers["user-agent"]);
|
// const cid = req.params.cid.toLowerCase();
|
||||||
|
|
||||||
// TODO(jim): Temporary workaround
|
// let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
||||||
if (!Validations.userRoute(req.params.username)) {
|
// let isMac = Window.isMac(req.headers["user-agent"]);
|
||||||
return handler(req, res, req.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = Utilities.getIdFromCookie(req);
|
// // TODO(jim): Temporary workaround
|
||||||
const shouldViewerRedirect = await ViewerManager.shouldRedirect({ id });
|
// if (!Validations.userRoute(username)) {
|
||||||
if (shouldViewerRedirect) {
|
// return handler(req, res, req.url);
|
||||||
return res.redirect(
|
// }
|
||||||
`/_${Strings.createQueryParams({
|
|
||||||
scene: "NAV_PROFILE",
|
|
||||||
user: req.params.username,
|
|
||||||
cid: req.params.cid,
|
|
||||||
})}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// let viewer = null;
|
// const id = Utilities.getIdFromCookie(req);
|
||||||
// if (id) {
|
|
||||||
// viewer = await ViewerManager.getById({
|
|
||||||
// id,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
let creator = await Data.getUserByUsername({
|
// let user = await Data.getUserByUsername({
|
||||||
username: req.params.username.toLowerCase(),
|
// username,
|
||||||
includeFiles: true,
|
// includeFiles: true,
|
||||||
sanitize: true,
|
// sanitize: true,
|
||||||
publicOnly: true,
|
// publicOnly: true,
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (!creator) {
|
// if (!user) {
|
||||||
return res.redirect("/404");
|
// return res.redirect("/_/404");
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (creator.error) {
|
// if (user.error) {
|
||||||
return res.redirect("/404");
|
// return res.redirect("/_/404");
|
||||||
}
|
// }
|
||||||
|
|
||||||
const slates = await Data.getSlatesByUserId({
|
// const slates = await Data.getSlatesByUserId({
|
||||||
ownerId: creator.id,
|
// ownerId: user.id,
|
||||||
sanitize: true,
|
// sanitize: true,
|
||||||
includeFiles: true,
|
// includeFiles: true,
|
||||||
publicOnly: true,
|
// publicOnly: true,
|
||||||
});
|
// });
|
||||||
|
|
||||||
creator.slates = slates;
|
// user.slates = slates;
|
||||||
|
|
||||||
return app.render(req, res, "/_/profile", {
|
// let viewer = null;
|
||||||
viewer: null,
|
// if (id) {
|
||||||
creator,
|
// viewer = await ViewerManager.getById({
|
||||||
isMobile,
|
// id,
|
||||||
isMac,
|
// });
|
||||||
resources: EXTERNAL_RESOURCES,
|
// }
|
||||||
cid: req.params.cid,
|
|
||||||
});
|
// let page = NavigationData.getById("NAV_PROFILE", viewer);
|
||||||
});
|
// page = { ...page, cid };
|
||||||
|
|
||||||
|
// return app.render(req, res, "/_", {
|
||||||
|
// viewer,
|
||||||
|
// isMobile,
|
||||||
|
// isMac,
|
||||||
|
// page,
|
||||||
|
// data: user,
|
||||||
|
// resources: EXTERNAL_RESOURCES,
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
server.get("/:username/:slatename", async (req, res) => {
|
server.get("/:username/:slatename", async (req, res) => {
|
||||||
|
const username = req.params.username.toLowerCase();
|
||||||
|
const slatename = req.params.slatename.toLowerCase();
|
||||||
|
|
||||||
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
||||||
let isMac = Window.isMac(req.headers["user-agent"]);
|
let isMac = Window.isMac(req.headers["user-agent"]);
|
||||||
|
|
||||||
// TODO(jim): Temporary workaround
|
// TODO(jim): Temporary workaround
|
||||||
if (!Validations.userRoute(req.params.username)) {
|
if (!Validations.userRoute(username)) {
|
||||||
return handler(req, res, req.url, {
|
return handler(req, res, req.url, {
|
||||||
isMobile,
|
isMobile,
|
||||||
resources: EXTERNAL_RESOURCES,
|
resources: EXTERNAL_RESOURCES,
|
||||||
@ -444,146 +418,117 @@ app.prepare().then(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const id = Utilities.getIdFromCookie(req);
|
const id = Utilities.getIdFromCookie(req);
|
||||||
const shouldViewerRedirect = await ViewerManager.shouldRedirect({ id });
|
|
||||||
if (shouldViewerRedirect) {
|
let viewer = null;
|
||||||
return res.redirect(
|
if (id) {
|
||||||
`/_${Strings.createQueryParams({
|
viewer = await ViewerManager.getById({
|
||||||
scene: "NAV_SLATE",
|
id,
|
||||||
user: req.params.username,
|
});
|
||||||
slate: req.params.slatename,
|
|
||||||
})}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let { page } = NavigationData.getByHref(req.path, viewer);
|
||||||
|
page = { ...page, params: req.query };
|
||||||
|
|
||||||
const slate = await Data.getSlateByName({
|
const slate = await Data.getSlateByName({
|
||||||
slatename: req.params.slatename,
|
slatename,
|
||||||
username: req.params.username,
|
username,
|
||||||
includeFiles: true,
|
includeFiles: true,
|
||||||
sanitize: true,
|
sanitize: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!slate) {
|
if (!slate || slate.error || (!slate.isPublic && slate.ownerId !== id)) {
|
||||||
return res.redirect("/404");
|
return res.redirect("/_/404");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slate.error) {
|
const user = await Data.getUserById({
|
||||||
return res.redirect("/404");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!slate.isPublic && slate.ownerId !== id) {
|
|
||||||
return res.redirect("/403");
|
|
||||||
}
|
|
||||||
|
|
||||||
const creator = await Data.getUserById({
|
|
||||||
id: slate.ownerId,
|
id: slate.ownerId,
|
||||||
sanitize: true,
|
sanitize: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!creator) {
|
if (!user) {
|
||||||
return res.redirect("/404");
|
return res.redirect("/_/404");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (creator.error) {
|
if (user.error) {
|
||||||
return res.redirect("/404");
|
return res.redirect("/_/404");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.params.username !== creator.username) {
|
slate.user = user;
|
||||||
return res.redirect("/403");
|
|
||||||
}
|
|
||||||
|
|
||||||
// let viewer = null;
|
return app.render(req, res, "/_", {
|
||||||
// if (id) {
|
viewer,
|
||||||
// viewer = await ViewerManager.getById({
|
|
||||||
// id,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
return app.render(req, res, "/_/slate", {
|
|
||||||
viewer: null,
|
|
||||||
creator,
|
|
||||||
slate,
|
|
||||||
isMobile,
|
isMobile,
|
||||||
isMac,
|
isMac,
|
||||||
|
data: slate,
|
||||||
|
page,
|
||||||
resources: EXTERNAL_RESOURCES,
|
resources: EXTERNAL_RESOURCES,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.get("/:username/:slatename/cid::cid", async (req, res) => {
|
// server.get("/:username/:slatename/cid::cid", async (req, res) => {
|
||||||
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
// const username = req.params.username.toLowerCase();
|
||||||
let isMac = Window.isMac(req.headers["user-agent"]);
|
// const slatename = req.params.slatename.toLowerCase();
|
||||||
|
// const cid = req.params.cid.toLowerCase();
|
||||||
|
|
||||||
// TODO(jim): Temporary workaround
|
// let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
||||||
if (!Validations.userRoute(req.params.username)) {
|
// let isMac = Window.isMac(req.headers["user-agent"]);
|
||||||
return handler(req, res, req.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = Utilities.getIdFromCookie(req);
|
// // TODO(jim): Temporary workaround
|
||||||
|
// if (!Validations.userRoute(username)) {
|
||||||
|
// return handler(req, res, req.url);
|
||||||
|
// }
|
||||||
|
|
||||||
const shouldViewerRedirect = await ViewerManager.shouldRedirect({ id });
|
// const id = Utilities.getIdFromCookie(req);
|
||||||
if (shouldViewerRedirect) {
|
|
||||||
return res.redirect(
|
|
||||||
`/_${Strings.createQueryParams({
|
|
||||||
scene: "NAV_SLATE",
|
|
||||||
user: req.params.username,
|
|
||||||
slate: req.params.slatename,
|
|
||||||
cid: req.params.cid,
|
|
||||||
})}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const slate = await Data.getSlateByName({
|
// const slate = await Data.getSlateByName({
|
||||||
slatename: req.params.slatename,
|
// slatename,
|
||||||
username: req.params.username,
|
// username,
|
||||||
includeFiles: true,
|
// includeFiles: true,
|
||||||
sanitize: true,
|
// sanitize: true,
|
||||||
});
|
// });
|
||||||
|
|
||||||
if (!slate) {
|
// if (!slate) {
|
||||||
return res.redirect("/404");
|
// return res.redirect("/_/404");
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (slate.error) {
|
// if (slate.error || !slate.isPublic && slate.ownerId !== id) {
|
||||||
return res.redirect("/404");
|
// return res.redirect("/_/404");
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (!slate.isPublic && slate.ownerId !== id) {
|
// const user = await Data.getUserById({
|
||||||
return res.redirect("/403");
|
// id: slate.ownerId,
|
||||||
}
|
// sanitize: true,
|
||||||
|
// });
|
||||||
|
|
||||||
const creator = await Data.getUserById({
|
// if (!user) {
|
||||||
id: slate.ownerId,
|
// return res.redirect("/_/404");
|
||||||
sanitize: true,
|
// }
|
||||||
});
|
|
||||||
|
|
||||||
if (!creator) {
|
// if (user.error) {
|
||||||
return res.redirect("/404");
|
// return res.redirect("/_/404");
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (creator.error) {
|
// let viewer = null;
|
||||||
return res.redirect("/404");
|
// if (id) {
|
||||||
}
|
// viewer = await ViewerManager.getById({
|
||||||
|
// id,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
if (req.params.username !== creator.username) {
|
// slate.user = user;
|
||||||
return res.redirect("/403");
|
|
||||||
}
|
|
||||||
|
|
||||||
// let viewer = null;
|
// let page = NavigationData.getById("NAV_SLATE", viewer);
|
||||||
// if (id) {
|
// page = { ...page, cid };
|
||||||
// viewer = await ViewerManager.getById({
|
|
||||||
// id,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
return app.render(req, res, "/_/slate", {
|
// return app.render(req, res, "/_", {
|
||||||
viewer: null,
|
// viewer,
|
||||||
creator,
|
// isMobile,
|
||||||
slate,
|
// isMac,
|
||||||
isMobile,
|
// data: slate,
|
||||||
isMac,
|
// page,
|
||||||
resources: EXTERNAL_RESOURCES,
|
// resources: EXTERNAL_RESOURCES,
|
||||||
cid: req.params.cid,
|
// });
|
||||||
});
|
// });
|
||||||
});
|
|
||||||
|
|
||||||
server.all("*", async (r, s) => handler(r, s, r.url));
|
server.all("*", async (r, s) => handler(r, s, r.url));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user