✨ Personal Page
This commit is contained in:
		| @@ -14,6 +14,8 @@ type Post struct { | |||||||
| 	DislikedAccounts []PostDislike `json:"disliked_accounts"` | 	DislikedAccounts []PostDislike `json:"disliked_accounts"` | ||||||
| 	RepostTo         *Post         `json:"repost_to" gorm:"foreignKey:RepostID"` | 	RepostTo         *Post         `json:"repost_to" gorm:"foreignKey:RepostID"` | ||||||
| 	ReplyTo          *Post         `json:"reply_to" gorm:"foreignKey:ReplyID"` | 	ReplyTo          *Post         `json:"reply_to" gorm:"foreignKey:ReplyID"` | ||||||
|  | 	PinnedAt         *time.Time    `json:"pinned_at"` | ||||||
|  | 	EditedAt         *time.Time    `json:"edited_at"` | ||||||
| 	PublishedAt      time.Time     `json:"published_at"` | 	PublishedAt      time.Time     `json:"published_at"` | ||||||
| 	RepostID         *uint         `json:"repost_id"` | 	RepostID         *uint         `json:"repost_id"` | ||||||
| 	ReplyID          *uint         `json:"reply_id"` | 	ReplyID          *uint         `json:"reply_id"` | ||||||
|   | |||||||
| @@ -13,16 +13,22 @@ import ( | |||||||
| func listPost(c *fiber.Ctx) error { | func listPost(c *fiber.Ctx) error { | ||||||
| 	take := c.QueryInt("take", 0) | 	take := c.QueryInt("take", 0) | ||||||
| 	offset := c.QueryInt("offset", 0) | 	offset := c.QueryInt("offset", 0) | ||||||
|  | 	authorId := c.QueryInt("authorId", 0) | ||||||
|  |  | ||||||
|  | 	tx := database.C.Where(&models.Post{RealmID: nil}).Order("created_at desc") | ||||||
|  |  | ||||||
|  | 	if authorId > 0 { | ||||||
|  | 		tx = tx.Where(&models.Post{AuthorID: uint(authorId)}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	var count int64 | 	var count int64 | ||||||
| 	if err := database.C. | 	if err := tx. | ||||||
| 		Where(&models.Post{RealmID: nil}). |  | ||||||
| 		Model(&models.Post{}). | 		Model(&models.Post{}). | ||||||
| 		Count(&count).Error; err != nil { | 		Count(&count).Error; err != nil { | ||||||
| 		return fiber.NewError(fiber.StatusInternalServerError, err.Error()) | 		return fiber.NewError(fiber.StatusInternalServerError, err.Error()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	posts, err := services.ListPost(take, offset) | 	posts, err := services.ListPost(tx, take, offset) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -57,7 +57,8 @@ func NewServer() { | |||||||
| 	api := A.Group("/api").Name("API") | 	api := A.Group("/api").Name("API") | ||||||
| 	{ | 	{ | ||||||
| 		api.Get("/users/me", auth, getUserinfo) | 		api.Get("/users/me", auth, getUserinfo) | ||||||
| 		api.Get("/users/:accountId/follow", auth, doFollowAccount) | 		api.Get("/users/:accountId", getOthersInfo) | ||||||
|  | 		api.Post("/users/:accountId/follow", auth, doFollowAccount) | ||||||
|  |  | ||||||
| 		api.Get("/auth", doLogin) | 		api.Get("/auth", doLogin) | ||||||
| 		api.Get("/auth/callback", doPostLogin) | 		api.Get("/auth/callback", doPostLogin) | ||||||
|   | |||||||
| @@ -20,6 +20,19 @@ func getUserinfo(c *fiber.Ctx) error { | |||||||
| 	return c.JSON(data) | 	return c.JSON(data) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func getOthersInfo(c *fiber.Ctx) error { | ||||||
|  | 	accountId, _ := c.ParamsInt("accountId", 0) | ||||||
|  |  | ||||||
|  | 	var data models.Account | ||||||
|  | 	if err := database.C. | ||||||
|  | 		Where(&models.Account{BaseModel: models.BaseModel{ID: uint(accountId)}}). | ||||||
|  | 		First(&data).Error; err != nil { | ||||||
|  | 		return fiber.NewError(fiber.StatusInternalServerError, err.Error()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return c.JSON(data) | ||||||
|  | } | ||||||
|  |  | ||||||
| func doFollowAccount(c *fiber.Ctx) error { | func doFollowAccount(c *fiber.Ctx) error { | ||||||
| 	user := c.Locals("principal").(models.Account) | 	user := c.Locals("principal").(models.Account) | ||||||
| 	id, _ := c.ParamsInt("accountId", 0) | 	id, _ := c.ParamsInt("accountId", 0) | ||||||
|   | |||||||
| @@ -10,13 +10,11 @@ import ( | |||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func ListPost(take int, offset int) ([]*models.Post, error) { | func ListPost(tx *gorm.DB, take int, offset int) ([]*models.Post, error) { | ||||||
| 	var posts []*models.Post | 	var posts []*models.Post | ||||||
| 	if err := database.C. | 	if err := tx. | ||||||
| 		Where(&models.Post{RealmID: nil}). |  | ||||||
| 		Limit(take). | 		Limit(take). | ||||||
| 		Offset(offset). | 		Offset(offset). | ||||||
| 		Order("created_at desc"). |  | ||||||
| 		Preload("Author"). | 		Preload("Author"). | ||||||
| 		Find(&posts).Error; err != nil { | 		Find(&posts).Error; err != nil { | ||||||
| 		return posts, err | 		return posts, err | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								pkg/view/src/components/NameCard.module.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								pkg/view/src/components/NameCard.module.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | .description { | ||||||
|  |     color: var(--fallback-bc, oklch(var(--bc)/.8)); | ||||||
|  | } | ||||||
							
								
								
									
										58
									
								
								pkg/view/src/components/NameCard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								pkg/view/src/components/NameCard.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | import { createSignal } from "solid-js"; | ||||||
|  |  | ||||||
|  | import styles from "./NameCard.module.css"; | ||||||
|  |  | ||||||
|  | export default function NameCard(props: { accountId: number, onError: (messasge: string | null) => void }) { | ||||||
|  |   const [info, setInfo] = createSignal<any>(null); | ||||||
|  |  | ||||||
|  |   const [_, setLoading] = createSignal(true); | ||||||
|  |  | ||||||
|  |   async function readInfo() { | ||||||
|  |     setLoading(true); | ||||||
|  |     const res = await fetch(`/api/users/${props.accountId}`); | ||||||
|  |     if (res.status !== 200) { | ||||||
|  |       props.onError(await res.text()); | ||||||
|  |     } else { | ||||||
|  |       setInfo(await res.json()); | ||||||
|  |       props.onError(null); | ||||||
|  |     } | ||||||
|  |     setLoading(false); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   readInfo(); | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div class="relative"> | ||||||
|  |       <figure id="banner"> | ||||||
|  |         <img class="object-cover w-full h-36" src="https://images.unsplash.com/photo-1464822759023-fed622ff2c3b" | ||||||
|  |              alt="banner" /> | ||||||
|  |       </figure> | ||||||
|  |  | ||||||
|  |       <div id="avatar" class="avatar absolute border-4 border-base-200 left-[20px] top-[4.5rem]"> | ||||||
|  |         <div class="w-24"> | ||||||
|  |           <img src={info()?.avatar} alt="avatar" /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <div id="actions" class="flex justify-end"> | ||||||
|  |         <div> | ||||||
|  |           <button type="button" class="btn btn-primary"> | ||||||
|  |             <i class="fa-solid fa-plus"></i> | ||||||
|  |             Follow | ||||||
|  |           </button> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <div id="description" class="px-6 pb-7"> | ||||||
|  |         <h2 class="text-2xl font-bold">{info()?.name}</h2> | ||||||
|  |         <p class="text-md">{info()?.description}</p> | ||||||
|  |         <div class={`mt-2 ${styles.description}`}> | ||||||
|  |           <p class="text-xs"> | ||||||
|  |             <i class="fa-solid fa-calendar-days me-2"></i> | ||||||
|  |             Joined at {new Date(info()?.created_at).toLocaleString()} | ||||||
|  |           </p> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @@ -24,22 +24,24 @@ export default function PostItem(props: { post: any, onError: (message: string | | |||||||
|   return ( |   return ( | ||||||
|     <div class="post-item"> |     <div class="post-item"> | ||||||
|  |  | ||||||
|       <div class="flex bg-base-200"> |       <a href={`/accounts/${props.post.author.id}`}> | ||||||
|         <div class="avatar"> |         <div class="flex bg-base-200"> | ||||||
|           <div class="w-12"> |           <div class="avatar pl-[20px]"> | ||||||
|             <Show when={props.post.author.avatar} |             <div class="w-12"> | ||||||
|                   fallback={<span class="text-3xl">{props.post.author.name.substring(0, 1)}</span>}> |               <Show when={props.post.author.avatar} | ||||||
|               <img alt="avatar" src={props.post.author.avatar} /> |                     fallback={<span class="text-3xl">{props.post.author.name.substring(0, 1)}</span>}> | ||||||
|             </Show> |                 <img alt="avatar" src={props.post.author.avatar} /> | ||||||
|  |               </Show> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |           <div class="flex items-center px-5"> | ||||||
|  |             <div> | ||||||
|  |               <h3 class="font-bold text-sm">{props.post.author.name}</h3> | ||||||
|  |               <p class="text-xs">{props.post.author.description}</p> | ||||||
|  |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|         <div class="flex items-center px-5"> |       </a> | ||||||
|           <div> |  | ||||||
|             <h3 class="font-bold text-sm">{props.post.author.name}</h3> |  | ||||||
|             <p class="text-xs">{props.post.author.description}</p> |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|  |  | ||||||
|       <article class="py-5 px-7"> |       <article class="py-5 px-7"> | ||||||
|         <h2 class="card-title">{props.post.title}</h2> |         <h2 class="card-title">{props.post.title}</h2> | ||||||
| @@ -48,7 +50,7 @@ export default function PostItem(props: { post: any, onError: (message: string | | |||||||
|  |  | ||||||
|       <div class="grid grid-cols-3 border-y border-base-200"> |       <div class="grid grid-cols-3 border-y border-base-200"> | ||||||
|  |  | ||||||
|         <div class="col-span-2 grid grid-cols-4"> |         <div class="grid grid-cols-2"> | ||||||
|           <div class="tooltip" data-tip="Daisuki"> |           <div class="tooltip" data-tip="Daisuki"> | ||||||
|             <button type="button" class="btn btn-ghost btn-block" disabled={reacting()} |             <button type="button" class="btn btn-ghost btn-block" disabled={reacting()} | ||||||
|                     onClick={() => reactPost(props.post, "like")}> |                     onClick={() => reactPost(props.post, "like")}> | ||||||
| @@ -64,7 +66,9 @@ export default function PostItem(props: { post: any, onError: (message: string | | |||||||
|               <code class="font-mono">{props.post.dislike_count}</code> |               <code class="font-mono">{props.post.dislike_count}</code> | ||||||
|             </button> |             </button> | ||||||
|           </div> |           </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <div class="col-span-2 flex justify-end"> | ||||||
|           <div class="tooltip" data-tip="Reply"> |           <div class="tooltip" data-tip="Reply"> | ||||||
|             <button type="button" class="btn btn-ghost btn-block"> |             <button type="button" class="btn btn-ghost btn-block"> | ||||||
|               <i class="fa-solid fa-reply"></i> |               <i class="fa-solid fa-reply"></i> | ||||||
| @@ -76,9 +80,7 @@ export default function PostItem(props: { post: any, onError: (message: string | | |||||||
|               <i class="fa-solid fa-retweet"></i> |               <i class="fa-solid fa-retweet"></i> | ||||||
|             </button> |             </button> | ||||||
|           </div> |           </div> | ||||||
|         </div> |  | ||||||
|  |  | ||||||
|         <div class="flex justify-end"> |  | ||||||
|           <div class="dropdown dropdown-end"> |           <div class="dropdown dropdown-end"> | ||||||
|             <div tabIndex="0" role="button" class="btn btn-ghost w-12"> |             <div tabIndex="0" role="button" class="btn btn-ghost w-12"> | ||||||
|               <i class="fa-solid fa-ellipsis-vertical"></i> |               <i class="fa-solid fa-ellipsis-vertical"></i> | ||||||
|   | |||||||
| @@ -1,33 +1,24 @@ | |||||||
| import { createMemo, createSignal, For, Show } from "solid-js"; | import { createMemo, createSignal, For, Show } from "solid-js"; | ||||||
|  |  | ||||||
| import styles from "./PostList.module.css"; | import styles from "./PostList.module.css"; | ||||||
|  |  | ||||||
| import PostPublish from "./PostPublish.tsx"; |  | ||||||
| import PostItem from "./PostItem.tsx"; | import PostItem from "./PostItem.tsx"; | ||||||
|  |  | ||||||
| export default function PostList(props: { onError: (message: string | null) => void }) { | export default function PostList(props: { | ||||||
|  |   info: { data: any[], count: number } | null, | ||||||
|  |   onUpdate: (pn: number) => Promise<void>, | ||||||
|  |   onError: (message: string | null) => void | ||||||
|  | }) { | ||||||
|   const [loading, setLoading] = createSignal(true); |   const [loading, setLoading] = createSignal(true); | ||||||
|  |  | ||||||
|   const [posts, setPosts] = createSignal<any[]>([]); |   const posts = createMemo(() => props.info?.data) | ||||||
|   const [postCount, setPostCount] = createSignal(0); |   const postCount = createMemo<number>(() => props.info?.count ?? 0) | ||||||
|  |  | ||||||
|   const [page, setPage] = createSignal(1); |   const [page, setPage] = createSignal(1); | ||||||
|   const pageCount = createMemo(() => Math.ceil(postCount() / 10)); |   const pageCount = createMemo(() => Math.ceil(postCount() / 10)); | ||||||
|  |  | ||||||
|   async function readPosts() { |   async function readPosts() { | ||||||
|     setLoading(true); |     setLoading(true); | ||||||
|     const res = await fetch("/api/posts?" + new URLSearchParams({ |     await props.onUpdate(page()); | ||||||
|       take: (10).toString(), |  | ||||||
|       offset: ((page() - 1) * 10).toString() |  | ||||||
|     })); |  | ||||||
|     if (res.status !== 200) { |  | ||||||
|       props.onError(await res.text()); |  | ||||||
|     } else { |  | ||||||
|       const data = await res.json(); |  | ||||||
|       setPosts(data["data"]); |  | ||||||
|       setPostCount(data["count"]); |  | ||||||
|       props.onError(null); |  | ||||||
|     } |  | ||||||
|     setLoading(false); |     setLoading(false); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -42,8 +33,6 @@ export default function PostList(props: { onError: (message: string | null) => v | |||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div id="post-list"> |     <div id="post-list"> | ||||||
|       <PostPublish onPost={() => readPosts()} onError={props.onError} /> |  | ||||||
|  |  | ||||||
|       <div id="posts"> |       <div id="posts"> | ||||||
|         <For each={posts()}> |         <For each={posts()}> | ||||||
|           {item => <PostItem post={item} onReact={() => readPosts()} onError={props.onError} />} |           {item => <PostItem post={item} onReact={() => readPosts()} onError={props.onError} />} | ||||||
|   | |||||||
| @@ -20,7 +20,10 @@ render(() => ( | |||||||
|   <WellKnownProvider> |   <WellKnownProvider> | ||||||
|     <UserinfoProvider> |     <UserinfoProvider> | ||||||
|       <Router root={RootLayout}> |       <Router root={RootLayout}> | ||||||
|         <Route path="/" component={lazy(() => import("./pages/feed.tsx"))} /> |         <Route path="/" component={lazy(() => import("./pages/feed.tsx"))}> | ||||||
|  |           <Route path="/" component={lazy(() => import("./pages/global.tsx"))} /> | ||||||
|  |           <Route path="/accounts/:accountId" component={lazy(() => import("./pages/account.tsx"))} /> | ||||||
|  |         </Route> | ||||||
|         <Route path="/auth" component={lazy(() => import("./pages/auth/callout.tsx"))} /> |         <Route path="/auth" component={lazy(() => import("./pages/auth/callout.tsx"))} /> | ||||||
|         <Route path="/auth/callback" component={lazy(() => import("./pages/auth/callback.tsx"))} /> |         <Route path="/auth/callback" component={lazy(() => import("./pages/auth/callback.tsx"))} /> | ||||||
|       </Router> |       </Router> | ||||||
|   | |||||||
							
								
								
									
										50
									
								
								pkg/view/src/pages/account.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								pkg/view/src/pages/account.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | import { createSignal, Show } from "solid-js"; | ||||||
|  | import { useParams } from "@solidjs/router"; | ||||||
|  |  | ||||||
|  | import PostList from "../components/PostList.tsx"; | ||||||
|  | import NameCard from "../components/NameCard.tsx"; | ||||||
|  |  | ||||||
|  | export default function DashboardPage() { | ||||||
|  |   const [error, setError] = createSignal<string | null>(null); | ||||||
|  |  | ||||||
|  |   const [page, setPage] = createSignal(0); | ||||||
|  |   const [info, setInfo] = createSignal<any>(null); | ||||||
|  |  | ||||||
|  |   const params = useParams(); | ||||||
|  |  | ||||||
|  |   async function readPosts(pn?: number) { | ||||||
|  |     if (pn) setPage(pn); | ||||||
|  |     const res = await fetch("/api/posts?" + new URLSearchParams({ | ||||||
|  |       take: (10).toString(), | ||||||
|  |       offset: ((page() - 1) * 10).toString(), | ||||||
|  |       authorId: params["accountId"] | ||||||
|  |     })); | ||||||
|  |     if (res.status !== 200) { | ||||||
|  |       setError(await res.text()); | ||||||
|  |     } else { | ||||||
|  |       setError(null); | ||||||
|  |       setInfo(await res.json()); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <> | ||||||
|  |       <div id="alerts"> | ||||||
|  |         <Show when={error()}> | ||||||
|  |           <div role="alert" class="alert alert-error"> | ||||||
|  |             <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" | ||||||
|  |                  viewBox="0 0 24 24"> | ||||||
|  |               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | ||||||
|  |                     d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /> | ||||||
|  |             </svg> | ||||||
|  |             <span class="capitalize">{error()}</span> | ||||||
|  |           </div> | ||||||
|  |         </Show> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <NameCard accountId={parseInt(params["accountId"])} onError={setError} /> | ||||||
|  |  | ||||||
|  |       <PostList info={info()} onUpdate={readPosts} onError={setError} /> | ||||||
|  |     </> | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @@ -1,33 +1,12 @@ | |||||||
| import { createEffect, createSignal, For, Show } from "solid-js"; |  | ||||||
|  |  | ||||||
| import styles from "./feed.module.css"; | import styles from "./feed.module.css"; | ||||||
|  |  | ||||||
| import PostList from "../components/PostList.tsx"; | export default function DashboardPage(props: any) { | ||||||
|  |  | ||||||
| export default function DashboardPage() { |  | ||||||
|   const [error, setError] = createSignal<string | null>(null); |  | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div class={`${styles.wrapper} container mx-auto`}> |     <div class={`${styles.wrapper} container mx-auto`}> | ||||||
|       <div id="trending" class="card shadow-xl h-fit"></div> |       <div id="trending" class="card shadow-xl h-fit"></div> | ||||||
|  |  | ||||||
|       <div id="content" class="card shadow-xl"> |       <div id="content" class="card shadow-xl"> | ||||||
|  |         {props.children} | ||||||
|         <div id="alerts"> |  | ||||||
|           <Show when={error()}> |  | ||||||
|             <div role="alert" class="alert alert-error"> |  | ||||||
|               <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" |  | ||||||
|                    viewBox="0 0 24 24"> |  | ||||||
|                 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" |  | ||||||
|                       d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /> |  | ||||||
|               </svg> |  | ||||||
|               <span class="capitalize">{error()}</span> |  | ||||||
|             </div> |  | ||||||
|           </Show> |  | ||||||
|         </div> |  | ||||||
|          |  | ||||||
|         <PostList onError={setError} /> |  | ||||||
|  |  | ||||||
|       </div> |       </div> | ||||||
|  |  | ||||||
|       <div id="well-known" class="card shadow-xl h-fit"></div> |       <div id="well-known" class="card shadow-xl h-fit"></div> | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								pkg/view/src/pages/global.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								pkg/view/src/pages/global.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | import { createSignal, Show } from "solid-js"; | ||||||
|  |  | ||||||
|  | import PostList from "../components/PostList.tsx"; | ||||||
|  | import PostPublish from "../components/PostPublish.tsx"; | ||||||
|  |  | ||||||
|  | export default function DashboardPage() { | ||||||
|  |   const [error, setError] = createSignal<string | null>(null); | ||||||
|  |  | ||||||
|  |   const [page, setPage] = createSignal(0); | ||||||
|  |   const [info, setInfo] = createSignal<any>(null); | ||||||
|  |  | ||||||
|  |   async function readPosts(pn?: number) { | ||||||
|  |     if (pn) setPage(pn); | ||||||
|  |     const res = await fetch("/api/posts?" + new URLSearchParams({ | ||||||
|  |       take: (10).toString(), | ||||||
|  |       offset: ((page() - 1) * 10).toString() | ||||||
|  |     })); | ||||||
|  |     if (res.status !== 200) { | ||||||
|  |       setError(await res.text()); | ||||||
|  |     } else { | ||||||
|  |       setError(null); | ||||||
|  |       setInfo(await res.json()); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <> | ||||||
|  |       <div id="alerts"> | ||||||
|  |         <Show when={error()}> | ||||||
|  |           <div role="alert" class="alert alert-error"> | ||||||
|  |             <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" | ||||||
|  |                  viewBox="0 0 24 24"> | ||||||
|  |               <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | ||||||
|  |                     d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /> | ||||||
|  |             </svg> | ||||||
|  |             <span class="capitalize">{error()}</span> | ||||||
|  |           </div> | ||||||
|  |         </Show> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <PostPublish onPost={() => readPosts()} onError={setError} /> | ||||||
|  |  | ||||||
|  |       <PostList info={info()} onUpdate={readPosts} onError={setError} /> | ||||||
|  |     </> | ||||||
|  |   ); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user