✨ 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)
|
authorId := c.QueryInt("authorId", 0)
|
||||||
|
|
||||||
tx := database.C.
|
tx := database.C.
|
||||||
Where(&models.Post{RealmID: nil}).
|
Where("realm_id IS NULL").
|
||||||
Where("published_at <= ? OR published_at IS NULL", time.Now()).
|
Where("published_at <= ? OR published_at IS NULL", time.Now()).
|
||||||
Order("created_at desc")
|
Order("created_at desc")
|
||||||
|
|
||||||
@ -55,6 +55,7 @@ func createPost(c *fiber.Ctx) error {
|
|||||||
Categories []models.Category `json:"categories"`
|
Categories []models.Category `json:"categories"`
|
||||||
Attachments []models.Attachment `json:"attachments"`
|
Attachments []models.Attachment `json:"attachments"`
|
||||||
PublishedAt *time.Time `json:"published_at"`
|
PublishedAt *time.Time `json:"published_at"`
|
||||||
|
RealmID *uint `json:"realm_id"`
|
||||||
RepostTo uint `json:"repost_to"`
|
RepostTo uint `json:"repost_to"`
|
||||||
ReplyTo uint `json:"reply_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(
|
post, err := services.NewPost(
|
||||||
user,
|
user,
|
||||||
|
realm,
|
||||||
data.Alias,
|
data.Alias,
|
||||||
data.Title,
|
data.Title,
|
||||||
data.Content,
|
data.Content,
|
||||||
|
@ -9,10 +9,32 @@ import (
|
|||||||
"time"
|
"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)
|
user := c.Locals("principal").(models.Account)
|
||||||
|
|
||||||
realms, err := services.ListRealms(user)
|
realms, err := services.ListRealmWithUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,9 @@ func NewServer() {
|
|||||||
api.Put("/posts/:postId", auth, editPost)
|
api.Put("/posts/:postId", auth, editPost)
|
||||||
api.Delete("/posts/:postId", auth, deletePost)
|
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.Get("/realms/:realmId/posts", listPostInRealm)
|
||||||
api.Post("/realms", auth, createRealm)
|
api.Post("/realms", auth, createRealm)
|
||||||
api.Put("/realms/:realmId", auth, editRealm)
|
api.Put("/realms/:realmId", auth, editRealm)
|
||||||
|
@ -77,30 +77,6 @@ WHERE t.id IN (?)`, prefix, prefix, prefix), postIds).Scan(&reactInfo)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewPost(
|
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,
|
user models.Account,
|
||||||
realm *models.Realm,
|
realm *models.Realm,
|
||||||
alias, title, content string,
|
alias, title, content string,
|
||||||
|
@ -5,7 +5,16 @@ import (
|
|||||||
"code.smartsheep.studio/hydrogen/interactive/pkg/models"
|
"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
|
var realms []models.Realm
|
||||||
if err := database.C.Where(&models.Realm{AccountID: user.ID}).Find(&realms).Error; err != nil {
|
if err := database.C.Where(&models.Realm{AccountID: user.ID}).Find(&realms).Error; err != nil {
|
||||||
return realms, err
|
return realms, err
|
||||||
|
@ -3,7 +3,7 @@ import { createSignal, Show } from "solid-js";
|
|||||||
import styles from "./NameCard.module.css";
|
import styles from "./NameCard.module.css";
|
||||||
import { getAtk } from "../stores/userinfo.tsx";
|
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 [info, setInfo] = createSignal<any>(null);
|
||||||
const [isFollowing, setIsFollowing] = createSignal(false);
|
const [isFollowing, setIsFollowing] = createSignal(false);
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ export default function PostPublish(props: {
|
|||||||
replying?: any,
|
replying?: any,
|
||||||
reposting?: any,
|
reposting?: any,
|
||||||
editing?: any,
|
editing?: any,
|
||||||
|
realmId?: number,
|
||||||
onReset: () => void,
|
onReset: () => void,
|
||||||
onError: (message: string | null) => void,
|
onError: (message: string | null) => void,
|
||||||
onPost: () => void
|
onPost: () => void
|
||||||
@ -48,6 +49,7 @@ export default function PostPublish(props: {
|
|||||||
attachments: attachments(),
|
attachments: attachments(),
|
||||||
categories: categories(),
|
categories: categories(),
|
||||||
tags: tags(),
|
tags: tags(),
|
||||||
|
realm_id: props.realmId,
|
||||||
published_at: data.published_at ? new Date(data.published_at as string) : new Date(),
|
published_at: data.published_at ? new Date(data.published_at as string) : new Date(),
|
||||||
repost_to: props.reposting?.id,
|
repost_to: props.reposting?.id,
|
||||||
reply_to: props.replying?.id
|
reply_to: props.replying?.id
|
||||||
@ -85,6 +87,7 @@ export default function PostPublish(props: {
|
|||||||
attachments: attachments(),
|
attachments: attachments(),
|
||||||
categories: categories(),
|
categories: categories(),
|
||||||
tags: tags(),
|
tags: tags(),
|
||||||
|
realm_id: props.realmId,
|
||||||
published_at: data.published_at ? new Date(data.published_at as string) : new Date()
|
published_at: data.published_at ? new Date(data.published_at as string) : new Date()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -22,6 +22,8 @@ render(() => (
|
|||||||
<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="/" 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 path="/accounts/:accountId" component={lazy(() => import("./pages/account.tsx"))} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/auth" component={lazy(() => import("./pages/auth/callout.tsx"))} />
|
<Route path="/auth" component={lazy(() => import("./pages/auth/callout.tsx"))} />
|
||||||
|
@ -11,8 +11,7 @@ interface MenuItem {
|
|||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
const nav: MenuItem[] = [
|
const nav: MenuItem[] = [
|
||||||
{ label: "Feed", href: "/" },
|
{ label: "Feed", href: "/" }
|
||||||
{ label: "Realms", href: "/realms" }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const wellKnown = useWellKnown();
|
const wellKnown = useWellKnown();
|
||||||
|
@ -64,7 +64,7 @@ export default function DashboardPage() {
|
|||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NameCard accountId={parseInt(params["accountId"])} onError={setError} />
|
<NameCard accountId={params["accountId"]} onError={setError} />
|
||||||
|
|
||||||
|
|
||||||
<dialog id="post-publish" class="modal">
|
<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