コミットを比較

..

8 コミット

作成者 SHA1 メッセージ 日付
みてるぞ f662dc9dc0 #399 2026-07-18 20:22:53 +09:00
みてるぞ 206c6bc0a0 #399 2026-07-18 20:10:15 +09:00
みてるぞ 8f66ee8059 #399 2026-07-18 20:03:26 +09:00
みてるぞ 83e3db3314 #399 2026-07-18 20:00:40 +09:00
みてるぞ eae6c30064 #399 2026-07-18 19:26:27 +09:00
みてるぞ 240e078f0b #399 2026-07-18 18:33:11 +09:00
みてるぞ 74b1ada0dd #399 2026-07-18 17:44:56 +09:00
みてるぞ 23d8adf65d #399 2026-07-18 17:35:28 +09:00
16個のファイルの変更231行の追加171行の削除
+8 -3
ファイルの表示
@@ -216,7 +216,8 @@ class PostsController < ApplicationController
preflight = PostCreatePreflight.new( preflight = PostCreatePreflight.new(
attributes: post_create_attributes, attributes: post_create_attributes,
thumbnail: params[:thumbnail]).run thumbnail: params[:thumbnail],
host: request.base_url).run
return render json: dry_run_json(preflight) if bool?(:dry) return render json: dry_run_json(preflight) if bool?(:dry)
if preflight[:existing_post_id].present? if preflight[:existing_post_id].present?
post = Post.new(url: preflight[:url]) post = Post.new(url: preflight[:url])
@@ -273,7 +274,11 @@ class PostsController < ApplicationController
return head :payload_too_large if request.content_length.to_i > MAX_BULK_REQUEST_BYTES return head :payload_too_large if request.content_length.to_i > MAX_BULK_REQUEST_BYTES
posts = parse_bulk_posts_manifest posts = parse_bulk_posts_manifest
thumbnails = parse_bulk_thumbnails(posts.length) thumbnails = parse_bulk_thumbnails(posts.length)
result = PostBulkCreator.new(actor: current_user, posts:, thumbnails:).run result = PostBulkCreator.new(
actor: current_user,
posts:,
thumbnails:,
host: request.base_url).run
render json: result render json: result
rescue JSON::ParserError rescue JSON::ParserError
render_bad_request 'posts manifest の JSON が不正です.' render_bad_request 'posts manifest の JSON が不正です.'
@@ -627,7 +632,7 @@ class PostsController < ApplicationController
return nil if post_id.blank? return nil if post_id.blank?
post = Post.with_attached_thumbnail.find_by(id: post_id) post = Post.with_attached_thumbnail.find_by(id: post_id)
PostCompactRepr.base(post) PostCompactRepr.base(post, host: request.base_url)
end end
def dry_run_json preflight def dry_run_json preflight
+2 -2
ファイルの表示
@@ -4,11 +4,11 @@
module PostCompactRepr module PostCompactRepr
module_function module_function
def base post def base post, host: nil
return nil if post.nil? return nil if post.nil?
PostRepr PostRepr
.common(post) .common(post, host:)
.slice( .slice(
'id', 'id',
'title', 'title',
+48 -12
ファイルの表示
@@ -17,8 +17,13 @@ module PostRepr
module_function module_function
def base post, current_user = nil def base post, current_user = nil, host: nil
json = common(post) json =
if host.present?
common(post, host:)
else
common(post)
end
json['tags'] = tag_json(post) json['tags'] = tag_json(post)
json['uploaded_user'] = post.uploaded_user && UserRepr.base(post.uploaded_user) json['uploaded_user'] = post.uploaded_user && UserRepr.base(post.uploaded_user)
json['viewed'] = current_user ? current_user.viewed?(post) : false json['viewed'] = current_user ? current_user.viewed?(post) : false
@@ -26,7 +31,16 @@ module PostRepr
end end
def detail post, current_user = nil, parent_posts: [], child_posts: [], def detail post, current_user = nil, parent_posts: [], child_posts: [],
sibling_posts: { }, related: [] sibling_posts: { }, related: [], host: nil
if host.present?
base(post, current_user, host:).merge(
'parent_posts' => cards(parent_posts, host:),
'child_posts' => cards(child_posts, host:),
'sibling_posts' => sibling_posts.transform_keys(&:to_s).transform_values { |posts|
cards(posts, host:)
},
'related' => cards(related, host:))
else
base(post, current_user).merge( base(post, current_user).merge(
'parent_posts' => cards(parent_posts), 'parent_posts' => cards(parent_posts),
'child_posts' => cards(child_posts), 'child_posts' => cards(child_posts),
@@ -35,22 +49,41 @@ module PostRepr
}, },
'related' => cards(related)) 'related' => cards(related))
end end
end
def card post def card post, host: nil
if host.present?
common(post, host:).merge('parent_posts' => [], 'child_posts' => [])
else
common(post).merge('parent_posts' => [], 'child_posts' => []) common(post).merge('parent_posts' => [], 'child_posts' => [])
end end
end
def cards posts def cards posts, host: nil
if host.present?
posts.map { |post| card(post, host:) }
else
posts.map { |post| card(post) } posts.map { |post| card(post) }
end end
def many posts, current_user = nil
posts.map { |p| base(p, current_user) }
end end
def common post def many posts, current_user = nil, host: nil
if host.present?
posts.map { |p| base(p, current_user, host:) }
else
posts.map { |p| base(p, current_user) }
end
end
def common post, host: nil
BASE_FIELDS.to_h { |field| [field.to_s, post.public_send(field)] } BASE_FIELDS.to_h { |field| [field.to_s, post.public_send(field)] }
.merge('thumbnail' => thumbnail_url(post)) .merge(
'thumbnail' =>
if host.present?
thumbnail_url(post, host:)
else
thumbnail_url(post)
end)
end end
def tag_json post def tag_json post
@@ -65,10 +98,13 @@ module PostRepr
} }
end end
def thumbnail_url post def thumbnail_url post, host: nil
return nil unless post.thumbnail.attached? return nil unless post.thumbnail.attached?
Rails.application.routes.url_helpers.rails_blob_url(post.thumbnail, only_path: false) options = { only_path: false }
options[:host] = host if host.present?
Rails.application.routes.url_helpers.rails_storage_proxy_url(post.thumbnail, **options)
rescue ActionController::UrlGenerationError, ArgumentError, URI::InvalidURIError => e rescue ActionController::UrlGenerationError, ArgumentError, URI::InvalidURIError => e
Rails.logger.warn( Rails.logger.warn(
"PostRepr.thumbnail_url failed post_id=#{post.id} " \ "PostRepr.thumbnail_url failed post_id=#{post.id} " \
+5 -3
ファイルの表示
@@ -1,8 +1,9 @@
class PostBulkCreator class PostBulkCreator
def initialize actor:, posts:, thumbnails: def initialize actor:, posts:, thumbnails:, host: nil
@actor_id = actor.id @actor_id = actor.id
@posts = posts @posts = posts
@thumbnails = thumbnails @thumbnails = thumbnails
@host = host
end end
def run def run
@@ -64,7 +65,8 @@ class PostBulkCreator
preflight = preflight =
PostCreatePreflight.new( PostCreatePreflight.new(
attributes: attributes, attributes: attributes,
thumbnail: thumbnail_for(index, attributes)).run thumbnail: thumbnail_for(index, attributes),
host: @host).run
if preflight[:existing_post_id].present? if preflight[:existing_post_id].present?
return { return {
status: 'skipped', status: 'skipped',
@@ -195,6 +197,6 @@ class PostBulkCreator
end end
def compact_existing_post post def compact_existing_post post
PostCompactRepr.base(post) PostCompactRepr.base(post, host: @host)
end end
end end
+3 -2
ファイルの表示
@@ -9,9 +9,10 @@ class PostCreatePreflight
end end
end end
def initialize attributes:, thumbnail: nil def initialize attributes:, thumbnail: nil, host: nil
@attributes = attributes.symbolize_keys @attributes = attributes.symbolize_keys
@thumbnail = thumbnail @thumbnail = thumbnail
@host = host
end end
def run def run
@@ -124,7 +125,7 @@ class PostCreatePreflight
return nil if post_id.blank? return nil if post_id.blank?
post = Post.with_attached_thumbnail.find_by(id: post_id) post = Post.with_attached_thumbnail.find_by(id: post_id)
PostCompactRepr.base(post) PostCompactRepr.base(post, host: @host)
end end
def final_field_warnings field_warnings def final_field_warnings field_warnings
+16 -11
ファイルの表示
@@ -1,8 +1,9 @@
import { AnimatePresence, LayoutGroup, MotionConfig, motion } from 'framer-motion' import { AnimatePresence, LayoutGroup, MotionConfig, motion } from 'framer-motion'
import { Fragment, useEffect, useMemo, useState } from 'react' import { Fragment, useEffect, useMemo, useState } from 'react'
import { BrowserRouter, import { createBrowserRouter,
Navigate, Navigate,
Route, Route,
RouterProvider,
Routes, Routes,
useLocation } from 'react-router-dom' useLocation } from 'react-router-dom'
@@ -154,7 +155,7 @@ const PostDetailRoute = ({ user }: { user: User | null }) => {
} }
const App: FC = () => { const RoutedApp: FC = () => {
const [user, setUser] = useState<User | null> (null) const [user, setUser] = useState<User | null> (null)
const [status, setStatus] = useState (200) const [status, setStatus] = useState (200)
const behaviourSettings = useClientBehaviourSettings () const behaviourSettings = useClientBehaviourSettings ()
@@ -253,11 +254,6 @@ const App: FC = () => {
} }
return ( return (
<>
<RouteBlockerOverlay/>
{import.meta.env.DEV && <DevModeWatermark/>}
<BrowserRouter>
<DialogueProvider> <DialogueProvider>
<UnsavedChangesGuardProvider> <UnsavedChangesGuardProvider>
<KeyboardShortcutsProvider> <KeyboardShortcutsProvider>
@@ -273,7 +269,7 @@ const App: FC = () => {
<motion.div <motion.div
layout={animationMode === 'off' ? false : 'position'} layout={animationMode === 'off' ? false : 'position'}
transition={{ layout: appLayoutTransition }} transition={{ layout: appLayoutTransition }}
className="relative flex flex-col h-dvh w-full overflow-y-hidden"> className="relative flex h-dvh w-full flex-col overflow-y-hidden">
<TopNav user={user}/> <TopNav user={user}/>
<RouteTransitionWrapper <RouteTransitionWrapper
animationMode={animationMode} animationMode={animationMode}
@@ -286,9 +282,18 @@ const App: FC = () => {
<Toaster/> <Toaster/>
</KeyboardShortcutsProvider> </KeyboardShortcutsProvider>
</UnsavedChangesGuardProvider> </UnsavedChangesGuardProvider>
</DialogueProvider> </DialogueProvider>)
</BrowserRouter>
</>)
} }
const router = createBrowserRouter ([{
path: '*',
element: <RoutedApp/> }])
const App: FC = () => (
<>
<RouteBlockerOverlay/>
{import.meta.env.DEV && <DevModeWatermark/>}
<RouterProvider router={router}/>
</>)
export default App export default App
-13
ファイルの表示
@@ -6,7 +6,6 @@ import { createPath, useNavigate } from 'react-router-dom'
import { useOverlayStore } from '@/components/RouteBlockerOverlay' import { useOverlayStore } from '@/components/RouteBlockerOverlay'
import { prefetchForURL } from '@/lib/prefetchers' import { prefetchForURL } from '@/lib/prefetchers'
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings' import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
import { useUnsavedChangesGuard } from '@/lib/useUnsavedChangesGuard'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import type { AnchorHTMLAttributes, MouseEvent, TouchEvent } from 'react' import type { AnchorHTMLAttributes, MouseEvent, TouchEvent } from 'react'
@@ -36,7 +35,6 @@ export default forwardRef<HTMLAnchorElement, Props> (({
const navigate = useNavigate () const navigate = useNavigate ()
const qc = useQueryClient () const qc = useQueryClient ()
const behaviourSettings = useClientBehaviourSettings () const behaviourSettings = useClientBehaviourSettings ()
const { confirmDiscardNavigation } = useUnsavedChangesGuard ()
const linkPreloadMode = behaviourSettings.linkPreload ?? 'intent' const linkPreloadMode = behaviourSettings.linkPreload ?? 'intent'
const path = useMemo ( const path = useMemo (
() => typeof to === 'string' ? to : createPath (to), () => typeof to === 'string' ? to : createPath (to),
@@ -45,10 +43,6 @@ export default forwardRef<HTMLAnchorElement, Props> (({
const url = useMemo (() => { const url = useMemo (() => {
return (new URL (path, window.location.origin)).toString () return (new URL (path, window.location.origin)).toString ()
}, [path]) }, [path])
const nextPathname = useMemo (
() => (new URL (path, window.location.origin)).pathname,
[path],
)
const setOverlay = useOverlayStore (s => s.setActive) const setOverlay = useOverlayStore (s => s.setActive)
const doPrefetch = async () => { const doPrefetch = async () => {
@@ -93,13 +87,6 @@ export default forwardRef<HTMLAnchorElement, Props> (({
ev.preventDefault () ev.preventDefault ()
if (nextPathname !== window.location.pathname)
{
const confirmed = await confirmDiscardNavigation ()
if (!(confirmed))
return
}
flushSync (() => { flushSync (() => {
setOverlay (true) setOverlay (true)
}) })
+1 -1
ファイルの表示
@@ -10,8 +10,8 @@ import { hasThumbnailBaseValue, hasVideoTag } from '@/lib/postImportRows'
import type { FC } from 'react' import type { FC } from 'react'
import type { PostImportEditableDraft, PostImportRow } from '@/lib/postImportSession'
import type { DialogueFormControls } from '@/lib/dialogues/useDialogue' import type { DialogueFormControls } from '@/lib/dialogues/useDialogue'
import type { PostImportEditableDraft, PostImportRow } from '@/lib/postImportTypes'
type Draft = PostImportEditableDraft type Draft = PostImportEditableDraft
+6 -3
ファイルの表示
@@ -4,13 +4,16 @@ import { Button } from '@/components/ui/button'
import PostImportThumbnailPreview from '@/components/posts/import/PostImportThumbnailPreview' import PostImportThumbnailPreview from '@/components/posts/import/PostImportThumbnailPreview'
import PostImportStatusBadge from '@/components/posts/import/PostImportStatusBadge' import PostImportStatusBadge from '@/components/posts/import/PostImportStatusBadge'
import { displayPostImportStatus } from '@/components/posts/import/postImportRowStatus' import { displayPostImportStatus } from '@/components/posts/import/postImportRowStatus'
import { hasVideoTag } from '@/lib/postImportSession' import {
import { canEditReviewRow, canRetryResultRow } from '@/lib/postImportSession' canEditReviewRow,
canRetryResultRow,
hasVideoTag,
} from '@/lib/postImportRows'
import { cn, originalCreatedAtString } from '@/lib/utils' import { cn, originalCreatedAtString } from '@/lib/utils'
import type { FC } from 'react' import type { FC } from 'react'
import type { PostImportRow } from '@/lib/postImportSession' import type { PostImportRow } from '@/lib/postImportTypes'
type Props = { type Props = {
row: PostImportRow row: PostImportRow
+1 -1
ファイルの表示
@@ -1,4 +1,4 @@
import type { PostImportRow } from '@/lib/postImportSession' import type { PostImportRow } from '@/lib/postImportTypes'
export type PostImportDisplayStatus = export type PostImportDisplayStatus =
'ready' 'ready'
-5
ファイルの表示
@@ -226,11 +226,6 @@ export const buildNextEditedRow = (
? 'manual' ? 'manual'
: (editingRow.provenance[field] ?? 'automatic') : (editingRow.provenance[field] ?? 'automatic')
}) })
if (nextProvenance.duration === 'manual')
{
nextAttributes.videoMs = ''
nextProvenance.videoMs = 'manual'
}
nextAttributes.tags = draft.tags nextAttributes.tags = draft.tags
if (isManualChange (editingRow.attributes.tags, draft.tags)) if (isManualChange (editingRow.attributes.tags, draft.tags))
{ {
-4
ファイルの表示
@@ -1,4 +0,0 @@
export * from '@/lib/postImportTypes'
export * from '@/lib/postImportStorage'
export * from '@/lib/postImportSourceValidation'
export * from '@/lib/postImportRows'
+1 -6
ファイルの表示
@@ -25,7 +25,6 @@ import {
getEffectiveKeyBindings, getEffectiveKeyBindings,
setClientKeyboardSettings, setClientKeyboardSettings,
} from '@/lib/settings' } from '@/lib/settings'
import { useUnsavedChangesGuard } from '@/lib/useUnsavedChangesGuard'
import type { import type {
KeyBinding, KeyBinding,
@@ -97,7 +96,6 @@ const focusSearchTarget = (): void => {
export const KeyboardShortcutsProvider = ({ children }: PropsWithChildren) => { export const KeyboardShortcutsProvider = ({ children }: PropsWithChildren) => {
const location = useLocation () const location = useLocation ()
const navigate = useNavigate () const navigate = useNavigate ()
const { confirmDiscardNavigation } = useUnsavedChangesGuard ()
const [keyboardSettings, setKeyboardSettingsState] = const [keyboardSettings, setKeyboardSettingsState] =
useState<ClientKeyboardSettings> (() => getClientKeyboardSettings ()) useState<ClientKeyboardSettings> (() => getClientKeyboardSettings ())
@@ -149,11 +147,8 @@ export const KeyboardShortcutsProvider = ({ children }: PropsWithChildren) => {
}, []) }, [])
const guardedNavigate = useCallback ((path: string) => { const guardedNavigate = useCallback ((path: string) => {
confirmDiscardNavigation ().then (confirmed => {
if (confirmed)
navigate (path) navigate (path)
}) }, [navigate])
}, [confirmDiscardNavigation, navigate])
const builtinHandlers = useMemo<ShortcutHandlers> ( const builtinHandlers = useMemo<ShortcutHandlers> (
() => ({ () => ({
+87 -40
ファイルの表示
@@ -7,7 +7,7 @@ import {
useRef, useRef,
useState, useState,
} from 'react' } from 'react'
import { useBlocker, useLocation } from 'react-router-dom' import { useBlocker } from 'react-router-dom'
import { useDialogue } from '@/lib/dialogues/useDialogue' import { useDialogue } from '@/lib/dialogues/useDialogue'
@@ -24,9 +24,8 @@ type UseUnsavedChangesGuardOptions = {
type UnsavedChangesGuardContextValue = { type UnsavedChangesGuardContextValue = {
hasUnsavedChanges: boolean hasUnsavedChanges: boolean
registerUnsavedChangesSource: ( registerUnsavedChangesSource: (
source: UnsavedChangesSource | null, source: UnsavedChangesSource,
) => void ) => () => void
confirmDiscardNavigation: () => Promise<boolean>
allowNextNavigation: () => void } allowNextNavigation: () => void }
const UnsavedChangesGuardContext = const UnsavedChangesGuardContext =
@@ -35,24 +34,67 @@ const UnsavedChangesGuardContext =
export const UnsavedChangesGuardProvider: FC<PropsWithChildren> = ({ children }) => { export const UnsavedChangesGuardProvider: FC<PropsWithChildren> = ({ children }) => {
const dialogue = useDialogue () const dialogue = useDialogue ()
const location = useLocation () const sourcesRef = useRef (new Map<symbol, UnsavedChangesSource> ())
const [source, setSource] = useState<UnsavedChangesSource | null> (null) const bypassNextNavigationRef = useRef<symbol | null> (null)
const bypassNextNavigationRef = useRef (false) const [revision, setRevision] = useState (0)
const dialogueOpenRef = useRef (false)
const registerUnsavedChangesSource = useCallback (( const registerUnsavedChangesSource = useCallback ((
nextSource: UnsavedChangesSource | null, source: UnsavedChangesSource,
) => { ): (() => void) => {
setSource (nextSource) const sourceId = Symbol ('unsaved-changes-source')
sourcesRef.current.set (sourceId, source)
setRevision (current => current + 1)
return () => {
if (!(sourcesRef.current.delete (sourceId)))
return
setRevision (current => current + 1)
}
}, []) }, [])
const allowNextNavigation = useCallback (() => { const allowNextNavigation = useCallback (() => {
bypassNextNavigationRef.current = true const token = Symbol ('allowed-navigation')
bypassNextNavigationRef.current = token
queueMicrotask (() => {
if (bypassNextNavigationRef.current === token)
bypassNextNavigationRef.current = null
})
}, []) }, [])
const confirmDiscardNavigation = useCallback (async (): Promise<boolean> => { const sources = useMemo (
if (!(source?.dirty)) () => [...sourcesRef.current.values ()],
[revision],
)
const dirtySources = useMemo (
() => sources.filter (source => source.dirty),
[sources],
)
const hasUnsavedChanges = dirtySources.length > 0
const shouldBlock = useCallback (() => {
if (bypassNextNavigationRef.current != null)
{
bypassNextNavigationRef.current = null
return false
}
return hasUnsavedChanges
}, [hasUnsavedChanges])
const blocker = useBlocker (shouldBlock)
const discardDirtyChanges = useCallback (async (): Promise<boolean> => {
if (!(hasUnsavedChanges))
return true return true
if (dialogueOpenRef.current)
return false
dialogueOpenRef.current = true
try
{
const confirmed = await dialogue.confirm ({ const confirmed = await dialogue.confirm ({
title: '変更が破棄してページ移動しますか?', title: '変更が破棄してページ移動しますか?',
confirmText: '変更を破棄して移動', confirmText: '変更を破棄して移動',
@@ -60,42 +102,51 @@ export const UnsavedChangesGuardProvider: FC<PropsWithChildren> = ({ children })
if (!(confirmed)) if (!(confirmed))
return false return false
bypassNextNavigationRef.current = true try
{
for (const source of dirtySources)
await source.discard () await source.discard ()
return true }
}, [dialogue, source]) catch
{
return false
}
const blocker = useBlocker (() => return true
source?.dirty === true && !(bypassNextNavigationRef.current)) }
finally
{
dialogueOpenRef.current = false
}
}, [dialogue, dirtySources, hasUnsavedChanges])
useEffect (() => { useEffect (() => {
if (blocker.state !== 'blocked') if (blocker.state !== 'blocked')
return return
let cancelled = false let active = true
void (async () => { void (async () => {
const confirmed = await confirmDiscardNavigation () const confirmed = await discardDirtyChanges ()
if (cancelled) if (!(active))
return return
if (confirmed) if (confirmed)
{
blocker.proceed () blocker.proceed ()
else return
}
blocker.reset () blocker.reset ()
}) () }) ()
return () => { return () => {
cancelled = true active = false
} }
}, [blocker, confirmDiscardNavigation]) }, [blocker, discardDirtyChanges])
useEffect (() => { useEffect (() => {
bypassNextNavigationRef.current = false if (!(hasUnsavedChanges))
}, [location.hash, location.pathname, location.search])
useEffect (() => {
if (!(source?.dirty))
return return
const handleBeforeUnload = (event: BeforeUnloadEvent) => { const handleBeforeUnload = (event: BeforeUnloadEvent) => {
@@ -107,18 +158,16 @@ export const UnsavedChangesGuardProvider: FC<PropsWithChildren> = ({ children })
return () => { return () => {
window.removeEventListener ('beforeunload', handleBeforeUnload) window.removeEventListener ('beforeunload', handleBeforeUnload)
} }
}, [source?.dirty]) }, [hasUnsavedChanges])
const value = useMemo<UnsavedChangesGuardContextValue> (() => ({ const value = useMemo<UnsavedChangesGuardContextValue> (() => ({
hasUnsavedChanges: source?.dirty === true, hasUnsavedChanges,
registerUnsavedChangesSource, registerUnsavedChangesSource,
confirmDiscardNavigation,
allowNextNavigation, allowNextNavigation,
}), [ }), [
allowNextNavigation, allowNextNavigation,
confirmDiscardNavigation, hasUnsavedChanges,
registerUnsavedChangesSource, registerUnsavedChangesSource,
source?.dirty,
]) ])
return ( return (
@@ -136,18 +185,16 @@ export const useUnsavedChangesGuard = (
if (context == null) if (context == null)
throw new Error ('UnsavedChangesGuardProvider が必要です.') throw new Error ('UnsavedChangesGuardProvider が必要です.')
const { registerUnsavedChangesSource } = context
useEffect (() => { useEffect (() => {
if (options == null) if (options == null)
return return
context.registerUnsavedChangesSource ({ return registerUnsavedChangesSource ({
dirty: options.dirty, dirty: options.dirty,
discard: options.onDiscard ?? (() => undefined) }) discard: options.onDiscard ?? (() => undefined) })
}, [options?.dirty, options?.onDiscard, registerUnsavedChangesSource])
return () => {
context.registerUnsavedChangesSource (null)
}
}, [context, options?.dirty, options?.onDiscard])
return context return context
} }
-8
ファイルの表示
@@ -108,7 +108,6 @@ const emptyAttributes = () => ({
originalCreatedFrom: '', originalCreatedFrom: '',
originalCreatedBefore: '', originalCreatedBefore: '',
duration: '', duration: '',
videoMs: '',
tags: '', tags: '',
parentPostIds: '' }) parentPostIds: '' })
@@ -120,7 +119,6 @@ const emptyProvenance = () => ({
originalCreatedFrom: 'automatic', originalCreatedFrom: 'automatic',
originalCreatedBefore: 'automatic', originalCreatedBefore: 'automatic',
duration: 'automatic', duration: 'automatic',
videoMs: 'automatic',
tags: 'automatic', tags: 'automatic',
parentPostIds: 'automatic' }) as const parentPostIds: 'automatic' }) as const
@@ -294,7 +292,6 @@ const buildDryRunFormData = (row: PostImportRow): FormData => {
formData.append ( formData.append (
'original_created_before', 'original_created_before',
String (row.attributes.originalCreatedBefore ?? '')) String (row.attributes.originalCreatedBefore ?? ''))
formData.append ('video_ms', String (row.attributes.videoMs ?? ''))
formData.append ('duration', String (row.attributes.duration ?? '')) formData.append ('duration', String (row.attributes.duration ?? ''))
if (!(hasThumbnailBaseValue (row.attributes.thumbnailBase)) && row.thumbnailFile != null) if (!(hasThumbnailBaseValue (row.attributes.thumbnailBase)) && row.thumbnailFile != null)
formData.append ('thumbnail', row.thumbnailFile) formData.append ('thumbnail', row.thumbnailFile)
@@ -321,7 +318,6 @@ const mergeDryRunRow = (
originalCreatedFrom: result.originalCreatedFrom ?? '', originalCreatedFrom: result.originalCreatedFrom ?? '',
originalCreatedBefore: result.originalCreatedBefore ?? '', originalCreatedBefore: result.originalCreatedBefore ?? '',
duration: result.duration ?? '', duration: result.duration ?? '',
videoMs: result.videoMs ?? '',
tags: result.tags ?? '', tags: result.tags ?? '',
parentPostIds: String ( parentPostIds: String (
result.parentPostIds ?? currentRow.attributes.parentPostIds ?? '') }, result.parentPostIds ?? currentRow.attributes.parentPostIds ?? '') },
@@ -355,7 +351,6 @@ const buildPreviewResetSnapshot = (
originalCreatedFrom: preview.originalCreatedFrom ?? '', originalCreatedFrom: preview.originalCreatedFrom ?? '',
originalCreatedBefore: preview.originalCreatedBefore ?? '', originalCreatedBefore: preview.originalCreatedBefore ?? '',
duration: preview.duration ?? '', duration: preview.duration ?? '',
videoMs: preview.videoMs ?? '',
tags: preview.tags ?? '', tags: preview.tags ?? '',
parentPostIds: String (preview.parentPostIds ?? '') }, parentPostIds: String (preview.parentPostIds ?? '') },
displayTags: cloneDisplayTags (preview.displayTags), displayTags: cloneDisplayTags (preview.displayTags),
@@ -489,8 +484,6 @@ const mergePreviewRow = (
nextRow.tagSources.automatic = preview.tags ?? '' nextRow.tagSources.automatic = preview.tags ?? ''
if (currentRow.provenance.parentPostIds !== 'manual') if (currentRow.provenance.parentPostIds !== 'manual')
nextRow.attributes.parentPostIds = String (preview.parentPostIds ?? '') nextRow.attributes.parentPostIds = String (preview.parentPostIds ?? '')
if (currentRow.provenance.videoMs !== 'manual')
nextRow.attributes.videoMs = preview.videoMs ?? ''
if (currentRow.provenance.duration !== 'manual') if (currentRow.provenance.duration !== 'manual')
nextRow.attributes.duration = preview.duration ?? '' nextRow.attributes.duration = preview.duration ?? ''
@@ -526,7 +519,6 @@ const buildBulkFormData = (rows: PostImportRow[]): FormData => {
parent_post_ids: row.attributes.parentPostIds ?? '', parent_post_ids: row.attributes.parentPostIds ?? '',
original_created_from: row.attributes.originalCreatedFrom ?? '', original_created_from: row.attributes.originalCreatedFrom ?? '',
original_created_before: row.attributes.originalCreatedBefore ?? '', original_created_before: row.attributes.originalCreatedBefore ?? '',
video_ms: row.attributes.videoMs ?? '',
duration: row.attributes.duration ?? '' })))) duration: row.attributes.duration ?? '' }))))
rows.forEach ((row, index) => { rows.forEach ((row, index) => {
if (!(hasThumbnailBaseValue (row.attributes.thumbnailBase)) && row.thumbnailFile != null) if (!(hasThumbnailBaseValue (row.attributes.thumbnailBase)) && row.thumbnailFile != null)
+1 -5
ファイルの表示
@@ -1197,13 +1197,9 @@ const SettingPage: FC<Props> = ({ user, setUser }) => {
}, [hasUnsavedThemeChanges, savedThemeSlots]) }, [hasUnsavedThemeChanges, savedThemeSlots])
useEffect (() => { useEffect (() => {
registerUnsavedChangesSource ({ return registerUnsavedChangesSource ({
dirty: hasPageUnsavedChanges, dirty: hasPageUnsavedChanges,
discard: discardAllDirtyChanges }) discard: discardAllDirtyChanges })
return () => {
registerUnsavedChangesSource (null)
}
}, [discardAllDirtyChanges, hasPageUnsavedChanges, registerUnsavedChangesSource]) }, [discardAllDirtyChanges, hasPageUnsavedChanges, registerUnsavedChangesSource])
useKeyboardShortcuts ({ useKeyboardShortcuts ({