mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
feat: update memo resources style (#933)
* feat: update memo resources style * chore: update
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"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)
|
||||
}
|
||||
|
||||
resourceType := resource.Type
|
||||
if strings.HasPrefix(resource.Type, "text") || strings.HasPrefix(resource.Type, "application") {
|
||||
c.Response().Writer.Header().Set("Content-Type", echo.MIMETextPlain)
|
||||
} else {
|
||||
c.Response().Writer.Header().Set("Content-Type", resource.Type)
|
||||
resourceType = echo.MIMETextPlain
|
||||
}
|
||||
c.Response().Writer.WriteHeader(http.StatusOK)
|
||||
c.Response().Writer.Header().Set(echo.HeaderCacheControl, "max-age=31536000, immutable")
|
||||
c.Response().Writer.Header().Set(echo.HeaderContentSecurityPolicy, "default-src 'self'")
|
||||
if _, err := c.Response().Writer.Write(resource.Blob); err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to write response").SetInternal(err)
|
||||
}
|
||||
return nil
|
||||
return c.Stream(http.StatusOK, resourceType, bytes.NewReader(resource.Blob))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ const DailyMemo: React.FC<Props> = (props: Props) => {
|
||||
</div>
|
||||
<div className="memo-container">
|
||||
<MemoContent content={memo.content} displayConfig={displayConfig} />
|
||||
<MemoResources resourceList={memo.resourceList} style="col" />
|
||||
<MemoResources resourceList={memo.resourceList} />
|
||||
</div>
|
||||
<div className="split-line"></div>
|
||||
</div>
|
||||
|
@ -1,23 +1,23 @@
|
||||
import Image from "./Image";
|
||||
import { absolutifyLink } from "../helpers/utils";
|
||||
import Icon from "./Icon";
|
||||
import SquareDiv from "./common/SquareDiv";
|
||||
import showPreviewImageDialog from "./PreviewImageDialog";
|
||||
import "../less/memo-resources.less";
|
||||
|
||||
interface Props {
|
||||
resourceList: Resource[];
|
||||
className?: string;
|
||||
style?: "row" | "col";
|
||||
}
|
||||
|
||||
const getDefaultProps = (): Props => {
|
||||
return {
|
||||
className: "",
|
||||
style: "row",
|
||||
resourceList: [],
|
||||
};
|
||||
};
|
||||
|
||||
const MemoResources: React.FC<Props> = (props: Props) => {
|
||||
const { className, style, resourceList } = {
|
||||
const { className, resourceList } = {
|
||||
...getDefaultProps(),
|
||||
...props,
|
||||
};
|
||||
@ -35,24 +35,39 @@ const MemoResources: React.FC<Props> = (props: Props) => {
|
||||
return `/o/r/${resource.id}/${resource.filename}`;
|
||||
});
|
||||
|
||||
const handleImageClick = (imgUrl: string) => {
|
||||
const index = imgUrls.findIndex((url) => url === imgUrl);
|
||||
showPreviewImageDialog(imgUrls, index);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`resource-wrapper ${className || ""}`}>
|
||||
{availableResourceList.length > 0 && (
|
||||
<div className={`images-wrapper ${style}`}>
|
||||
<div className="images-wrapper">
|
||||
{availableResourceList.map((resource) => {
|
||||
const url = `/o/r/${resource.id}/${resource.filename}`;
|
||||
if (resource.type.startsWith("image")) {
|
||||
return (
|
||||
<Image className="memo-resource" key={resource.id} imgUrls={imgUrls} index={imgUrls.findIndex((item) => item === url)} />
|
||||
<SquareDiv key={resource.id} className="memo-resource">
|
||||
<img src={absolutifyLink(url)} onClick={() => handleImageClick(url)} decoding="async" loading="lazy" />
|
||||
</SquareDiv>
|
||||
);
|
||||
} else if (resource.type.startsWith("video")) {
|
||||
return <video className="memo-resource" controls key={resource.id} src={url} />;
|
||||
return (
|
||||
<SquareDiv key={resource.id} className="memo-resource">
|
||||
<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">
|
||||
{otherResourceList.map((resource) => {
|
||||
return (
|
||||
@ -63,7 +78,7 @@ const MemoResources: React.FC<Props> = (props: Props) => {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -135,7 +135,7 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
|
||||
<span className="time-text">{memo.createdAtStr}</span>
|
||||
<div className="memo-content-wrapper">
|
||||
<MemoContent content={memo.content} displayConfig={{ enableExpand: false }} />
|
||||
<MemoResources style="col" resourceList={memo.resourceList} />
|
||||
<MemoResources resourceList={memo.resourceList} />
|
||||
</div>
|
||||
<div className="watermark-container">
|
||||
<div className="userinfo-container">
|
||||
|
45
web/src/components/common/SquareDiv.tsx
Normal file
45
web/src/components/common/SquareDiv.tsx
Normal file
@ -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 {
|
||||
@apply mt-1;
|
||||
}
|
||||
|
||||
> .resource-wrapper {
|
||||
> .images-wrapper {
|
||||
@apply !grid-cols-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
.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 {
|
||||
@apply relative w-full min-h-full mx-auto flex flex-col justify-start items-center pb-8;
|
||||
|
||||
> .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 {
|
||||
@apply flex flex-row justify-start items-center;
|
||||
@ -27,7 +27,7 @@
|
||||
}
|
||||
|
||||
> .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 {
|
||||
@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 {
|
||||
@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 {
|
||||
@apply w-full flex flex-col justify-start items-center;
|
||||
|
@ -2,42 +2,23 @@
|
||||
@apply w-full flex flex-col justify-start items-start;
|
||||
|
||||
> .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 {
|
||||
@apply w-auto h-auto shrink-0 grow-0 cursor-pointer rounded;
|
||||
@apply shadow overflow-auto hide-scrollbar;
|
||||
|
||||
> 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;
|
||||
> video {
|
||||
@apply cursor-pointer rounded w-full h-full object-contain bg-zinc-100 dark:bg-zinc-800;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .memo-resource {
|
||||
@apply max-w-xs h-auto max-h-40 mr-2 last:mr-0;
|
||||
|
||||
> img {
|
||||
@apply w-auto max-h-40;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.col {
|
||||
@apply flex-col justify-start items-start;
|
||||
|
||||
> .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;
|
||||
|
||||
> .other-resource-container {
|
||||
@ -51,5 +32,4 @@
|
||||
@apply text-gray-500 text-sm max-w-xs truncate font-mono;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,13 +47,11 @@
|
||||
|
||||
> .memo-content-wrapper {
|
||||
@apply w-full px-6 text-base pb-4;
|
||||
|
||||
> .resource-wrapper {
|
||||
> .images-wrapper {
|
||||
@apply !grid-cols-2;
|
||||
}
|
||||
|
||||
> .images-container {
|
||||
@apply w-full h-auto flex flex-col justify-start items-start px-6 pb-2;
|
||||
|
||||
> img {
|
||||
@apply w-full h-auto mb-2 rounded;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user