add game
This commit is contained in:
parent
4fa3212bdd
commit
e7471b20e4
@ -0,0 +1,4 @@
|
|||||||
|
<Button asChild>
|
||||||
|
<Link href="/post/game">Game</Link>
|
||||||
|
</Button>
|
||||||
|
|
@ -0,0 +1,26 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { DID } from "@/lib/data/atproto/did";
|
||||||
|
import { getVerifiedHandle } from "@/lib/data/atproto/identity";
|
||||||
|
import { putPost } from "@/lib/data/atproto/game";
|
||||||
|
import { uncached_doesPostExist } from "@/lib/data/db/post";
|
||||||
|
import { DataLayerError } from "@/lib/data/error";
|
||||||
|
import { ensureUser } from "@/lib/data/user";
|
||||||
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
export async function newPostAction(_prevState: unknown) {
|
||||||
|
"use server";
|
||||||
|
const user = await ensureUser();
|
||||||
|
const [handle] = await Promise.all([
|
||||||
|
getVerifiedHandle(user.did),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const account = `at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/${handle}`;
|
||||||
|
try {
|
||||||
|
const { rkey } = await putPost({ account });
|
||||||
|
redirect(`https://at.syu.is/at/${user.did}/ai.syui.game/self`);
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof DataLayerError)) throw error;
|
||||||
|
return { error: "Failed to create post" };
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { startTransition, useActionState, useId, useState } from "react";
|
||||||
|
import { newPostAction } from "./_action";
|
||||||
|
import { Label } from "@/lib/components/ui/label";
|
||||||
|
import { Input } from "@/lib/components/ui/input";
|
||||||
|
import { Button } from "@/lib/components/ui/button";
|
||||||
|
import { Alert, AlertDescription, AlertTitle } from "@/lib/components/ui/alert";
|
||||||
|
import { Spinner } from "@/lib/components/ui/spinner";
|
||||||
|
import { InputLengthIndicator } from "@/lib/components/input-length-indicator";
|
||||||
|
|
||||||
|
export function NewPostButton() {
|
||||||
|
const [state, action, isPending] = useActionState(newPostAction, null);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const handleClick = async () => {
|
||||||
|
try {
|
||||||
|
const result = await action({});
|
||||||
|
if (result && 'error' in result) {
|
||||||
|
setError(result.error);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error creating post:', e);
|
||||||
|
setError('予期しないエラーが発生しました');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button onClick={handleClick} disabled={isPending}>
|
||||||
|
{isPending ? '投稿中...' : '新規投稿'}
|
||||||
|
</button>
|
||||||
|
{error && <p style={{ color: 'red' }}>{error}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
import { Metadata } from "next";
|
||||||
|
import { NewPostButton } from "./_client";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "New post | Frontpage",
|
||||||
|
robots: "noindex, nofollow",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function NewPost() {
|
||||||
|
return (
|
||||||
|
<main className="flex flex-col gap-3">
|
||||||
|
<h2 className="text-3xl font-bold tracking-tight text-gray-900 dark:text-gray-100">
|
||||||
|
</h2>
|
||||||
|
<NewPostButton />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
import "server-only";
|
||||||
|
import {
|
||||||
|
atprotoPutRecord,
|
||||||
|
atprotoCreateRecord,
|
||||||
|
atprotoDeleteRecord,
|
||||||
|
atprotoGetRecord,
|
||||||
|
} from "./record";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { DataLayerError } from "../error";
|
||||||
|
import { DID, getPdsUrl } from "./did";
|
||||||
|
|
||||||
|
export const PostCollection = "ai.syui.game";
|
||||||
|
|
||||||
|
export const PostRecord = z.object({
|
||||||
|
account: z.string(),
|
||||||
|
createdAt: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Post = z.infer<typeof PostRecord>;
|
||||||
|
|
||||||
|
type PostInput = {
|
||||||
|
account: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function putPost({ account }: PostInput) {
|
||||||
|
const record = { account, createdAt: new Date().toISOString() };
|
||||||
|
PostRecord.parse(record);
|
||||||
|
|
||||||
|
const result = await atprotoPutRecord({
|
||||||
|
rkey: "self",
|
||||||
|
record,
|
||||||
|
collection: PostCollection,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
rkey: "self",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deletePost(rkey: string) {
|
||||||
|
await atprotoDeleteRecord({
|
||||||
|
rkey,
|
||||||
|
collection: PostCollection,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPost({ rkey, repo }: { rkey: string; repo: DID }) {
|
||||||
|
const service = await getPdsUrl(repo);
|
||||||
|
|
||||||
|
if (!service) {
|
||||||
|
throw new DataLayerError("Failed to get service url");
|
||||||
|
}
|
||||||
|
|
||||||
|
const { value } = await atprotoGetRecord({
|
||||||
|
serviceEndpoint: service,
|
||||||
|
repo,
|
||||||
|
collection: PostCollection,
|
||||||
|
rkey,
|
||||||
|
});
|
||||||
|
|
||||||
|
return PostRecord.parse(value);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user