fix: stick mobile footer nav to bottom
This commit is contained in:
@@ -121,6 +121,9 @@ const zhDict: Dict = {
|
|||||||
favorites: "我的收藏",
|
favorites: "我的收藏",
|
||||||
favoritesComingSoon: "功能即将推出",
|
favoritesComingSoon: "功能即将推出",
|
||||||
favoritesComingSoonDesc: "登入与收藏功能开发中,敬请期待。",
|
favoritesComingSoonDesc: "登入与收藏功能开发中,敬请期待。",
|
||||||
|
featureUnavailable: "未开放",
|
||||||
|
featureUnavailableDesc: "该功能暂未开放。",
|
||||||
|
confirm: "知道了",
|
||||||
backToHome: "返回首页",
|
backToHome: "返回首页",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -236,6 +239,9 @@ const enDict: Dict = {
|
|||||||
favoritesComingSoon: "Coming Soon",
|
favoritesComingSoon: "Coming Soon",
|
||||||
favoritesComingSoonDesc:
|
favoritesComingSoonDesc:
|
||||||
"Sign-in and favorites are in development. Stay tuned.",
|
"Sign-in and favorites are in development. Stay tuned.",
|
||||||
|
featureUnavailable: "Not available yet",
|
||||||
|
featureUnavailableDesc: "This feature is not available yet.",
|
||||||
|
confirm: "Got it",
|
||||||
backToHome: "Back to Home",
|
backToHome: "Back to Home",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ function navIsActive(
|
|||||||
case "browseAll":
|
case "browseAll":
|
||||||
return pathname === "/browse" && !sp.has("sort");
|
return pathname === "/browse" && !sp.has("sort");
|
||||||
case "categories":
|
case "categories":
|
||||||
return pathname === "/" && hash === "#categories";
|
return (
|
||||||
|
pathname === "/categories" ||
|
||||||
|
(pathname === "/" && hash === "#categories")
|
||||||
|
);
|
||||||
case "browseLatest":
|
case "browseLatest":
|
||||||
return pathname === "/" && hash === "#latest";
|
return pathname === "/" && hash === "#latest";
|
||||||
case "browseRecommended":
|
case "browseRecommended":
|
||||||
@@ -387,7 +390,7 @@ export function PublicLayout() {
|
|||||||
{t("all")}
|
{t("all")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/#categories"
|
to="/categories"
|
||||||
className={navClassName(na("categories"))}
|
className={navClassName(na("categories"))}
|
||||||
aria-current={na("categories") ? "page" : undefined}
|
aria-current={na("categories") ? "page" : undefined}
|
||||||
>
|
>
|
||||||
@@ -479,7 +482,7 @@ export function PublicLayout() {
|
|||||||
{t("all")}
|
{t("all")}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/#categories"
|
to="/categories"
|
||||||
className={navClassName(na("categories"))}
|
className={navClassName(na("categories"))}
|
||||||
aria-current={na("categories") ? "page" : undefined}
|
aria-current={na("categories") ? "page" : undefined}
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
@@ -570,10 +573,10 @@ export function PublicLayout() {
|
|||||||
active={pathname === "/favorites"}
|
active={pathname === "/favorites"}
|
||||||
/>
|
/>
|
||||||
<BottomNavIcon
|
<BottomNavIcon
|
||||||
to="/#latest"
|
to="/#popular"
|
||||||
label={t("latest")}
|
label={t("popular")}
|
||||||
icon="update"
|
icon="update"
|
||||||
active={pathname === "/" && hash === "#latest"}
|
active={pathname === "/" && hash === "#popular"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export function Home() {
|
|||||||
const [latestPosts, setLatestPosts] = useState<Post[]>([]);
|
const [latestPosts, setLatestPosts] = useState<Post[]>([]);
|
||||||
const [popular, setPopular] = useState<PostBackedResource[]>([]);
|
const [popular, setPopular] = useState<PostBackedResource[]>([]);
|
||||||
const [popularPosts, setPopularPosts] = useState<Post[]>([]);
|
const [popularPosts, setPopularPosts] = useState<Post[]>([]);
|
||||||
|
const [categoryUnavailableOpen, setCategoryUnavailableOpen] = useState(false);
|
||||||
const [err, setErr] = useState<string | null>(null);
|
const [err, setErr] = useState<string | null>(null);
|
||||||
const recRowRef = useRef<HTMLDivElement>(null);
|
const recRowRef = useRef<HTMLDivElement>(null);
|
||||||
const categoryRowRef = useRef<HTMLDivElement>(null);
|
const categoryRowRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -66,9 +67,9 @@ export function Home() {
|
|||||||
Promise.all([
|
Promise.all([
|
||||||
getJSON<Category[]>(`/api/categories${catQ}`),
|
getJSON<Category[]>(`/api/categories${catQ}`),
|
||||||
getJSON<{ items: Post[] }>(`/api/posts/recommended${postQ}&limit=12`),
|
getJSON<{ items: Post[] }>(`/api/posts/recommended${postQ}&limit=12`),
|
||||||
getJSON<{ items: Post[] }>(`/api/posts/latest${postQ}&limit=8`),
|
getJSON<{ items: Post[] }>(`/api/posts/latest${postQ}&limit=5`),
|
||||||
getJSON<{ items: Post[] }>(
|
getJSON<{ items: Post[] }>(
|
||||||
`/api/posts${postQ}&sort=popular&limit=8`,
|
`/api/posts${postQ}&sort=popular&limit=5`,
|
||||||
).catch((): { items: Post[] } => ({ items: [] })),
|
).catch((): { items: Post[] } => ({ items: [] })),
|
||||||
])
|
])
|
||||||
.then(([c, r, l, p]) => {
|
.then(([c, r, l, p]) => {
|
||||||
@@ -206,7 +207,7 @@ export function Home() {
|
|||||||
<div className="px-4 md:px-0">
|
<div className="px-4 md:px-0">
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
title={t("categorySection")}
|
title={t("categorySection")}
|
||||||
viewAllTo="/browse"
|
viewAllTo="/categories"
|
||||||
viewAllLabel={t("viewAll")}
|
viewAllLabel={t("viewAll")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -224,9 +225,11 @@ export function Home() {
|
|||||||
className="grid w-full shrink-0 snap-start grid-cols-3 gap-2 px-4"
|
className="grid w-full shrink-0 snap-start grid-cols-3 gap-2 px-4"
|
||||||
>
|
>
|
||||||
{page.map((c) => (
|
{page.map((c) => (
|
||||||
<div
|
<button
|
||||||
key={c.id}
|
key={c.id}
|
||||||
className="flex h-[88px] min-w-0 cursor-default flex-col items-center justify-center gap-2 rounded-xl border border-[#27292E] bg-[#1D1E23] px-4 py-3 text-center"
|
type="button"
|
||||||
|
onClick={() => setCategoryUnavailableOpen(true)}
|
||||||
|
className="flex h-[88px] min-w-0 flex-col items-center justify-center gap-2 rounded-xl border border-[#27292E] bg-[#1D1E23] px-4 py-3 text-center outline-none transition hover:border-ark-gold/55 hover:bg-[#252630] focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg"
|
||||||
>
|
>
|
||||||
<CategoryIcon
|
<CategoryIcon
|
||||||
iconKey={c.iconKey}
|
iconKey={c.iconKey}
|
||||||
@@ -236,7 +239,7 @@ export function Home() {
|
|||||||
<div className="w-full truncate text-[13px] font-medium leading-[19.5px] text-white">
|
<div className="w-full truncate text-[13px] font-medium leading-[19.5px] text-white">
|
||||||
{cleanCategoryDisplayName(c.name)}
|
{cleanCategoryDisplayName(c.name)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -275,9 +278,11 @@ export function Home() {
|
|||||||
|
|
||||||
<div className="mt-7 hidden grid-cols-3 gap-3 min-[440px]:gap-3.5 md:grid md:grid-cols-5 md:gap-3 lg:grid-cols-6 xl:grid-cols-7 xl:gap-4">
|
<div className="mt-7 hidden grid-cols-3 gap-3 min-[440px]:gap-3.5 md:grid md:grid-cols-5 md:gap-3 lg:grid-cols-6 xl:grid-cols-7 xl:gap-4">
|
||||||
{figmaOrderedCategories.map((c) => (
|
{figmaOrderedCategories.map((c) => (
|
||||||
<div
|
<button
|
||||||
key={c.id}
|
key={c.id}
|
||||||
className="flex h-[88px] min-w-0 cursor-default flex-col items-center justify-center gap-2 rounded-xl border border-[#27292E] bg-[#1D1E23] px-4 py-3 text-center"
|
type="button"
|
||||||
|
onClick={() => setCategoryUnavailableOpen(true)}
|
||||||
|
className="flex h-[88px] min-w-0 flex-col items-center justify-center gap-2 rounded-xl border border-[#27292E] bg-[#1D1E23] px-4 py-3 text-center outline-none transition hover:border-ark-gold/55 hover:bg-[#252630] focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg"
|
||||||
>
|
>
|
||||||
<CategoryIcon
|
<CategoryIcon
|
||||||
iconKey={c.iconKey}
|
iconKey={c.iconKey}
|
||||||
@@ -287,7 +292,7 @@ export function Home() {
|
|||||||
<div className="w-full text-center text-[13px] font-medium leading-[19.5px] text-white line-clamp-2">
|
<div className="w-full text-center text-[13px] font-medium leading-[19.5px] text-white line-clamp-2">
|
||||||
{cleanCategoryDisplayName(c.name)}
|
{cleanCategoryDisplayName(c.name)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -296,7 +301,7 @@ export function Home() {
|
|||||||
<div className="px-4 md:px-0">
|
<div className="px-4 md:px-0">
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
title={t("officialSection")}
|
title={t("officialSection")}
|
||||||
viewAllTo="/browse?sort=recommended"
|
viewAllTo="/official-recommendations"
|
||||||
viewAllLabel={t("viewAll")}
|
viewAllLabel={t("viewAll")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -366,7 +371,7 @@ export function Home() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-3 md:hidden">
|
<div className="flex flex-col gap-3 md:hidden">
|
||||||
{latestPosts.slice(0, 4).map((post) => (
|
{latestPosts.slice(0, 5).map((post) => (
|
||||||
<MessageBubble key={post.id} post={post} />
|
<MessageBubble key={post.id} post={post} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -392,7 +397,7 @@ export function Home() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-3 md:hidden">
|
<div className="flex flex-col gap-3 md:hidden">
|
||||||
{popularPosts.slice(0, 4).map((post) => (
|
{popularPosts.slice(0, 5).map((post) => (
|
||||||
<MessageBubble key={post.id} post={post} />
|
<MessageBubble key={post.id} post={post} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -408,6 +413,38 @@ export function Home() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{categoryUnavailableOpen ? (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 z-[100] flex items-center justify-center bg-black/70 px-6 backdrop-blur-sm"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="category-unavailable-title"
|
||||||
|
onClick={() => setCategoryUnavailableOpen(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="w-full max-w-[320px] rounded-2xl border border-[#27292E] bg-[#1D1E23] p-5 text-center shadow-2xl"
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
id="category-unavailable-title"
|
||||||
|
className="text-xl font-bold text-white"
|
||||||
|
>
|
||||||
|
{t("featureUnavailable")}
|
||||||
|
</div>
|
||||||
|
<p className="mt-2 text-sm leading-6 text-[#A8A9AE]">
|
||||||
|
{t("featureUnavailableDesc")}
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setCategoryUnavailableOpen(false)}
|
||||||
|
className="mt-5 h-10 w-full rounded-full bg-ark-gold text-sm font-semibold text-black transition hover:bg-ark-gold2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-[#1D1E23]"
|
||||||
|
>
|
||||||
|
{t("confirm")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user