Merge branch 'subwork' into mainwork
This commit is contained in:
@@ -188,7 +188,6 @@ async function upsertBoards(admin, categoryMap) {
|
|||||||
{ name: "이벤트", slug: "event", description: "이벤트", sortOrder: 4, requiredTags: { required: ["이벤트"] } },
|
{ name: "이벤트", slug: "event", description: "이벤트", sortOrder: 4, requiredTags: { required: ["이벤트"] } },
|
||||||
{ name: "자유게시판", slug: "free", description: "자유", sortOrder: 5 },
|
{ name: "자유게시판", slug: "free", description: "자유", sortOrder: 5 },
|
||||||
{ name: "무엇이든", slug: "qna", description: "무엇이든 물어보세요", sortOrder: 6 },
|
{ name: "무엇이든", slug: "qna", description: "무엇이든 물어보세요", sortOrder: 6 },
|
||||||
{ name: "마사지꿀팁", slug: "tips", description: "팁", sortOrder: 7 },
|
|
||||||
{ name: "익명게시판", slug: "anonymous", description: "익명", sortOrder: 8, allowAnonymousPost: true, allowSecretComment: true },
|
{ name: "익명게시판", slug: "anonymous", description: "익명", sortOrder: 8, allowAnonymousPost: true, allowSecretComment: true },
|
||||||
{ name: "청와대", slug: "blue-house", description: "레벨 제한", sortOrder: 10, readLevel: "member" },
|
{ name: "청와대", slug: "blue-house", description: "레벨 제한", sortOrder: 10, readLevel: "member" },
|
||||||
// 특수
|
// 특수
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function BoardToolbar({ boardId }: { boardId: string }) {
|
|||||||
const onChangeSort = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
const onChangeSort = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
const next = new URLSearchParams(sp.toString());
|
const next = new URLSearchParams(sp.toString());
|
||||||
next.set("sort", e.target.value);
|
next.set("sort", e.target.value);
|
||||||
router.push(`/boards/${boardId}?${next.toString()}`);
|
router.push(`/boards/${boardId}?${next.toString()}` , { scroll: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChangePeriod = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
const onChangePeriod = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
@@ -26,7 +26,7 @@ export function BoardToolbar({ boardId }: { boardId: string }) {
|
|||||||
if (v === "1w") now.setDate(now.getDate() - 7);
|
if (v === "1w") now.setDate(now.getDate() - 7);
|
||||||
if (v === "1m") now.setMonth(now.getMonth() - 1);
|
if (v === "1m") now.setMonth(now.getMonth() - 1);
|
||||||
if (v === "all") next.delete("start"); else next.set("start", now.toISOString());
|
if (v === "all") next.delete("start"); else next.set("start", now.toISOString());
|
||||||
router.push(`/boards/${boardId}?${next.toString()}`);
|
router.push(`/boards/${boardId}?${next.toString()}` , { scroll: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = (formData: FormData) => {
|
const onSubmit = (formData: FormData) => {
|
||||||
@@ -41,7 +41,7 @@ export function BoardToolbar({ boardId }: { boardId: string }) {
|
|||||||
next.delete("author");
|
next.delete("author");
|
||||||
if (text) next.set("q", text); else next.delete("q");
|
if (text) next.set("q", text); else next.delete("q");
|
||||||
}
|
}
|
||||||
router.push(`/boards/${boardId}?${next.toString()}`);
|
router.push(`/boards/${boardId}?${next.toString()}` , { scroll: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import useSWRInfinite from "swr/infinite";
|
import useSWRInfinite from "swr/infinite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
import ViewsIcon from "@/app/svgs/ViewsIcon";
|
import ViewsIcon from "@/app/svgs/ViewsIcon";
|
||||||
import LikeIcon from "@/app/svgs/LikeIcon";
|
import LikeIcon from "@/app/svgs/LikeIcon";
|
||||||
import CommentIcon from "@/app/svgs/CommentIcon";
|
import CommentIcon from "@/app/svgs/CommentIcon";
|
||||||
@@ -31,8 +31,9 @@ const fetcher = (url: string) => fetch(url).then((r) => r.json());
|
|||||||
|
|
||||||
export function PostList({ boardId, sort = "recent", q, tag, author, start, end, variant = "default", newPostHref }: { boardId?: string; sort?: "recent" | "popular"; q?: string; tag?: string; author?: string; start?: string; end?: string; variant?: "default" | "board"; newPostHref?: string }) {
|
export function PostList({ boardId, sort = "recent", q, tag, author, start, end, variant = "default", newPostHref }: { boardId?: string; sort?: "recent" | "popular"; q?: string; tag?: string; author?: string; start?: string; end?: string; variant?: "default" | "board"; newPostHref?: string }) {
|
||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
const router = useRouter();
|
|
||||||
const sp = useSearchParams();
|
const sp = useSearchParams();
|
||||||
|
const listContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [lockedMinHeight, setLockedMinHeight] = useState<number | null>(null);
|
||||||
|
|
||||||
const getKey = (index: number, prev: Resp | null) => {
|
const getKey = (index: number, prev: Resp | null) => {
|
||||||
if (prev && prev.items.length === 0) return null;
|
if (prev && prev.items.length === 0) return null;
|
||||||
@@ -76,6 +77,21 @@ export function PostList({ boardId, sort = "recent", q, tag, author, start, end,
|
|||||||
const items = variant === "board" ? itemsSingle : itemsInfinite;
|
const items = variant === "board" ? itemsSingle : itemsInfinite;
|
||||||
const isEmpty = variant === "board" ? isEmptySingle : isEmptyInfinite;
|
const isEmpty = variant === "board" ? isEmptySingle : isEmptyInfinite;
|
||||||
|
|
||||||
|
// 잠깐 높이 고정: 페이지 변경으로 재로딩될 때 현재 높이를 min-height로 유지
|
||||||
|
const lockHeight = () => {
|
||||||
|
const el = listContainerRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
const h = el.offsetHeight;
|
||||||
|
if (h > 0) setLockedMinHeight(h);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 로딩이 끝나면 해제
|
||||||
|
useEffect(() => {
|
||||||
|
if (variant === "board" && !isLoadingSingle) {
|
||||||
|
setLockedMinHeight(null);
|
||||||
|
}
|
||||||
|
}, [variant, isLoadingSingle]);
|
||||||
|
|
||||||
const initials = (name?: string | null) => (name ? name.trim().slice(0, 1) : "익");
|
const initials = (name?: string | null) => (name ? name.trim().slice(0, 1) : "익");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -122,6 +138,7 @@ export function PostList({ boardId, sort = "recent", q, tag, author, start, end,
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 아이템들 */}
|
{/* 아이템들 */}
|
||||||
|
<div ref={listContainerRef} style={{ minHeight: lockedMinHeight ? `${lockedMinHeight}px` : undefined }}>
|
||||||
<ul className="divide-y divide-[#ececec]">
|
<ul className="divide-y divide-[#ececec]">
|
||||||
{items.map((p) => (
|
{items.map((p) => (
|
||||||
<li key={p.id} className={`px-4 ${variant === "board" ? "py-2.5" : "py-3 md:py-3"} hover:bg-neutral-50 transition-colors`}>
|
<li key={p.id} className={`px-4 ${variant === "board" ? "py-2.5" : "py-3 md:py-3"} hover:bg-neutral-50 transition-colors`}>
|
||||||
@@ -156,6 +173,7 @@ export function PostList({ boardId, sort = "recent", q, tag, author, start, end,
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 페이지네이션 */}
|
{/* 페이지네이션 */}
|
||||||
{!isEmpty && (
|
{!isEmpty && (
|
||||||
@@ -165,11 +183,14 @@ export function PostList({ boardId, sort = "recent", q, tag, author, start, end,
|
|||||||
{/* Previous */}
|
{/* Previous */}
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
lockHeight();
|
||||||
const next = Math.max(1, page - 1);
|
const next = Math.max(1, page - 1);
|
||||||
setPage(next);
|
setPage(next);
|
||||||
const nextSp = new URLSearchParams(Array.from(sp.entries()));
|
const nextSp = new URLSearchParams(Array.from(sp.entries()));
|
||||||
nextSp.set("page", String(next));
|
nextSp.set("page", String(next));
|
||||||
router.push(`?${nextSp.toString()}`);
|
if (typeof window !== "undefined") {
|
||||||
|
window.history.replaceState(null, "", `?${nextSp.toString()}`);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
disabled={page <= 1}
|
disabled={page <= 1}
|
||||||
className="h-9 px-3 rounded-md border border-neutral-300 bg-white text-sm disabled:opacity-50"
|
className="h-9 px-3 rounded-md border border-neutral-300 bg-white text-sm disabled:opacity-50"
|
||||||
@@ -196,10 +217,13 @@ export function PostList({ boardId, sort = "recent", q, tag, author, start, end,
|
|||||||
<button
|
<button
|
||||||
key={`p-${n}-${idx}`}
|
key={`p-${n}-${idx}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
lockHeight();
|
||||||
setPage(n);
|
setPage(n);
|
||||||
const nextSp = new URLSearchParams(Array.from(sp.entries()));
|
const nextSp = new URLSearchParams(Array.from(sp.entries()));
|
||||||
nextSp.set("page", String(n));
|
nextSp.set("page", String(n));
|
||||||
router.push(`?${nextSp.toString()}`);
|
if (typeof window !== "undefined") {
|
||||||
|
window.history.replaceState(null, "", `?${nextSp.toString()}`);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
aria-current={n === page ? "page" : undefined}
|
aria-current={n === page ? "page" : undefined}
|
||||||
className={`h-9 w-9 rounded-md border ${n === page ? "border-neutral-300 text-[#f94b37] font-semibold" : "border-neutral-300 text-neutral-900"}`}
|
className={`h-9 w-9 rounded-md border ${n === page ? "border-neutral-300 text-[#f94b37] font-semibold" : "border-neutral-300 text-neutral-900"}`}
|
||||||
@@ -215,11 +239,14 @@ export function PostList({ boardId, sort = "recent", q, tag, author, start, end,
|
|||||||
{/* Next */}
|
{/* Next */}
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
lockHeight();
|
||||||
const next = Math.min(totalPages, page + 1);
|
const next = Math.min(totalPages, page + 1);
|
||||||
setPage(next);
|
setPage(next);
|
||||||
const nextSp = new URLSearchParams(Array.from(sp.entries()));
|
const nextSp = new URLSearchParams(Array.from(sp.entries()));
|
||||||
nextSp.set("page", String(next));
|
nextSp.set("page", String(next));
|
||||||
router.push(`?${nextSp.toString()}`);
|
if (typeof window !== "undefined") {
|
||||||
|
window.history.replaceState(null, "", `?${nextSp.toString()}`);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
disabled={page >= totalPages}
|
disabled={page >= totalPages}
|
||||||
className="h-9 px-3 rounded-md border border-neutral-300 bg-white text-sm disabled:opacity-50"
|
className="h-9 px-3 rounded-md border border-neutral-300 bg-white text-sm disabled:opacity-50"
|
||||||
|
|||||||
Reference in New Issue
Block a user