mirror of
https://github.com/filecoin-project/slate.git
synced 2024-12-25 10:05:02 +03:00
added standalone file page and edited share modal to use it
This commit is contained in:
parent
dd386ba452
commit
d9e2c89659
@ -4,14 +4,19 @@ import { css } from "@emotion/react";
|
||||
|
||||
/* TYPOGRAPHY */
|
||||
|
||||
export const OVERFLOW_ELLIPSIS = css`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
export const LINK = css`
|
||||
text-decoration: none;
|
||||
color: ${Constants.system.blue};
|
||||
color: ${Constants.semantic.textBlack};
|
||||
cursor: pointer;
|
||||
transition: 200ms ease color;
|
||||
|
||||
:visited {
|
||||
color: ${Constants.system.blue};
|
||||
color: ${Constants.semantic.textBlack};
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -166,3 +166,7 @@ export const getImageUrlIfExists = (file, sizeLimit = null) => {
|
||||
return file.linkImage;
|
||||
}
|
||||
};
|
||||
|
||||
export const getUserDisplayName = (user) => {
|
||||
return user.name || `@${user.username}`;
|
||||
};
|
||||
|
@ -17,7 +17,6 @@ import * as Environment from "~/common/environment";
|
||||
// Scenes each have an ID and can be navigated to with _handleAction
|
||||
import SceneError from "~/scenes/SceneError";
|
||||
import SceneEditAccount from "~/scenes/SceneEditAccount";
|
||||
import SceneFile from "~/scenes/SceneFile";
|
||||
import SceneFilesFolder from "~/scenes/SceneFilesFolder";
|
||||
import SceneSettings from "~/scenes/SceneSettings";
|
||||
import SceneSlates from "~/scenes/SceneSlates";
|
||||
@ -75,7 +74,6 @@ const SCENES = {
|
||||
NAV_DIRECTORY: <SceneDirectory />,
|
||||
NAV_PROFILE: <SceneProfile />,
|
||||
NAV_DATA: <SceneFilesFolder />,
|
||||
// NAV_FILE: <SceneFile />,
|
||||
NAV_SLATE: <SceneSlate />,
|
||||
NAV_API: <SceneSettingsDeveloper />,
|
||||
NAV_SETTINGS: <SceneEditAccount />,
|
||||
|
@ -4,6 +4,7 @@ import * as Styles from "~/common/styles";
|
||||
import * as UserBehaviors from "~/common/user-behaviors";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Environment from "~/common/environment";
|
||||
import * as Utilities from "~/common/utilities";
|
||||
|
||||
import { ButtonPrimaryFull, PopoverNavigation } from "~/components/system";
|
||||
import { css } from "@emotion/react";
|
||||
@ -129,7 +130,7 @@ export class ApplicationUserControlsPopup extends React.Component {
|
||||
render() {
|
||||
if (this.props.popup !== "profile") return null;
|
||||
|
||||
const username = this.props.viewer.name || `@${this.props.viewer.username}`;
|
||||
const username = Utilities.getUserDisplayName(this.props.viewer);
|
||||
const objectsLength = this.props.viewer.library.length;
|
||||
const { stats } = this.props.viewer;
|
||||
|
||||
|
@ -67,11 +67,9 @@ export default function LinkTag({ url, ...props }) {
|
||||
<System.P2
|
||||
style={{
|
||||
paddingRight: 8,
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
...props.style,
|
||||
}}
|
||||
css={Styles.OVERFLOW_ELLIPSIS}
|
||||
>
|
||||
{url}
|
||||
</System.P2>
|
||||
|
@ -9,13 +9,13 @@ import isEqual from "lodash/isEqual";
|
||||
import Dismissible from "~/components/core/Dismissible";
|
||||
|
||||
const STYLES_AVATAR = css`
|
||||
display: inline-flex;
|
||||
background-size: cover;
|
||||
background-position: 50% 50%;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
background-color: ${Constants.system.black};
|
||||
display: block;
|
||||
`;
|
||||
|
||||
const STYLES_AVATAR_ONLINE = css`
|
||||
|
@ -4,6 +4,7 @@ import * as SVG from "~/common/svg";
|
||||
import * as System from "~/components/system";
|
||||
import * as Styles from "~/common/styles";
|
||||
import * as Jumpers from "~/components/system/components/GlobalCarousel/jumpers";
|
||||
import * as Utilities from "~/common/utilities";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { Alert } from "~/components/core/Alert";
|
||||
@ -19,6 +20,7 @@ import { ModalPortal } from "~/components/core/ModalPortal";
|
||||
|
||||
import SlateMediaObject from "~/components/core/SlateMediaObject";
|
||||
import LinkIcon from "~/components/core/LinkIcon";
|
||||
import ProfilePhoto from "~/components/core/ProfilePhoto";
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Carousel Header
|
||||
@ -79,6 +81,7 @@ const STYLES_ACTION_BUTTON = css`
|
||||
`;
|
||||
|
||||
function CarouselHeader({
|
||||
isStandalone,
|
||||
viewer,
|
||||
data,
|
||||
external,
|
||||
@ -295,11 +298,27 @@ function CarouselHeader({
|
||||
{file.isLink ? <VisitLinkButton file={file} /> : null}
|
||||
</div>
|
||||
</AnimateSharedLayout>
|
||||
{isStandalone ? (
|
||||
<a href={`/${file.owner.username}`} css={Styles.LINK} style={{ marginLeft: 80 }}>
|
||||
<div
|
||||
style={{ gap: 8, maxWidth: "138px", justifyContent: "flex-end" }}
|
||||
css={Styles.HORIZONTAL_CONTAINER_CENTERED}
|
||||
>
|
||||
<div>
|
||||
<ProfilePhoto user={file.owner} style={{ borderRadius: "8px" }} size={20} />
|
||||
</div>
|
||||
<p css={[Styles.H5, Styles.OVERFLOW_ELLIPSIS]}>
|
||||
{Utilities.getUserDisplayName(file.owner)}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<div style={{ marginLeft: 80 }}>
|
||||
<button onClick={onClose} css={STYLES_ACTION_BUTTON}>
|
||||
<SVG.Dismiss />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</motion.nav>
|
||||
</>
|
||||
@ -346,14 +365,42 @@ const STYLES_CAROUSEL_MOBILE_SLIDE_COUNT = css`
|
||||
transform: translate(-50%, -50%);
|
||||
`;
|
||||
|
||||
function CarouselHeaderMobile({ current, total, onClose, onNextSlide, onPreviousSlide }) {
|
||||
function CarouselHeaderMobile({
|
||||
isStandalone,
|
||||
file,
|
||||
current,
|
||||
total,
|
||||
onClose,
|
||||
onNextSlide,
|
||||
onPreviousSlide,
|
||||
}) {
|
||||
const isPreviousButtonDisabled = current === 1;
|
||||
const isNextButtonDisabled = current === total;
|
||||
return (
|
||||
<nav css={STYLES_CAROUSEL_MOBILE_HEADER} style={{ justifyContent: "space-between" }}>
|
||||
{!isStandalone && (
|
||||
<>
|
||||
<div style={{ width: 76 }}>
|
||||
<button css={STYLES_ACTION_BUTTON} onClick={onPreviousSlide}>
|
||||
<button
|
||||
css={STYLES_ACTION_BUTTON}
|
||||
disabled={isPreviousButtonDisabled}
|
||||
style={isPreviousButtonDisabled ? { color: Constants.system.grayLight3 } : null}
|
||||
onClick={onPreviousSlide}
|
||||
>
|
||||
<SVG.ChevronLeft width={16} height={16} />
|
||||
</button>
|
||||
<button style={{ marginLeft: 12 }} css={STYLES_ACTION_BUTTON} onClick={onNextSlide}>
|
||||
<button
|
||||
style={
|
||||
isNextButtonDisabled
|
||||
? { color: Constants.system.grayLight3, marginLeft: 12 }
|
||||
: {
|
||||
marginLeft: 12,
|
||||
}
|
||||
}
|
||||
disabled={isNextButtonDisabled}
|
||||
css={STYLES_ACTION_BUTTON}
|
||||
onClick={onNextSlide}
|
||||
>
|
||||
<SVG.ChevronRight width={16} height={16} />
|
||||
</button>
|
||||
</div>
|
||||
@ -361,12 +408,28 @@ function CarouselHeaderMobile({ current, total, onClose, onNextSlide, onPrevious
|
||||
<System.H5 color="textGray" as="p" css={STYLES_CAROUSEL_MOBILE_SLIDE_COUNT}>
|
||||
{current} / {total}
|
||||
</System.H5>
|
||||
</>
|
||||
)}
|
||||
|
||||
{isStandalone ? (
|
||||
<div
|
||||
style={{ gap: 8, maxWidth: "138px", justifyContent: "flex-end" }}
|
||||
css={Styles.HORIZONTAL_CONTAINER_CENTERED}
|
||||
>
|
||||
<div>
|
||||
<ProfilePhoto user={file.owner} style={{ borderRadius: "8px" }} size={20} />
|
||||
</div>
|
||||
<p css={[Styles.H5, Styles.OVERFLOW_ELLIPSIS]}>
|
||||
{Utilities.getUserDisplayName(file.owner)}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ textAlign: "right" }}>
|
||||
<button onClick={onClose} css={STYLES_ACTION_BUTTON}>
|
||||
<SVG.Dismiss />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@ -617,7 +680,6 @@ const STYLES_PREVIEW_WRAPPER = (theme) => css`
|
||||
`;
|
||||
|
||||
export function CarouselContent({
|
||||
carouselType,
|
||||
objects,
|
||||
index,
|
||||
data,
|
||||
@ -629,9 +691,6 @@ export function CarouselContent({
|
||||
}) {
|
||||
const file = objects?.[index];
|
||||
|
||||
let isRepost = false;
|
||||
if (carouselType === "SLATE") isRepost = data?.ownerId !== file.ownerId;
|
||||
|
||||
useLockScroll();
|
||||
|
||||
return (
|
||||
@ -782,7 +841,7 @@ const STYLES_ROOT = (theme) => css`
|
||||
`;
|
||||
|
||||
export function GlobalCarousel({
|
||||
carouselType,
|
||||
isStandalone,
|
||||
objects,
|
||||
index,
|
||||
params,
|
||||
@ -797,7 +856,7 @@ export function GlobalCarousel({
|
||||
style,
|
||||
}) {
|
||||
const file = objects?.[index];
|
||||
const isCarouselOpen = (carouselType || index > 0 || index <= objects.length) && !!file;
|
||||
const isCarouselOpen = (index > 0 || index <= objects.length) && !!file;
|
||||
|
||||
useCarouselViaParams({ index, params, objects, onChange });
|
||||
|
||||
@ -815,6 +874,8 @@ export function GlobalCarousel({
|
||||
<div css={STYLES_ROOT}>
|
||||
{isMobile ? (
|
||||
<CarouselHeaderMobile
|
||||
isStandalone={isStandalone}
|
||||
file={file}
|
||||
current={index + 1}
|
||||
total={objects.length}
|
||||
onPreviousSlide={handlePrevious}
|
||||
@ -823,6 +884,7 @@ export function GlobalCarousel({
|
||||
/>
|
||||
) : (
|
||||
<CarouselHeader
|
||||
isStandalone={isStandalone}
|
||||
viewer={viewer}
|
||||
external={external}
|
||||
isOwner={isOwner}
|
||||
@ -839,7 +901,6 @@ export function GlobalCarousel({
|
||||
/>
|
||||
)}
|
||||
<CarouselContent
|
||||
carouselType={carouselType}
|
||||
objects={objects}
|
||||
index={index}
|
||||
data={data}
|
||||
|
@ -36,6 +36,12 @@ const getSlateURLFromViewer = ({ viewer, file }) => {
|
||||
return `${rootUrl}/${username}/${collection.slatename}?cid=${file.cid}`;
|
||||
};
|
||||
|
||||
const getFileURL = ({ file }) => {
|
||||
const rootUrl = window?.location?.origin;
|
||||
|
||||
return `${rootUrl}/_/object/${file.id}`;
|
||||
};
|
||||
|
||||
const getSlateURLFromData = ({ data, file }) => {
|
||||
const username = data?.user?.username;
|
||||
const rootUrl = window?.location?.origin;
|
||||
@ -46,9 +52,7 @@ const getSlateURLFromData = ({ data, file }) => {
|
||||
function FileSharingButtons({ file, data, viewer }) {
|
||||
const fileName = file?.name || file?.filename;
|
||||
const username = data?.user?.username || viewer?.username;
|
||||
const fileLink = data
|
||||
? getSlateURLFromData({ data, file })
|
||||
: getSlateURLFromViewer({ viewer, file });
|
||||
const fileLink = getFileURL({ file });
|
||||
const [copyState, setCopyState] = React.useState({ isCidCopied: false, isLinkCopied: false });
|
||||
|
||||
const handleTwitterSharing = () =>
|
||||
@ -61,9 +65,8 @@ function FileSharingButtons({ file, data, viewer }) {
|
||||
window.open(`mailto: ?subject=${fileName} by ${username} on Slate&body=${fileLink}`, "_b");
|
||||
};
|
||||
|
||||
const cidLink = Strings.getURLfromCID(file.cid);
|
||||
const handleLinkCopy = () => (
|
||||
Utilities.copyToClipboard(cidLink), setCopyState({ isLinkCopied: true })
|
||||
Utilities.copyToClipboard(fileLink), setCopyState({ isLinkCopied: true })
|
||||
);
|
||||
const handleCidCopy = () => (
|
||||
Utilities.copyToClipboard(file.cid), setCopyState({ isCidCopied: true })
|
||||
@ -82,7 +85,7 @@ function FileSharingButtons({ file, data, viewer }) {
|
||||
<button css={STYLES_SHARING_BUTTON} onClick={handleLinkCopy}>
|
||||
<SVG.Link width={16} />
|
||||
<System.P2 style={{ marginLeft: 12 }}>
|
||||
{copyState.isLinkCopied ? "Copied" : "Copy CID link"}
|
||||
{copyState.isLinkCopied ? "Copied" : "Copy link"}
|
||||
</System.P2>
|
||||
</button>
|
||||
<button css={STYLES_SHARING_BUTTON} onClick={handleCidCopy}>
|
||||
|
99
pages/_/file.js
Normal file
99
pages/_/file.js
Normal file
@ -0,0 +1,99 @@
|
||||
import * as React from "react";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as Strings from "~/common/strings";
|
||||
import * as Events from "~/common/custom-events";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { GlobalCarousel } from "~/components/system/components/GlobalCarousel";
|
||||
import { ButtonPrimary } from "~/components/system/components/Buttons";
|
||||
|
||||
import Profile from "~/components/core/Profile";
|
||||
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
|
||||
import WebsitePrototypeHeader from "~/components/core/WebsitePrototypeHeader";
|
||||
import WebsitePrototypeFooter from "~/components/core/WebsitePrototypeFooter";
|
||||
import CTATransition from "~/components/core/CTATransition";
|
||||
|
||||
const DEFAULT_IMAGE =
|
||||
"https://slate.textile.io/ipfs/bafkreiaow45dlq5xaydaeqocdxvffudibrzh2c6qandpqkb6t3ahbvh6re";
|
||||
|
||||
export const getServerSideProps = async (context) => {
|
||||
return {
|
||||
props: { ...context.query },
|
||||
};
|
||||
};
|
||||
|
||||
const STYLES_ROOT = css`
|
||||
display: block;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
font-size: 1rem;
|
||||
min-height: 100vh;
|
||||
background-color: ${Constants.semantic.bgLight};
|
||||
`;
|
||||
|
||||
export default class ProfilePage extends React.Component {
|
||||
state = {
|
||||
visible: false,
|
||||
page: null,
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
console.log(this.props.data);
|
||||
// window.onpopstate = this._handleBackForward;
|
||||
|
||||
// if (!Strings.isEmpty(this.props.cid)) {
|
||||
// let files = this.props.creator.library || [];
|
||||
// let index = files.findIndex((object) => object.cid === this.props.cid);
|
||||
// if (index !== -1) {
|
||||
// Events.dispatchCustomEvent({
|
||||
// name: "slate-global-open-carousel",
|
||||
// detail: { index },
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
// _handleBackForward = (e) => {
|
||||
// let page = window.history.state;
|
||||
// this.setState({ page });
|
||||
// Events.dispatchCustomEvent({ name: "slate-global-close-carousel", detail: {} });
|
||||
// };
|
||||
|
||||
render() {
|
||||
const isMobile = this.props.isMobile;
|
||||
|
||||
const viewer = this.props.viewer;
|
||||
const file = this.props.data;
|
||||
const { filename: title, body: description } = this.props.data;
|
||||
// const title = this.props.data
|
||||
// ? this.props.creator.name
|
||||
// ? `${this.props.creator.name} on Slate`
|
||||
// : `@${this.props.creator.username} on Slate`
|
||||
// : "404";
|
||||
const url = `https://slate.host/${title}`;
|
||||
// const description = this.props.creator.body;
|
||||
const image = DEFAULT_IMAGE; //this.props.creator.photo;
|
||||
// if (Strings.isEmpty(image)) {
|
||||
// image = DEFAULT_IMAGE;
|
||||
// }
|
||||
|
||||
return (
|
||||
<WebsitePrototypeWrapper title={title} description={description} url={url} image={image}>
|
||||
<WebsitePrototypeHeader />
|
||||
<div css={STYLES_ROOT}>
|
||||
<GlobalCarousel
|
||||
isStandalone
|
||||
viewer={viewer}
|
||||
objects={[file]}
|
||||
onAction={() => {}}
|
||||
isMobile={isMobile}
|
||||
// params={page.params}
|
||||
isOwner={viewer.id === file.ownerId}
|
||||
index={0}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
<WebsitePrototypeFooter />
|
||||
</WebsitePrototypeWrapper>
|
||||
);
|
||||
}
|
||||
}
|
@ -310,7 +310,6 @@ export default class SlatePage extends React.Component {
|
||||
<div css={STYLES_SLATE}>
|
||||
<GlobalCarousel
|
||||
data={this.props.slate}
|
||||
carouselType="SLATE"
|
||||
viewer={this.props.viewer}
|
||||
objects={objects}
|
||||
isMobile={this.props.isMobile}
|
||||
|
@ -96,7 +96,6 @@ export default function SceneActivity({ page, viewer, external, onAction, ...pro
|
||||
</ScenePage>
|
||||
|
||||
<GlobalCarousel
|
||||
carouselType="ACTIVITY"
|
||||
viewer={viewer}
|
||||
objects={globalCarouselState.currentObjects}
|
||||
index={globalCarouselState.currentCarousel}
|
||||
|
@ -1,98 +0,0 @@
|
||||
import * as React from "react";
|
||||
import * as Constants from "~/common/constants";
|
||||
import * as SVG from "~/common/svg";
|
||||
import * as Strings from "~/common/strings";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
|
||||
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper";
|
||||
import SlateMediaObject from "~/components/core/SlateMediaObject";
|
||||
|
||||
const STYLES_FLEX = css`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: ${Constants.system.black};
|
||||
`;
|
||||
|
||||
const STYLES_TOP = css`
|
||||
background: ${Constants.system.black};
|
||||
border-bottom: 1px solid ${Constants.system.black};
|
||||
color: ${Constants.system.white};
|
||||
width: 100%;
|
||||
padding: 12px 16px 12px 16px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const STYLES_LEFT = css`
|
||||
min-width: 10%;
|
||||
width: 100%;
|
||||
padding-right: 16px;
|
||||
`;
|
||||
|
||||
const STYLES_RIGHT = css`
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
padding-top: 4px;
|
||||
transition: 200ms ease color;
|
||||
user-select: none;
|
||||
|
||||
:hover {
|
||||
color: ${Constants.system.blue};
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_BOTTOM = css`
|
||||
background: ${Constants.system.black};
|
||||
color: ${Constants.system.white};
|
||||
padding: 12px 16px 12px 48px;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const STYLES_PATH = css`
|
||||
font-family: "mono";
|
||||
color: ${Constants.system.white};
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
overflow-wrap: break-word;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export default class SceneFile extends React.Component {
|
||||
render() {
|
||||
const cid = this.props.data.cid;
|
||||
const fileURL = Strings.getURLfromCID(cid);
|
||||
|
||||
return (
|
||||
<WebsitePrototypeWrapper
|
||||
title={`${this.props.page.pageTitle} • Slate`}
|
||||
url={`${Constants.hostname}${this.props.page.pathname}`}
|
||||
>
|
||||
<div css={STYLES_FLEX}>
|
||||
<div css={STYLES_TOP}>
|
||||
<div css={STYLES_LEFT}>
|
||||
<a css={STYLES_PATH} href={fileURL} target="_blank">
|
||||
{fileURL}
|
||||
</a>
|
||||
</div>
|
||||
<div css={STYLES_RIGHT}>
|
||||
<SVG.Dismiss height="24px" />
|
||||
</div>
|
||||
</div>
|
||||
<SlateMediaObject file={this.props.data} />
|
||||
</div>
|
||||
</WebsitePrototypeWrapper>
|
||||
);
|
||||
}
|
||||
}
|
@ -36,7 +36,6 @@ export default function SceneFilesFolder({ viewer, page, onAction, isMobile }) {
|
||||
>
|
||||
<ScenePage css={STYLES_SCENE_PAGE}>
|
||||
<GlobalCarousel
|
||||
carouselType="DATA"
|
||||
viewer={viewer}
|
||||
objects={objects}
|
||||
onAction={onAction}
|
||||
|
@ -465,7 +465,6 @@ class SlatePage extends React.Component {
|
||||
{objects && objects.length ? (
|
||||
<>
|
||||
<GlobalCarousel
|
||||
carouselType="SLATE"
|
||||
viewer={this.props.viewer}
|
||||
objects={objects}
|
||||
data={this.props.data}
|
||||
|
35
server.js
35
server.js
@ -122,6 +122,41 @@ app.prepare().then(async () => {
|
||||
// });
|
||||
});
|
||||
|
||||
server.get("/_/object/:id", async (req, res) => {
|
||||
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
||||
let isMac = Window.isMac(req.headers["user-agent"]);
|
||||
|
||||
const fileId = req.params.id;
|
||||
|
||||
const file = await Data.getFileById({ id: fileId });
|
||||
|
||||
const id = Utilities.getIdFromCookie(req);
|
||||
|
||||
if (!file.isPublic && file.ownerId !== id) {
|
||||
return res.redirect("/_/404");
|
||||
}
|
||||
|
||||
let viewer = null;
|
||||
if (id) {
|
||||
viewer = await ViewerManager.getById({
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
if (id && id === file.ownerId) {
|
||||
file.owner = viewer;
|
||||
} else {
|
||||
file.owner = await Data.getUserById({ id: file.ownerId, sanitize: true });
|
||||
}
|
||||
|
||||
return app.render(req, res, "/_/file", {
|
||||
viewer,
|
||||
isMobile,
|
||||
isMac,
|
||||
data: file,
|
||||
});
|
||||
});
|
||||
|
||||
server.get("/_/:scene", async (req, res) => {
|
||||
let isMobile = Window.isMobileBrowser(req.headers["user-agent"]);
|
||||
let isMac = Window.isMac(req.headers["user-agent"]);
|
||||
|
Loading…
Reference in New Issue
Block a user