✨ Realm directory
This commit is contained in:
parent
0c8afb2bae
commit
22ad495308
@ -18,7 +18,7 @@ func listPost(c *fiber.Ctx) error {
|
||||
authorId := c.QueryInt("authorId", 0)
|
||||
|
||||
tx := database.C.
|
||||
Where(&models.Post{RealmID: nil}).
|
||||
Where("realm_id IS NULL").
|
||||
Where("published_at <= ? OR published_at IS NULL", time.Now()).
|
||||
Order("created_at desc")
|
||||
|
||||
@ -55,6 +55,7 @@ func createPost(c *fiber.Ctx) error {
|
||||
Categories []models.Category `json:"categories"`
|
||||
Attachments []models.Attachment `json:"attachments"`
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
RealmID *uint `json:"realm_id"`
|
||||
RepostTo uint `json:"repost_to"`
|
||||
ReplyTo uint `json:"reply_to"`
|
||||
}
|
||||
@ -90,8 +91,18 @@ func createPost(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
var realm *models.Realm
|
||||
if data.RealmID != nil {
|
||||
if err := database.C.Where(&models.Realm{
|
||||
BaseModel: models.BaseModel{ID: *data.RealmID},
|
||||
}).First(&realm).Error; err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
post, err := services.NewPost(
|
||||
user,
|
||||
realm,
|
||||
data.Alias,
|
||||
data.Title,
|
||||
data.Content,
|
||||
|
@ -9,10 +9,32 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func listRealms(c *fiber.Ctx) error {
|
||||
func getRealm(c *fiber.Ctx) error {
|
||||
id, _ := c.ParamsInt("realmId", 0)
|
||||
|
||||
var realm models.Realm
|
||||
if err := database.C.Where(&models.Realm{
|
||||
BaseModel: models.BaseModel{ID: uint(id)},
|
||||
}).First(&realm).Error; err != nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
return c.JSON(realm)
|
||||
}
|
||||
|
||||
func listRealm(c *fiber.Ctx) error {
|
||||
realms, err := services.ListRealm()
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
return c.JSON(realms)
|
||||
}
|
||||
|
||||
func listOwnedRealm(c *fiber.Ctx) error {
|
||||
user := c.Locals("principal").(models.Account)
|
||||
|
||||
realms, err := services.ListRealms(user)
|
||||
realms, err := services.ListRealmWithUser(user)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
@ -74,7 +74,9 @@ func NewServer() {
|
||||
api.Put("/posts/:postId", auth, editPost)
|
||||
api.Delete("/posts/:postId", auth, deletePost)
|
||||
|
||||
api.Get("/realms", auth, listRealms)
|
||||
api.Get("/realms", listRealm)
|
||||
api.Get("/realms/me", auth, listOwnedRealm)
|
||||
api.Get("/realms/:realmId", getRealm)
|
||||
api.Get("/realms/:realmId/posts", listPostInRealm)
|
||||
api.Post("/realms", auth, createRealm)
|
||||
api.Put("/realms/:realmId", auth, editRealm)
|
||||
|
@ -77,30 +77,6 @@ WHERE t.id IN (?)`, prefix, prefix, prefix), postIds).Scan(&reactInfo)
|
||||
}
|
||||
|
||||
func NewPost(
|
||||
user models.Account,
|
||||
alias, title, content string,
|
||||
attachments []models.Attachment,
|
||||
categories []models.Category,
|
||||
tags []models.Tag,
|
||||
publishedAt *time.Time,
|
||||
replyTo, repostTo *uint,
|
||||
) (models.Post, error) {
|
||||
return NewPostWithRealm(
|
||||
user,
|
||||
nil,
|
||||
alias,
|
||||
title,
|
||||
content,
|
||||
attachments,
|
||||
categories,
|
||||
tags,
|
||||
publishedAt,
|
||||
replyTo,
|
||||
repostTo,
|
||||
)
|
||||
}
|
||||
|
||||
func NewPostWithRealm(
|
||||
user models.Account,
|
||||
realm *models.Realm,
|
||||
alias, title, content string,
|
||||
|
@ -5,7 +5,16 @@ import (
|
||||
"code.smartsheep.studio/hydrogen/interactive/pkg/models"
|
||||
)
|
||||
|
||||
func ListRealms(user models.Account) ([]models.Realm, error) {
|
||||
func ListRealm() ([]models.Realm, error) {
|
||||
var realms []models.Realm
|
||||
if err := database.C.Find(&realms).Error; err != nil {
|
||||
return realms, err
|
||||
}
|
||||
|
||||
return realms, nil
|
||||
}
|
||||
|
||||
func ListRealmWithUser(user models.Account) ([]models.Realm, error) {
|
||||
var realms []models.Realm
|
||||
if err := database.C.Where(&models.Realm{AccountID: user.ID}).Find(&realms).Error; err != nil {
|
||||
return realms, err
|
||||
|
@ -3,7 +3,7 @@ import { createSignal, Show } from "solid-js";
|
||||
import styles from "./NameCard.module.css";
|
||||
import { getAtk } from "../stores/userinfo.tsx";
|
||||
|
||||
export default function NameCard(props: { accountId: number, onError: (messasge: string | null) => void }) {
|
||||
export default function NameCard(props: { accountId: string, onError: (messasge: string | null) => void }) {
|
||||
const [info, setInfo] = createSignal<any>(null);
|
||||
const [isFollowing, setIsFollowing] = createSignal(false);
|
||||
|
||||
|
@ -8,6 +8,7 @@ export default function PostPublish(props: {
|
||||
replying?: any,
|
||||
reposting?: any,
|
||||
editing?: any,
|
||||
realmId?: number,
|
||||
onReset: () => void,
|
||||
onError: (message: string | null) => void,
|
||||
onPost: () => void
|
||||
@ -48,6 +49,7 @@ export default function PostPublish(props: {
|
||||
attachments: attachments(),
|
||||
categories: categories(),
|
||||
tags: tags(),
|
||||
realm_id: props.realmId,
|
||||
published_at: data.published_at ? new Date(data.published_at as string) : new Date(),
|
||||
repost_to: props.reposting?.id,
|
||||
reply_to: props.replying?.id
|
||||
@ -85,6 +87,7 @@ export default function PostPublish(props: {
|
||||
attachments: attachments(),
|
||||
categories: categories(),
|
||||
tags: tags(),
|
||||
realm_id: props.realmId,
|
||||
published_at: data.published_at ? new Date(data.published_at as string) : new Date()
|
||||
})
|
||||
});
|
||||
|
@ -22,6 +22,8 @@ render(() => (
|
||||
<Router root={RootLayout}>
|
||||
<Route path="/" component={lazy(() => import("./pages/feed.tsx"))}>
|
||||
<Route path="/" component={lazy(() => import("./pages/global.tsx"))} />
|
||||
<Route path="/realms" component={lazy(() => import("./pages/realms.tsx"))} />
|
||||
<Route path="/realms/:realmId" component={lazy(() => import("./pages/realm.tsx"))} />
|
||||
<Route path="/accounts/:accountId" component={lazy(() => import("./pages/account.tsx"))} />
|
||||
</Route>
|
||||
<Route path="/auth" component={lazy(() => import("./pages/auth/callout.tsx"))} />
|
||||
|
@ -11,8 +11,7 @@ interface MenuItem {
|
||||
|
||||
export default function Navbar() {
|
||||
const nav: MenuItem[] = [
|
||||
{ label: "Feed", href: "/" },
|
||||
{ label: "Realms", href: "/realms" }
|
||||
{ label: "Feed", href: "/" }
|
||||
];
|
||||
|
||||
const wellKnown = useWellKnown();
|
||||
|
@ -64,7 +64,7 @@ export default function DashboardPage() {
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<NameCard accountId={parseInt(params["accountId"])} onError={setError} />
|
||||
<NameCard accountId={params["accountId"]} onError={setError} />
|
||||
|
||||
|
||||
<dialog id="post-publish" class="modal">
|
||||
|
3
pkg/view/src/pages/realm.module.css
Normal file
3
pkg/view/src/pages/realm.module.css
Normal file
@ -0,0 +1,3 @@
|
||||
.description {
|
||||
color: var(--fallback-bc, oklch(var(--bc)/.8));
|
||||
}
|
106
pkg/view/src/pages/realm.tsx
Normal file
106
pkg/view/src/pages/realm.tsx
Normal file
@ -0,0 +1,106 @@
|
||||
import { createSignal, Show } from "solid-js";
|
||||
import { createStore } from "solid-js/store";
|
||||
import { useParams } from "@solidjs/router";
|
||||
|
||||
import PostList from "../components/PostList.tsx";
|
||||
import PostPublish from "../components/PostPublish.tsx";
|
||||
|
||||
import styles from "./realm.module.css";
|
||||
|
||||
export default function RealmPage() {
|
||||
const [error, setError] = createSignal<string | null>(null);
|
||||
|
||||
const [realm, setRealm] = createSignal<any>(null);
|
||||
const [page, setPage] = createSignal(0);
|
||||
const [info, setInfo] = createSignal<any>(null);
|
||||
|
||||
const params = useParams();
|
||||
|
||||
async function readRealm() {
|
||||
const res = await fetch(`/api/realms/${params["realmId"]}`);
|
||||
if (res.status !== 200) {
|
||||
setError(await res.text());
|
||||
} else {
|
||||
setRealm(await res.json());
|
||||
}
|
||||
}
|
||||
|
||||
readRealm();
|
||||
|
||||
async function readPosts(pn?: number) {
|
||||
if (pn) setPage(pn);
|
||||
const res = await fetch(`/api/realms/${params["realmId"]}/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());
|
||||
}
|
||||
}
|
||||
|
||||
function setMeta(data: any, field: string, scroll = true) {
|
||||
const meta: { [id: string]: any } = {
|
||||
reposting: null,
|
||||
replying: null,
|
||||
editing: null
|
||||
};
|
||||
meta[field] = data;
|
||||
setPublishMeta(meta);
|
||||
|
||||
if (scroll) window.scroll({ top: 0, behavior: "smooth" });
|
||||
}
|
||||
|
||||
const [publishMeta, setPublishMeta] = createStore<any>({
|
||||
replying: null,
|
||||
reposting: null,
|
||||
editing: null
|
||||
});
|
||||
|
||||
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>
|
||||
|
||||
<div class="px-7 pt-7 pb-5">
|
||||
<h2 class="text-2xl font-bold">{realm()?.name}</h2>
|
||||
<p>{realm()?.description}</p>
|
||||
|
||||
<div class={`${styles.description} text-sm mt-3`}>
|
||||
<p>Realm #{realm()?.id}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PostPublish
|
||||
realmId={parseInt(params["realmId"])}
|
||||
replying={publishMeta.replying}
|
||||
reposting={publishMeta.reposting}
|
||||
editing={publishMeta.editing}
|
||||
onReset={() => setMeta(null, "none", false)}
|
||||
onPost={() => readPosts()}
|
||||
onError={setError}
|
||||
/>
|
||||
|
||||
<PostList
|
||||
info={info()}
|
||||
onUpdate={readPosts}
|
||||
onError={setError}
|
||||
onRepost={(item) => setMeta(item, "reposting")}
|
||||
onReply={(item) => setMeta(item, "replying")}
|
||||
onEdit={(item) => setMeta(item, "editing")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
46
pkg/view/src/pages/realms.tsx
Normal file
46
pkg/view/src/pages/realms.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { createSignal, For, Show } from "solid-js";
|
||||
|
||||
export default function RealmDirectoryPage() {
|
||||
const [error, setError] = createSignal<string | null>(null);
|
||||
|
||||
const [realms, setRealms] = createSignal<any>(null);
|
||||
|
||||
async function readRealms() {
|
||||
const res = await fetch(`/api/realms`);
|
||||
if (res.status !== 200) {
|
||||
setError(await res.text());
|
||||
} else {
|
||||
setRealms(await res.json());
|
||||
}
|
||||
}
|
||||
|
||||
readRealms();
|
||||
|
||||
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>
|
||||
|
||||
<For each={realms()}>
|
||||
{item => <div class="px-7 pt-7 pb-5 border-t border-base-200">
|
||||
<h2 class="text-xl font-bold">{item.name}</h2>
|
||||
<p>{item.description}</p>
|
||||
|
||||
<div class="mt-2">
|
||||
<a href={`/realms/${item.id}`} class="link">Jump in</a>
|
||||
</div>
|
||||
</div>}
|
||||
</For>
|
||||
</>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user