mirror of https://github.com/usememos/memos
feat: add explore page (#205)
parent
5eea1339c9
commit
e9ac6affef
@ -0,0 +1,29 @@
|
||||
import { useRef } from "react";
|
||||
import { formatMemoContent } from "../helpers/marked";
|
||||
import "../less/memo-content.less";
|
||||
|
||||
interface Props {
|
||||
className: string;
|
||||
content: string;
|
||||
onMemoContentClick: (e: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
const MemoContent: React.FC<Props> = (props: Props) => {
|
||||
const { className, content, onMemoContentClick } = props;
|
||||
const memoContentContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleMemoContentClick = async (e: React.MouseEvent) => {
|
||||
onMemoContentClick(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={memoContentContainerRef}
|
||||
className={`memo-content-text ${className}`}
|
||||
onClick={handleMemoContentClick}
|
||||
dangerouslySetInnerHTML={{ __html: formatMemoContent(content) }}
|
||||
></div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemoContent;
|
@ -0,0 +1,58 @@
|
||||
@import "./mixin.less";
|
||||
|
||||
.page-wrapper.explore {
|
||||
@apply relative top-0 w-full h-screen overflow-y-auto overflow-x-hidden;
|
||||
background-color: #f6f5f4;
|
||||
|
||||
> .page-container {
|
||||
@apply relative w-full min-h-screen mx-auto flex flex-col justify-start items-center;
|
||||
|
||||
> .page-header {
|
||||
@apply relative max-w-2xl w-full min-h-full flex flex-row justify-start items-center px-4 sm:pr-6;
|
||||
|
||||
> .logo-img {
|
||||
@apply h-14 w-auto mt-6 mb-2;
|
||||
}
|
||||
}
|
||||
|
||||
> .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;
|
||||
|
||||
> .memo-container {
|
||||
@apply flex flex-col justify-start items-start w-full p-4 mt-2 bg-white rounded-lg border border-white hover:border-gray-200;
|
||||
|
||||
> .memo-header {
|
||||
@apply mb-2 w-full flex flex-row justify-start items-center text-sm font-mono text-gray-400;
|
||||
|
||||
> .split-text {
|
||||
@apply mx-2;
|
||||
}
|
||||
|
||||
> .name-text {
|
||||
@apply hover:text-green-600 hover:underline;
|
||||
}
|
||||
}
|
||||
|
||||
> .memo-content {
|
||||
@apply cursor-default;
|
||||
|
||||
> * {
|
||||
@apply cursor-default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .addtion-btn-container {
|
||||
@apply fixed bottom-12 left-1/2 -translate-x-1/2;
|
||||
|
||||
> .btn {
|
||||
@apply bg-blue-600 text-white px-4 py-2 rounded-3xl shadow-2xl hover:opacity-80;
|
||||
|
||||
> .icon {
|
||||
@apply text-lg mr-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
import dayjs from "dayjs";
|
||||
import { useEffect, useState } from "react";
|
||||
import { locationService, memoService, userService } from "../services";
|
||||
import { useAppSelector } from "../store";
|
||||
import useI18n from "../hooks/useI18n";
|
||||
import useLoading from "../hooks/useLoading";
|
||||
import MemoContent from "../components/MemoContent";
|
||||
import "../less/explore.less";
|
||||
|
||||
interface State {
|
||||
memos: Memo[];
|
||||
}
|
||||
|
||||
const Explore = () => {
|
||||
const { t, locale } = useI18n();
|
||||
const user = useAppSelector((state) => state.user.user);
|
||||
const location = useAppSelector((state) => state.location);
|
||||
const [state, setState] = useState<State>({
|
||||
memos: [],
|
||||
});
|
||||
const loadingState = useLoading();
|
||||
|
||||
useEffect(() => {
|
||||
userService
|
||||
.initialState()
|
||||
.catch()
|
||||
.finally(async () => {
|
||||
const { host } = userService.getState();
|
||||
if (!host) {
|
||||
locationService.replaceHistory("/auth");
|
||||
return;
|
||||
}
|
||||
|
||||
memoService.fetchAllMemos().then((memos) => {
|
||||
setState({
|
||||
...state,
|
||||
memos,
|
||||
});
|
||||
});
|
||||
loadingState.setFinish();
|
||||
});
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
<section className="page-wrapper explore">
|
||||
{loadingState.isLoading ? null : (
|
||||
<div className="page-container">
|
||||
<div className="page-header">
|
||||
<img className="logo-img" src="/logo-full.webp" alt="" />
|
||||
</div>
|
||||
<main className="memos-wrapper">
|
||||
{state.memos.map((memo) => {
|
||||
const createdAtStr = dayjs(memo.createdTs).locale(locale).format("YYYY/MM/DD HH:mm:ss");
|
||||
return (
|
||||
<div className="memo-container" key={memo.id}>
|
||||
<div className="memo-header">
|
||||
<span className="time-text">{createdAtStr}</span>
|
||||
<span className="split-text">by</span>
|
||||
<a className="name-text" href={`/u/${memo.creator.id}`}>
|
||||
{memo.creator.name}
|
||||
</a>
|
||||
</div>
|
||||
<MemoContent className="memo-content" content={memo.content} onMemoContentClick={() => undefined} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</main>
|
||||
|
||||
<div className="addtion-btn-container">
|
||||
{user ? (
|
||||
<button className="btn" onClick={() => (window.location.href = "/")}>
|
||||
<span className="icon">🏠</span> {t("common.back-to-home")}
|
||||
</button>
|
||||
) : (
|
||||
<button className="btn" onClick={() => (window.location.href = "/auth")}>
|
||||
<span className="icon">👉</span> {t("common.sign-in")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Explore;
|
Loading…
Reference in New Issue