Skip to content

Commit

Permalink
Merge pull request #36 from sora32127/fix_edit
Browse files Browse the repository at this point in the history
編集ページに関するエラーを修正
  • Loading branch information
sora32127 authored Apr 30, 2024
2 parents 66c351f + 0e73032 commit 211f3d4
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 139 deletions.
233 changes: 95 additions & 138 deletions app/routes/_layout.archives.edit.$postId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { H1, H2 } from "~/components/Headings";
import { ClientOnly } from "remix-utils/client-only";
import MarkdownEditor from "~/components/MarkdownEditor.client";
import { useState } from "react";
// @ts-expect-error : markedの型定義が存在しないため、anyとしている
import { marked } from 'marked';
import { getSession, requireUserId } from "~/modules/session.server";
import * as diff from 'diff';
import { createEmbedding } from "~/modules/embedding.server";
import TagSelectionBox from "~/components/SubmitFormComponents/TagSelectionBox";


export async function loader({ params, request }: LoaderFunctionArgs) {
Expand All @@ -32,33 +34,22 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
- nowEditingInfoが存在し、最終ロック時刻から30分以上経過している場合、編集中でない
- それ以外の場合は編集中と判断する。つまりは以下の場合:
- nowEditingInfoが存在する
- なおかつ、自分以外のユーザー名が格納されている
- なおかつ、自分以外のユーザーIDが格納されている
- なおかつ、lasteHeartBeatAtUTCから30分以内である
*/
let isEditing = nowEditingInfo ? new Date().getTime() - nowEditingInfo.lastHeartBeatAtUTC.getTime() <= 1000 * 60 * 30 : false;

if (nowEditingInfo){
const userName = await prisma.userProfiles.findUniqueOrThrow({
select: { userId: true },
where: { userId },
});
if (nowEditingInfo.userId === userName.userId){
isEditing = false;
}
}
const isEditing = nowEditingInfo && nowEditingInfo.userId !== userId && (new Date().getTime() - new Date(nowEditingInfo.lastHeartBeatAtUTC).getTime()) < 30 * 60 * 1000;

if (isEditing && nowEditingInfo){
// モーダルを表示する:${nowEditingInfo.userName}さんが編集中です。
// モーダルを表示する:${nowEditingInfo.userId}さんが編集中です。
// 「戻る」を押してredirect(`/archives/${postId}`)する
return json({
postData: null,
postMarkdown: null,
tagNames: null,
allTagsForSearch: null,
editingUserName: nowEditingInfo.userId,
userId,
postId,
isEditing: true,
userName: null,
editHistory: null,
});
}
Expand All @@ -70,15 +61,10 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
where: { postId: Number(postId) },
});
}
const userName = await prisma.userProfiles.findUniqueOrThrow({
select: { userId: true },
where: { userId },
});

await prisma.nowEditingPages.create({
data: {
postId: Number(postId),
userId: userName.userId,
userId: userId,
},
});

Expand Down Expand Up @@ -147,22 +133,24 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
tagNames,
postMarkdown,
allTagsForSearch,
userName: userName.userId,
editingUserName:null,
userId,
isEditing:false,
postId,
editHistory,
});
}

export default function EditPost() {
const { postData, postMarkdown, tagNames, allTagsForSearch, editingUserName, isEditing, postId, userName, editHistory } = useLoaderData<typeof loader>();
const { postData, postMarkdown, tagNames, allTagsForSearch, isEditing, postId, userId, editHistory } = useLoaderData<typeof loader>();
const [markdownContent, setMarkdownContent] = useState<string>(postMarkdown || "");
const [selectedTags, setSelectedTags] = useState<string[] | null>(tagNames);
const navigation = useNavigation();

if (isEditing){
return (
<div className="fixed inset-0 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-8 shadow-lg">
<p className="text-xl font-bold mb-4">{editingUserName}さんが編集中です。</p>
<p className="text-xl font-bold mb-4">{userId}さんが編集中です。</p>
<NavLink to={`/archives/${postId}`} className="block w-full text-center text-white bg-blue-500 hover:bg-blue-600 py-2 rounded-md">
戻る
</NavLink>
Expand All @@ -171,40 +159,26 @@ export default function EditPost() {
);
}

if (!postData) {
return <div>投稿が見つかりません</div>;
}
const { postTitle } = postData;

const [markdownContent, setMarkdownContent] = useState(postMarkdown);
const [selectedTags, setSelectedTags] = useState<string[]>(tagNames);
const [tagInputValue, setTagInputValue] = useState<string>("");
const [tagSearchSuggestions, setTagSearchSuggestions] = useState<{ tagName: string, count: number }[]>([]);
const oldTags = tagNames

const handleTagInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setTagInputValue(value);

if (value.length > 0) {
const filteredTagSearchSuggestions = allTagsForSearch.filter((tag) => tag.tagName.includes(value));
setTagSearchSuggestions(filteredTagSearchSuggestions);
} else {
setTagSearchSuggestions([]);
const handleTagRemove = (tagName: string) => {
if (selectedTags) {
setSelectedTags(selectedTags.filter((tag) => tag !== tagName));
}
};

const handleTagSelect = (tagName: string) => {
if (!selectedTags.includes(tagName)) {
setSelectedTags([...selectedTags, tagName]);
setTagInputValue("");
setTagSearchSuggestions([]);
}
const handleTagsSelected = (tags: string[]) => {
setSelectedTags(tags);
};

const handleTagRemove = (tagName: string) => {
setSelectedTags(selectedTags.filter((tag) => tag !== tagName));
const handleMarkdownChange = (value: string | undefined) => {
setMarkdownContent(value || "");
};

const navigation = useNavigation();

return (
<ClientOnly fallback={<div>Loading...</div>}>
{() => (
Expand All @@ -226,65 +200,49 @@ export default function EditPost() {
<div className="mb-4">
<H2>タグを編集する</H2>
<div className="my-4">
<div className="mt-2">
<p className="my-4">変更前:</p>
<div className="flex flex-wrap">
{oldTags.map(
(tag) =>
<span className="bg-blue-500 text-white px-2 py-1 rounded-full mr-2 mb-2">{tag}</span>
)}
</div>
</div>
<div className="mt-2">
<p className="my-4">変更前:</p>
<div className="flex flex-wrap">
{oldTags.map((tag) => (
<span key={tag} className="bg-blue-500 text-white px-2 py-1 rounded-full mr-2 mb-2">
{tag}
</span>
))}
</div>
</div>
</div>
<div className="flex items-center">
<input
type="text"
value={tagInputValue}
onChange={handleTagInputChange}
className="border border-gray-300 rounded-l px-4 py-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 edit-tag-search-input placeholder-slate-500"
placeholder="タグを検索"
/>
</div>
{tagSearchSuggestions.length > 0 && (
<ul className="mt-2 border border-gray-300 rounded">
{tagSearchSuggestions.map((tag) => (
<li
key={tag.tagName}
className={`px-4 py-2 cursor-pointer hover:bg-gray-100 edit-tag-${tag.tagName}`}
onClick={() => handleTagSelect(tag.tagName)}
>
{tag.tagName} ({tag.count})
</li>
))}
</ul>
)}
<TagSelectionBox
onTagsSelected={handleTagsSelected}
parentComponentStateValues={selectedTags || []}
allTagsOnlyForSearch={allTagsForSearch}
/>
<div className="mt-2">
<p className="my-4">変更後:</p>
<div className="flex flex-wrap">
{selectedTags.map((tag) => (
<span
key={tag}
className="bg-blue-500 text-white px-2 py-1 rounded-full mr-2 mb-2"
>
<input type="hidden" name="tags" value={tag} />
{tag}
<button
type="button"
onClick={() => handleTagRemove(tag)}
className="ml-2 edit-tag-remove-button"
<p className="my-4">変更後:</p>
<div className="flex flex-wrap">
{selectedTags && selectedTags.map((tag) => (
<span
key={tag}
className="bg-blue-500 text-white px-2 py-1 rounded-full mr-2 mb-2"
>
x
</button>
</span>
))}
<input type="hidden" name="tags" value={tag} />
{tag}
<button
type="button"
onClick={() => handleTagRemove(tag)}
className="ml-2 edit-tag-remove-button"
>
x
</button>
</span>
))}
</div>
</div>
</div>
</div>
<div className="mb-4">
<H2>本文を編集する</H2>
<MarkdownEditor
defaultValue={markdownContent}
handleValueChange={setMarkdownContent}
defaultValue={markdownContent || ""}
handleValueChange={handleMarkdownChange}
/>
</div>
<input type="hidden" name="postContent" value={markdownContent} />
Expand All @@ -295,7 +253,6 @@ export default function EditPost() {
>
変更を保存する
</button>
<input type="hidden" name="userName" value={userName} />
</Form>
<div className="mb-4">
<H2>編集履歴</H2>
Expand All @@ -310,43 +267,43 @@ export default function EditPost() {
</tr>
</thead>
<tbody>
{editHistory && editHistory.map((edit) => (
<tr key={edit.postRevisionNumber}>
<td className="border px-2 py-2">{edit.postRevisionNumber}</td>
<td className="border px-2 py-2">{edit.postEditDateJst.toLocaleString()}</td>
<td className="border px-2 py-2">{edit.editorUserId.slice(0,8)}</td>
<td className="border px-2 py-2">
{diff.diffChars(edit.postTitleBeforeEdit, edit.postTitleAfterEdit).map((part, index) => {
if (part.added || part.removed) {
const start = Math.max(0, part.value.indexOf(part.value) - 50);
const end = Math.min(part.value.length, part.value.indexOf(part.value) + 50);
const excerpt = part.value.slice(start, end);
return (
<span key={index} className={part.added ? 'bg-green-200' : 'bg-red-200'}>
{excerpt}
</span>
);
}
return null;
})}
</td>
<td className="border py-2">
{diff.diffLines(edit.postContentBeforeEdit, edit.postContentAfterEdit).map((part, index) => {
if (part.added || part.removed) {
const start = Math.max(0, part.value.indexOf(part.value) - 50);
const end = Math.min(part.value.length, part.value.indexOf(part.value) + 50);
const excerpt = part.value.slice(start, end);
return (
<span key={index} className={part.added ? 'bg-green-200' : 'bg-red-200'}>
{excerpt}
</span>
);
}
return null;
})}
</td>
</tr>
))}
{editHistory && editHistory.map((edit) => (
<tr key={edit.postRevisionNumber}>
<td className="border px-2 py-2">{edit.postRevisionNumber}</td>
<td className="border px-2 py-2">{edit.postEditDateJst.toLocaleString()}</td>
<td className="border px-2 py-2">{edit.editorUserId.slice(0,8)}</td>
<td className="border px-2 py-2">
{diff.diffChars(edit.postTitleBeforeEdit, edit.postTitleAfterEdit).map((part: diff.Change, index: number) => {
if (part.added || part.removed) {
const start = Math.max(0, part.value.indexOf(part.value) - 50);
const end = Math.min(part.value.length, part.value.indexOf(part.value) + 50);
const excerpt = part.value.slice(start, end);
return (
<span key={index} className={part.added ? 'bg-green-200' : 'bg-red-200'}>
{excerpt}
</span>
);
}
return null;
})}
</td>
<td className="border py-2">
{diff.diffLines(edit.postContentBeforeEdit, edit.postContentAfterEdit).map((part: diff.Change, index: number) => {
if (part.added || part.removed) {
const start = Math.max(0, part.value.indexOf(part.value) - 50);
const end = Math.min(part.value.length, part.value.indexOf(part.value) + 50);
const excerpt = part.value.slice(start, end);
return (
<span key={index} className={part.added ? 'bg-green-200' : 'bg-red-200'}>
{excerpt}
</span>
);
}
return null;
})}
</td>
</tr>
))}
</tbody>
</table>
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/routes/_layout.signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export async function action({ request }: ActionFunctionArgs) {
},
});
} catch (error) {
return json({ status: 500, message: "ユーザー登録に失敗しました" });
throw new Error(`Failed to create user profile: ${error}`);
}

return redirect("/email");
Expand Down
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@axe-core/playwright": "^4.9.0",
"@playwright/test": "^1.42.1",
"@remix-run/dev": "^2.8.1",
"@types/diff": "^5.2.0",
"@types/marked": "^6.0.0",
"@types/node": "^20.11.30",
"@types/react": "^18.2.20",
Expand Down

1 comment on commit 211f3d4

@vercel
Copy link

@vercel vercel bot commented on 211f3d4 Apr 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.