Feat: Note & Gallery Details
日记列表增加点赞和精选效果,日记详情增加点赞等功能(尚未完善交互),相册列表增加精选和描述内容
This commit is contained in:
@@ -2,6 +2,7 @@ import { NavLink, useLoaderData, useNavigate } from "@remix-run/react";
|
||||
import { json, LoaderFunctionArgs, type MetaFunction } from "@remix-run/node";
|
||||
import Pagination from "~/components/common/pagination";
|
||||
import { clsn, siteTitle } from "~/utils";
|
||||
import { StarFill } from "~/components/common/icons";
|
||||
|
||||
import styles from "./styles.module.less";
|
||||
|
||||
@@ -65,11 +66,21 @@ export default function Gallery() {
|
||||
</section>
|
||||
<section className="grid gap-8 grid-cols-2 sm:grid-cols-[repeat(auto-fill,minmax(18em,_1fr))] mb-12">
|
||||
{media.data.map((item) => (
|
||||
<div key={item.id} className="bg-white rounded-xl overflow-hidden border-4 border-transparent hover:border-pink-400 transition-colors border-b-4 border-b-cyan-200">
|
||||
<div key={item.id} className="relative bg-white rounded-xl overflow-hidden border-4 border-transparent hover:border-pink-400 transition-colors border-b-4 border-b-cyan-200">
|
||||
<img className={styles.image} src={item.thumb_url} alt={item.title} loading="lazy" />
|
||||
<div className="relative p-4 sm:p-6 -mt-6 sm:-mt-12">
|
||||
{item.content && (
|
||||
<div className={clsn("absolute top-0 left-0 right-0 bottom-0 bg-opacity-60 bg-black p-4 sm:p-6 text-white leading-7 transition-opacity duration-300 opacity-0 hover:opacity-100", styles.desc)}>
|
||||
{item.content}
|
||||
</div>
|
||||
)}
|
||||
<div className="p-4 sm:p-6 -mt-6 sm:-mt-12">
|
||||
<span className="block text-sm mb-4 opacity-60">{item.take_time.substring(0, 10)}</span>
|
||||
<h1 className="text-pink-400 text-xl sm:text-2xl font-bold text-ellipsis overflow-hidden">{item.title}</h1>
|
||||
<h1 className="text-pink-400 text-xl sm:text-2xl font-bold text-ellipsis overflow-hidden">
|
||||
{item.title}
|
||||
{item.starred && (
|
||||
<StarFill className=" text-yellow-400 w-6 h-6 inline-block ml-2 align-[-.1em]" />
|
||||
)}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
.image {
|
||||
width: 100%;
|
||||
.image, .desc {
|
||||
aspect-ratio: 1 / 1;
|
||||
background-color: #999;
|
||||
clip-path: polygon(0 0, 100% 0%, 100% 100%, 0 75%);
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { LoaderFunctionArgs, MetaFunction, json } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { useState } from "react";
|
||||
import Article from "~/components/common/article";
|
||||
import { CupFill, ShareFill, ThumbUpFill } from "~/components/common/icons";
|
||||
import { siteTitle } from "~/utils";
|
||||
|
||||
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
||||
@@ -27,15 +29,52 @@ export async function loader({ params }: LoaderFunctionArgs) {
|
||||
export default function Detail() {
|
||||
const note = useLoaderData<typeof loader>();
|
||||
|
||||
const [likes, setLikes] = useState(note.data.likes);
|
||||
|
||||
const onLike = () => {
|
||||
setLikes((prevLike) => prevLike + 1);
|
||||
}
|
||||
|
||||
const onShare = () => {
|
||||
const shareData = {
|
||||
title: note.data.title,
|
||||
text: note.data.except,
|
||||
url: `${location.protocol}//${location.host}${location.pathname}`,
|
||||
};
|
||||
|
||||
if ("share" in navigator) {
|
||||
navigator.share(shareData);
|
||||
}
|
||||
else {
|
||||
alert("当前操作系统尚未实现此 API");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="px-2 py-24 max-w-3xl mx-auto">
|
||||
<section className="mb-12">
|
||||
<h1 className="text-center text-5xl/tight md:text-7xl/tight mb-4">{note.data.title}</h1>
|
||||
<h1 className="text-center text-5xl/tight md:text-7xl/tight mb-4" style={{ viewTransitionName: `note-title-${note.data.id}` }}>
|
||||
{note.data.title}
|
||||
</h1>
|
||||
<p className="text-center opacity-60">{note.data.date}</p>
|
||||
</section>
|
||||
<section className="p-5 bg-white rounded-xl border-b-4 border-b-cyan-200">
|
||||
<section className="p-5 bg-white rounded-xl border-b-4 border-b-cyan-200 mb-8">
|
||||
<Article html={note.data.content_html} />
|
||||
</section>
|
||||
<section className="flex gap-16 items-center justify-center text-base md:text-lg">
|
||||
<button onClick={onLike}>
|
||||
<ThumbUpFill className="inline-block h-8 w-8 mr-2" />
|
||||
{likes}
|
||||
</button>
|
||||
<button onClick={onShare}>
|
||||
<ShareFill className="inline-block h-8 w-8 mr-2" />
|
||||
分享
|
||||
</button>
|
||||
<button>
|
||||
<CupFill className="inline-block h-8 w-8 mr-2" />
|
||||
打赏
|
||||
</button>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { ChangeEvent, useEffect } from "react";
|
||||
import { Link, useLoaderData, useNavigate, useSearchParams } from "@remix-run/react";
|
||||
import { ChangeEvent, useEffect, useState } from "react";
|
||||
import { Link, unstable_useViewTransitionState, useLoaderData, useNavigate, useSearchParams } from "@remix-run/react";
|
||||
import { json, LoaderFunctionArgs, type MetaFunction } from "@remix-run/node";
|
||||
import Pagination from "~/components/common/pagination";
|
||||
import { clsn, siteTitle } from "~/utils";
|
||||
import { getFirstImage, years } from "~/utils/note";
|
||||
import { StarFill, ThumbUpFill } from "~/components/common/icons";
|
||||
|
||||
export const meta: MetaFunction = () => {
|
||||
return [
|
||||
@@ -61,11 +62,21 @@ export default function Note() {
|
||||
key={item.id}
|
||||
className="block group relative overflow-hidden p-5 bg-white rounded-xl mb-8 last:mb-0 border-4 border-transparent hover:border-pink-400 transition-colors border-b-cyan-200"
|
||||
to={`/note/${year}/${item.id}`}
|
||||
unstable_viewTransition
|
||||
>
|
||||
<h2 className="text-pink-400 text-2xl font-bold mb-4">{item.title}</h2>
|
||||
<p className={clsn(cover && "mr-40", "mb-8")}>{item.except}</p>
|
||||
{item.starred && (
|
||||
<StarFill className="absolute -top-5 -right-5 w-28 h-28 text-yellow-300 text-opacity-20 -rotate-[23deg]" />
|
||||
)}
|
||||
<h2 className="text-pink-400 text-2xl font-bold mb-4" style={{ viewTransitionName: `note-title-${item.id}` }}>
|
||||
{item.title}
|
||||
</h2>
|
||||
<p className={clsn(cover && "mr-40", "mb-8 relative")}>{item.except}</p>
|
||||
<div className="flex items-end justify-between text-sm">
|
||||
<p className="opacity-60">{item.date}</p>
|
||||
<span className="flex items-center opacity-60">
|
||||
<ThumbUpFill className="h-4 w-4 mr-1" />
|
||||
{item.likes}
|
||||
</span>
|
||||
</div>
|
||||
{cover && (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user