feat: update memo resources style (#933)

* feat: update memo resources style

* chore: update
pull/935/head
boojack 2 years ago committed by GitHub
parent 805122f45c
commit 8c146aed68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,7 @@
package server package server
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -267,18 +268,13 @@ func (s *Server) registerResourcePublicRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to fetch resource ID: %v", resourceID)).SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to fetch resource ID: %v", resourceID)).SetInternal(err)
} }
resourceType := resource.Type
if strings.HasPrefix(resource.Type, "text") || strings.HasPrefix(resource.Type, "application") { if strings.HasPrefix(resource.Type, "text") || strings.HasPrefix(resource.Type, "application") {
c.Response().Writer.Header().Set("Content-Type", echo.MIMETextPlain) resourceType = echo.MIMETextPlain
} else {
c.Response().Writer.Header().Set("Content-Type", resource.Type)
} }
c.Response().Writer.WriteHeader(http.StatusOK)
c.Response().Writer.Header().Set(echo.HeaderCacheControl, "max-age=31536000, immutable") c.Response().Writer.Header().Set(echo.HeaderCacheControl, "max-age=31536000, immutable")
c.Response().Writer.Header().Set(echo.HeaderContentSecurityPolicy, "default-src 'self'") c.Response().Writer.Header().Set(echo.HeaderContentSecurityPolicy, "default-src 'self'")
if _, err := c.Response().Writer.Write(resource.Blob); err != nil { return c.Stream(http.StatusOK, resourceType, bytes.NewReader(resource.Blob))
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to write response").SetInternal(err)
}
return nil
}) })
} }

@ -21,7 +21,7 @@ const DailyMemo: React.FC<Props> = (props: Props) => {
</div> </div>
<div className="memo-container"> <div className="memo-container">
<MemoContent content={memo.content} displayConfig={displayConfig} /> <MemoContent content={memo.content} displayConfig={displayConfig} />
<MemoResources resourceList={memo.resourceList} style="col" /> <MemoResources resourceList={memo.resourceList} />
</div> </div>
<div className="split-line"></div> <div className="split-line"></div>
</div> </div>

@ -1,23 +1,23 @@
import Image from "./Image"; import { absolutifyLink } from "../helpers/utils";
import Icon from "./Icon"; import Icon from "./Icon";
import SquareDiv from "./common/SquareDiv";
import showPreviewImageDialog from "./PreviewImageDialog";
import "../less/memo-resources.less"; import "../less/memo-resources.less";
interface Props { interface Props {
resourceList: Resource[]; resourceList: Resource[];
className?: string; className?: string;
style?: "row" | "col";
} }
const getDefaultProps = (): Props => { const getDefaultProps = (): Props => {
return { return {
className: "", className: "",
style: "row",
resourceList: [], resourceList: [],
}; };
}; };
const MemoResources: React.FC<Props> = (props: Props) => { const MemoResources: React.FC<Props> = (props: Props) => {
const { className, style, resourceList } = { const { className, resourceList } = {
...getDefaultProps(), ...getDefaultProps(),
...props, ...props,
}; };
@ -35,24 +35,39 @@ const MemoResources: React.FC<Props> = (props: Props) => {
return `/o/r/${resource.id}/${resource.filename}`; return `/o/r/${resource.id}/${resource.filename}`;
}); });
const handleImageClick = (imgUrl: string) => {
const index = imgUrls.findIndex((url) => url === imgUrl);
showPreviewImageDialog(imgUrls, index);
};
return ( return (
<div className={`resource-wrapper ${className || ""}`}> <>
{availableResourceList.length > 0 && ( <div className={`resource-wrapper ${className || ""}`}>
<div className={`images-wrapper ${style}`}> {availableResourceList.length > 0 && (
{availableResourceList.map((resource) => { <div className="images-wrapper">
const url = `/o/r/${resource.id}/${resource.filename}`; {availableResourceList.map((resource) => {
if (resource.type.startsWith("image")) { const url = `/o/r/${resource.id}/${resource.filename}`;
return ( if (resource.type.startsWith("image")) {
<Image className="memo-resource" key={resource.id} imgUrls={imgUrls} index={imgUrls.findIndex((item) => item === url)} /> return (
); <SquareDiv key={resource.id} className="memo-resource">
} else if (resource.type.startsWith("video")) { <img src={absolutifyLink(url)} onClick={() => handleImageClick(url)} decoding="async" loading="lazy" />
return <video className="memo-resource" controls key={resource.id} src={url} />; </SquareDiv>
} else { );
return null; } else if (resource.type.startsWith("video")) {
} return (
})} <SquareDiv key={resource.id} className="memo-resource">
</div> <video preload="metadata" controls key={resource.id}>
)} <source src={absolutifyLink(url)} type={resource.type} />
</video>
</SquareDiv>
);
} else {
return null;
}
})}
</div>
)}
</div>
<div className="other-resource-wrapper"> <div className="other-resource-wrapper">
{otherResourceList.map((resource) => { {otherResourceList.map((resource) => {
return ( return (
@ -63,7 +78,7 @@ const MemoResources: React.FC<Props> = (props: Props) => {
); );
})} })}
</div> </div>
</div> </>
); );
}; };

@ -135,7 +135,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
<span className="time-text">{memo.createdAtStr}</span> <span className="time-text">{memo.createdAtStr}</span>
<div className="memo-content-wrapper"> <div className="memo-content-wrapper">
<MemoContent content={memo.content} displayConfig={{ enableExpand: false }} /> <MemoContent content={memo.content} displayConfig={{ enableExpand: false }} />
<MemoResources style="col" resourceList={memo.resourceList} /> <MemoResources resourceList={memo.resourceList} />
</div> </div>
<div className="watermark-container"> <div className="watermark-container">
<div className="userinfo-container"> <div className="userinfo-container">

@ -0,0 +1,45 @@
import { useEffect, useRef } from "react";
interface Props {
children: React.ReactNode;
baseSide?: "width" | "height";
className?: string;
}
const SquareDiv: React.FC<Props> = (props: Props) => {
const { children, className } = props;
const baseSide = props.baseSide || "width";
const squareDivRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const adjustSquareSize = () => {
if (!squareDivRef.current) {
return;
}
if (baseSide === "width") {
const width = squareDivRef.current.clientWidth;
squareDivRef.current.style.height = width + "px";
} else {
const height = squareDivRef.current.clientHeight;
squareDivRef.current.style.width = height + "px";
}
};
adjustSquareSize();
window.addEventListener("resize", adjustSquareSize);
return () => {
window.removeEventListener("resize", adjustSquareSize);
};
}, []);
return (
<div ref={squareDivRef} className={`${[baseSide === "width" ? "w-full" : "h-full", className ?? ""].join(" ")}`}>
{children}
</div>
);
};
export default SquareDiv;

@ -21,5 +21,11 @@
.memo-content-text { .memo-content-text {
@apply mt-1; @apply mt-1;
} }
> .resource-wrapper {
> .images-wrapper {
@apply !grid-cols-2;
}
}
} }
} }

@ -1,11 +1,11 @@
.page-wrapper.explore { .page-wrapper.explore {
@apply relative top-0 w-full h-full overflow-y-auto overflow-x-hidden bg-zinc-100 dark:bg-zinc-800; @apply w-full h-full overflow-y-auto overflow-x-hidden bg-zinc-100 dark:bg-zinc-800;
> .page-container { > .page-container {
@apply relative w-full min-h-full mx-auto flex flex-col justify-start items-center pb-8; @apply relative w-full min-h-full mx-auto flex flex-col justify-start items-center pb-8;
> .page-header { > .page-header {
@apply sticky top-0 z-10 max-w-2xl w-full min-h-full flex flex-row justify-between backdrop-blur-sm items-center px-4 sm:pr-6 pt-6 mb-2 ml-calc; @apply sticky top-0 z-10 max-w-2xl w-full h-auto flex flex-row justify-between backdrop-blur-sm items-center px-4 sm:pr-6 pt-6 mb-2 ml-calc;
> .title-container { > .title-container {
@apply flex flex-row justify-start items-center; @apply flex flex-row justify-start items-center;
@ -27,7 +27,7 @@
} }
> .memos-wrapper { > .memos-wrapper {
@apply relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4 sm:pr-6 ml-calc; @apply relative flex-grow max-w-2xl w-full h-auto flex flex-col justify-start items-start px-4 sm:pr-6 ml-calc;
> .memo-container { > .memo-container {
@apply flex flex-col justify-start items-start w-full p-4 mt-2 bg-white dark:bg-zinc-700 rounded-lg border border-white dark:border-zinc-800 hover:border-gray-200 dark:hover:border-zinc-600; @apply flex flex-col justify-start items-start w-full p-4 mt-2 bg-white dark:bg-zinc-700 rounded-lg border border-white dark:border-zinc-800 hover:border-gray-200 dark:hover:border-zinc-600;

@ -1,5 +1,5 @@
.page-wrapper.home { .page-wrapper.home {
@apply relative top-0 w-full h-full overflow-y-auto overflow-x-hidden bg-zinc-100 dark:bg-zinc-800; @apply w-full h-full overflow-y-auto overflow-x-hidden bg-zinc-100 dark:bg-zinc-800;
> .banner-wrapper { > .banner-wrapper {
@apply w-full flex flex-col justify-start items-center; @apply w-full flex flex-col justify-start items-center;

@ -2,54 +2,34 @@
@apply w-full flex flex-col justify-start items-start; @apply w-full flex flex-col justify-start items-start;
> .images-wrapper { > .images-wrapper {
@apply w-full flex mt-2 pb-1; @apply w-full grid grid-cols-2 sm:grid-cols-3 gap-2 mt-2;
> .memo-resource { > .memo-resource {
@apply w-auto h-auto shrink-0 grow-0 cursor-pointer rounded; @apply shadow overflow-auto hide-scrollbar;
> img { > img {
@apply rounded hover:shadow; @apply cursor-pointer rounded min-h-full w-auto min-w-full object-cover;
} }
}
&.row {
@apply flex-row justify-start items-start overflow-x-auto overflow-y-hidden;
> .memo-resource {
@apply max-w-xs h-auto max-h-40 mr-2 last:mr-0;
> img {
@apply w-auto max-h-40;
}
}
}
&.col { > video {
@apply flex-col justify-start items-start; @apply cursor-pointer rounded w-full h-full object-contain bg-zinc-100 dark:bg-zinc-800;
> .memo-resource {
@apply w-full h-auto mb-2 last:mb-0;
> img {
@apply w-full h-auto shadow;
}
} }
} }
} }
}
> .other-resource-wrapper { .other-resource-wrapper {
@apply w-full flex flex-row justify-start flex-wrap; @apply w-full flex flex-row justify-start flex-wrap;
> .other-resource-container { > .other-resource-container {
@apply mt-1 mr-1 max-w-full flex flex-row justify-start items-center flex-nowrap bg-gray-100 px-2 py-1 rounded cursor-pointer hover:bg-gray-200; @apply mt-1 mr-1 max-w-full flex flex-row justify-start items-center flex-nowrap bg-gray-100 px-2 py-1 rounded cursor-pointer hover:bg-gray-200;
> .icon-img { > .icon-img {
@apply w-4 h-auto mr-1 text-gray-500; @apply w-4 h-auto mr-1 text-gray-500;
} }
> .name-text { > .name-text {
@apply text-gray-500 text-sm max-w-xs truncate font-mono; @apply text-gray-500 text-sm max-w-xs truncate font-mono;
}
} }
} }
} }

@ -47,13 +47,11 @@
> .memo-content-wrapper { > .memo-content-wrapper {
@apply w-full px-6 text-base pb-4; @apply w-full px-6 text-base pb-4;
}
> .images-container {
@apply w-full h-auto flex flex-col justify-start items-start px-6 pb-2;
> img { > .resource-wrapper {
@apply w-full h-auto mb-2 rounded; > .images-wrapper {
@apply !grid-cols-2;
}
} }
} }

Loading…
Cancel
Save