コミットを比較
2
コミット
| 作成者 | SHA1 | 日付 | |
|---|---|---|---|
|
|
ae3d9b3876 | ||
|
|
b7d92b7ddd |
@@ -314,7 +314,9 @@ class PostsController < ApplicationController
|
|||||||
def viewed
|
def viewed
|
||||||
return head :unauthorized unless current_user
|
return head :unauthorized unless current_user
|
||||||
|
|
||||||
current_user.viewed_posts << Post.find(params[:id])
|
UserPostView.create_or_find_by!(
|
||||||
|
user: current_user,
|
||||||
|
post: Post.find(params[:id]))
|
||||||
head :no_content
|
head :no_content
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -2559,6 +2559,21 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
|
|
||||||
expect(user.reload.viewed?(post_record)).to be(true)
|
expect(user.reload.viewed?(post_record)).to be(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it '204 and keeps one view on repeated requests' do
|
||||||
|
sign_in_as(user)
|
||||||
|
|
||||||
|
expect {
|
||||||
|
post "/posts/#{ post_record.id }/viewed"
|
||||||
|
}.to change(UserPostView, :count).by(1)
|
||||||
|
expect(response).to have_http_status(:no_content)
|
||||||
|
|
||||||
|
expect {
|
||||||
|
post "/posts/#{ post_record.id }/viewed"
|
||||||
|
}.not_to change(UserPostView, :count)
|
||||||
|
expect(response).to have_http_status(:no_content)
|
||||||
|
expect(user.reload.viewed?(post_record)).to be(true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'DELETE /posts/:id/viewed' do
|
describe 'DELETE /posts/:id/viewed' do
|
||||||
|
|||||||
@@ -153,21 +153,6 @@ const PostEditForm: FC<Props> = ({ post, onSave }) => {
|
|||||||
setDisabled (true)
|
setDisabled (true)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const titleMissing = !title?.trim ()
|
|
||||||
const thumbnailMissing =
|
|
||||||
!titleMissing
|
|
||||||
&& !(post.thumbnail?.trim () || post.thumbnailBase?.trim ())
|
|
||||||
|
|
||||||
if ((titleMissing || thumbnailMissing)
|
|
||||||
&& !(await dialogue.confirm ({
|
|
||||||
title: titleMissing
|
|
||||||
? label ('edit.titleMissingConfirmation')
|
|
||||||
: label ('edit.thumbnailMissingConfirmation'),
|
|
||||||
confirmText: label ('shared:actions.update'),
|
|
||||||
cancelText: label ('shared:actions.cancel'),
|
|
||||||
closeLabel: label ('settings:actions.close') })))
|
|
||||||
return
|
|
||||||
|
|
||||||
await update ({ id: post.id, title, tags, parentPostIds,
|
await update ({ id: post.id, title, tags, parentPostIds,
|
||||||
duration: videoFlg ? duration : null,
|
duration: videoFlg ? duration : null,
|
||||||
originalCreatedFrom, originalCreatedBefore },
|
originalCreatedFrom, originalCreatedBefore },
|
||||||
|
|||||||
@@ -70,16 +70,6 @@ describe ('SidebarComponent', () => {
|
|||||||
vi.spyOn (HTMLElement.prototype, 'hasPointerCapture').mockReturnValue (true)
|
vi.spyOn (HTMLElement.prototype, 'hasPointerCapture').mockReturnValue (true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it ('limits sidebar scrolling to desktop to avoid clipping mobile suggestions', () => {
|
|
||||||
renderSidebar ()
|
|
||||||
|
|
||||||
// jsdom has no layout; guard the responsive classes responsible for clipping.
|
|
||||||
const sidebar = screen.getByText ('Sidebar body')
|
|
||||||
|
|
||||||
expect (sidebar).not.toHaveClass ('overflow-y-auto')
|
|
||||||
expect (sidebar).toHaveClass ('md:overflow-y-auto')
|
|
||||||
})
|
|
||||||
|
|
||||||
it ('uses maxWidth without applying the container main-width reserve twice', async () => {
|
it ('uses maxWidth without applying the container main-width reserve twice', async () => {
|
||||||
localStorage.setItem ('sidebar:test-sidebar:left:width', '700')
|
localStorage.setItem ('sidebar:test-sidebar:left:width', '700')
|
||||||
const onWidthChange = vi.fn ()
|
const onWidthChange = vi.fn ()
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ const SidebarComponent: FC<Props> = ({
|
|||||||
</style>
|
</style>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|
||||||
<div className="h-full md:overflow-y-auto p-4 sidebar">
|
<div className="h-full overflow-y-auto p-4 sidebar">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_untranslated": ["edit.titleMissingConfirmation", "edit.thumbnailMissingConfirmation"],
|
|
||||||
"list": {
|
"list": {
|
||||||
"tagline": "{{brand}}",
|
"tagline": "{{brand}}",
|
||||||
"empty": "ミンタㇻ タ ネン カ イサㇺ",
|
"empty": "ミンタㇻ タ ネン カ イサㇺ",
|
||||||
@@ -32,8 +31,6 @@
|
|||||||
"showExternal": "ソイタㇰ ソㇱ アヌカレ"
|
"showExternal": "ソイタㇰ ソㇱ アヌカレ"
|
||||||
},
|
},
|
||||||
"edit": {
|
"edit": {
|
||||||
"titleMissingConfirmation": "タイトルが設定されてゐません.更新しますか?",
|
|
||||||
"thumbnailMissingConfirmation": "サムネールが設定されてゐません.更新しますか?",
|
|
||||||
"validationFailed": "アシㇼ カㇻ カ エアイカㇷ゚……",
|
"validationFailed": "アシㇼ カㇻ カ エアイカㇷ゚……",
|
||||||
"conflictTitle": "モㇱマ クㇽ アシㇼ カㇻ ワ アン!",
|
"conflictTitle": "モㇱマ クㇽ アシㇼ カㇻ ワ アン!",
|
||||||
"conflictDescription": "モㇱマ イカㇻ クㇽ エエトコ アシㇼ カㇻ ワ アン。",
|
"conflictDescription": "モㇱマ イカㇻ クㇽ エエトコ アシㇼ カㇻ ワ アン。",
|
||||||
|
|||||||
@@ -22,8 +22,6 @@
|
|||||||
"question": "表示しますか?", "showExternal": "外部ページを表示"
|
"question": "表示しますか?", "showExternal": "外部ページを表示"
|
||||||
},
|
},
|
||||||
"edit": {
|
"edit": {
|
||||||
"titleMissingConfirmation": "タイトルが設定されてゐません.更新しますか?",
|
|
||||||
"thumbnailMissingConfirmation": "サムネールが設定されてゐません.更新しますか?",
|
|
||||||
"validationFailed": "更新はできなかったよ……",
|
"validationFailed": "更新はできなかったよ……",
|
||||||
"conflictTitle": "競合が発生しました.",
|
"conflictTitle": "競合が発生しました.",
|
||||||
"conflictDescription": "ほかの耕作員が先に更新してゐます.",
|
"conflictDescription": "ほかの耕作員が先に更新してゐます.",
|
||||||
|
|||||||
@@ -35,10 +35,6 @@ const renderPage = () =>
|
|||||||
describe ('TagDetailPage', () => {
|
describe ('TagDetailPage', () => {
|
||||||
beforeEach (() => {
|
beforeEach (() => {
|
||||||
vi.clearAllMocks ()
|
vi.clearAllMocks ()
|
||||||
tagsApi.fetchTag.mockReset ()
|
|
||||||
api.apiPut.mockReset ()
|
|
||||||
api.isApiError.mockReset ()
|
|
||||||
toastApi.toast.mockReset ()
|
|
||||||
api.isApiError.mockReturnValue (false)
|
api.isApiError.mockReturnValue (false)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -55,11 +51,8 @@ describe ('TagDetailPage', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it ('submits edited tag fields', async () => {
|
it ('submits edited tag fields', async () => {
|
||||||
const updatedTag = buildTag ({ id: 7, name: 'new', aliases: ['alias'] })
|
tagsApi.fetchTag.mockResolvedValueOnce (buildTag ({ id: 7, name: 'old' }))
|
||||||
tagsApi.fetchTag
|
api.apiPut.mockResolvedValueOnce (buildTag ({ id: 7, name: 'new', aliases: ['alias'] }))
|
||||||
.mockResolvedValue (updatedTag)
|
|
||||||
.mockResolvedValueOnce (buildTag ({ id: 7, name: 'old' }))
|
|
||||||
api.apiPut.mockResolvedValueOnce (updatedTag)
|
|
||||||
|
|
||||||
renderPage ()
|
renderPage ()
|
||||||
|
|
||||||
|
|||||||
新しいイシューから参照
ユーザーをブロックする