chore(git): merge pull request #187 from 166-caching-fields into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 57s
All checks were successful
Release to Staging / build_and_release (push) Successful in 57s
Reviewed-on: #187
This commit is contained in:
commit
df27451c46
@ -1,13 +1,19 @@
|
||||
[
|
||||
{ "name": "gameplay ", "sub": ["difficulty"] },
|
||||
{ "name": "input", "sub": ["key mapping", "macro"] },
|
||||
{ "name": "visual", "sub": ["textures", "lighting", "character models", "environment models"] },
|
||||
{
|
||||
"name": "visual",
|
||||
"sub": ["textures", "lighting", "character models", "environment models"]
|
||||
},
|
||||
{ "name": "audio", "sub": ["sfx", "music", "voice"] },
|
||||
{ "name": "user interface", "sub": ["hud", "menu"] },
|
||||
{ "name": "quality of life", "sub": ["bug fixes", "performance", "accessibility"] },
|
||||
{
|
||||
"name": "quality of life",
|
||||
"sub": ["bug fixes", "performance", "accessibility"]
|
||||
},
|
||||
"total conversions",
|
||||
"translation",
|
||||
"multiplayer",
|
||||
"clothing",
|
||||
"Mod Manager"
|
||||
"mod manager"
|
||||
]
|
@ -22,7 +22,7 @@ import {
|
||||
ModFormState,
|
||||
ModPageLoaderResult
|
||||
} from '../types'
|
||||
import { initializeFormState } from '../utils'
|
||||
import { initializeFormState, MOD_DRAFT_CACHE_KEY } from '../utils'
|
||||
import { CheckboxField, InputField, InputFieldWithImageUpload } from './Inputs'
|
||||
import { OriginalAuthor } from './OriginalAuthor'
|
||||
import { CategoryAutocomplete } from './CategoryAutocomplete'
|
||||
@ -31,6 +31,7 @@ import { Editor, EditorRef } from './Markdown/Editor'
|
||||
import { MEDIA_OPTIONS } from 'controllers'
|
||||
import { InputError } from './Inputs/Error'
|
||||
import { ImageUpload } from './Inputs/ImageUpload'
|
||||
import { useLocalCache } from 'hooks/useLocalCache'
|
||||
|
||||
interface GameOption {
|
||||
value: string
|
||||
@ -45,9 +46,19 @@ export const ModForm = () => {
|
||||
const submit = useSubmit()
|
||||
const games = useGames()
|
||||
const [gameOptions, setGameOptions] = useState<GameOption[]>([])
|
||||
|
||||
// Enable cache for the new mod
|
||||
const isEditing = typeof mod !== 'undefined'
|
||||
const [cache, setCache, clearCache] =
|
||||
useLocalCache<ModFormState>(MOD_DRAFT_CACHE_KEY)
|
||||
const [formState, setFormState] = useState<ModFormState>(
|
||||
initializeFormState(mod)
|
||||
isEditing ? initializeFormState(mod) : cache ? cache : initializeFormState()
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
!isEditing && setCache(formState)
|
||||
}, [formState, isEditing, setCache])
|
||||
|
||||
const editorRef = useRef<EditorRef>(null)
|
||||
|
||||
useEffect(() => {
|
||||
@ -145,45 +156,42 @@ export const ModForm = () => {
|
||||
)
|
||||
|
||||
const [showConfirmPopup, setShowConfirmPopup] = useState<boolean>(false)
|
||||
const handleReset = () => {
|
||||
const handleReset = useCallback(() => {
|
||||
setShowConfirmPopup(true)
|
||||
}
|
||||
const handleResetConfirm = (confirm: boolean) => {
|
||||
}, [])
|
||||
const handleResetConfirm = useCallback(
|
||||
(confirm: boolean) => {
|
||||
setShowConfirmPopup(false)
|
||||
|
||||
// Cancel if not confirmed
|
||||
if (!confirm) return
|
||||
|
||||
// Editing
|
||||
if (mod) {
|
||||
const initial = initializeFormState(mod)
|
||||
// Reset fields to the initial or original existing data
|
||||
const initialState = initializeFormState(mod)
|
||||
|
||||
// Reset editor
|
||||
editorRef.current?.setMarkdown(initial.body)
|
||||
editorRef.current?.setMarkdown(initialState.body)
|
||||
setFormState(initialState)
|
||||
|
||||
// Reset fields to the original existing data
|
||||
setFormState(initial)
|
||||
return
|
||||
}
|
||||
// Clear cache
|
||||
!isEditing && clearCache()
|
||||
},
|
||||
[clearCache, isEditing, mod]
|
||||
)
|
||||
|
||||
// New - set form state to the initial (clear form state)
|
||||
setFormState(initializeFormState())
|
||||
}
|
||||
const handlePublish = () => {
|
||||
const handlePublish = useCallback(
|
||||
(e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
submit(JSON.stringify(formState), {
|
||||
method: mod ? 'put' : 'post',
|
||||
method: isEditing ? 'put' : 'post',
|
||||
encType: 'application/json'
|
||||
})
|
||||
}
|
||||
},
|
||||
[formState, isEditing, submit]
|
||||
)
|
||||
|
||||
return (
|
||||
<form
|
||||
className='IBMSMSMBS_Write'
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
handlePublish()
|
||||
}}
|
||||
>
|
||||
<form className='IBMSMSMBS_Write' onSubmit={handlePublish}>
|
||||
<GameDropdown
|
||||
options={gameOptions}
|
||||
selected={formState?.game}
|
||||
@ -406,7 +414,7 @@ export const ModForm = () => {
|
||||
navigation.state === 'loading' || navigation.state === 'submitting'
|
||||
}
|
||||
>
|
||||
{mod ? 'Reset' : 'Clear fields'}
|
||||
{isEditing ? 'Reset' : 'Clear fields'}
|
||||
</button>
|
||||
<button
|
||||
className='btn btnMain'
|
||||
|
@ -9,3 +9,4 @@ export * from './useNDKContext'
|
||||
export * from './useScrollDisable'
|
||||
export * from './useLocalStorage'
|
||||
export * from './useSessionStorage'
|
||||
export * from './useLocalCache'
|
||||
|
37
src/hooks/useLocalCache.tsx
Normal file
37
src/hooks/useLocalCache.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { setLocalStorageItem, removeLocalStorageItem } from 'utils'
|
||||
|
||||
export function useLocalCache<T>(
|
||||
key: string
|
||||
): [
|
||||
T | undefined,
|
||||
React.Dispatch<React.SetStateAction<T | undefined>>,
|
||||
() => void
|
||||
] {
|
||||
const [cache, setCache] = useState<T | undefined>(() => {
|
||||
const storedValue = window.localStorage.getItem(key)
|
||||
if (storedValue === null) return undefined
|
||||
|
||||
// Parse the value
|
||||
const parsedStoredValue = JSON.parse(storedValue)
|
||||
return parsedStoredValue
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (cache) {
|
||||
setLocalStorageItem(key, JSON.stringify(cache))
|
||||
} else {
|
||||
removeLocalStorageItem(key)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
}, [cache, key])
|
||||
|
||||
const clearCache = useCallback(() => {
|
||||
setCache(undefined)
|
||||
}, [])
|
||||
|
||||
return [cache, setCache, clearCache]
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import {
|
||||
getLocalStorageItem,
|
||||
mergeWithInitialValue,
|
||||
removeLocalStorageItem,
|
||||
setLocalStorageItem
|
||||
} from 'utils'
|
||||
@ -10,17 +11,6 @@ const useLocalStorageSubscribe = (callback: () => void) => {
|
||||
return () => window.removeEventListener('storage', callback)
|
||||
}
|
||||
|
||||
function mergeWithInitialValue<T>(storedValue: T, initialValue: T): T {
|
||||
if (
|
||||
!Array.isArray(storedValue) &&
|
||||
typeof storedValue === 'object' &&
|
||||
storedValue !== null
|
||||
) {
|
||||
return { ...initialValue, ...storedValue }
|
||||
}
|
||||
return storedValue
|
||||
}
|
||||
|
||||
export function useLocalStorage<T>(
|
||||
key: string,
|
||||
initialValue: T
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import {
|
||||
getSessionStorageItem,
|
||||
mergeWithInitialValue,
|
||||
removeSessionStorageItem,
|
||||
setSessionStorageItem
|
||||
} from 'utils'
|
||||
@ -10,17 +11,6 @@ const useSessionStorageSubscribe = (callback: () => void) => {
|
||||
return () => window.removeEventListener('sessionStorage', callback)
|
||||
}
|
||||
|
||||
function mergeWithInitialValue<T>(storedValue: T, initialValue: T): T {
|
||||
if (
|
||||
!Array.isArray(storedValue) &&
|
||||
typeof storedValue === 'object' &&
|
||||
storedValue !== null
|
||||
) {
|
||||
return { ...initialValue, ...storedValue }
|
||||
}
|
||||
return storedValue
|
||||
}
|
||||
|
||||
export function useSessionStorage<T>(
|
||||
key: string,
|
||||
initialValue: T
|
||||
|
@ -12,7 +12,9 @@ import {
|
||||
isValidUrl,
|
||||
log,
|
||||
LogType,
|
||||
now
|
||||
MOD_DRAFT_CACHE_KEY,
|
||||
now,
|
||||
removeLocalStorageItem
|
||||
} from 'utils'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { T_TAG_VALUE } from '../../constants'
|
||||
@ -141,6 +143,8 @@ export const submitModRouteAction =
|
||||
)}`
|
||||
)
|
||||
|
||||
!isEditing && removeLocalStorageItem(MOD_DRAFT_CACHE_KEY)
|
||||
|
||||
const naddr = nip19.naddrEncode({
|
||||
identifier: aTag,
|
||||
pubkey: signedEvent.pubkey,
|
||||
@ -209,13 +213,6 @@ const validateState = async (
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
formState.repost &&
|
||||
(!formState.originalAuthor || formState.originalAuthor === '')
|
||||
) {
|
||||
errors.originalAuthor = 'Original author field can not be empty'
|
||||
}
|
||||
|
||||
if (!formState.tags || formState.tags === '') {
|
||||
errors.tags = 'Tags field can not be empty'
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ import { ActionFunctionArgs, redirect } from 'react-router-dom'
|
||||
import { getBlogPageRoute } from 'routes'
|
||||
import { BlogFormErrors, BlogEventSubmitForm, BlogEventEditForm } from 'types'
|
||||
import {
|
||||
BLOG_DRAFT_CACHE_KEY,
|
||||
isReachable,
|
||||
isValidImageUrl,
|
||||
log,
|
||||
LogType,
|
||||
now,
|
||||
parseFormData
|
||||
removeLocalStorageItem
|
||||
} from 'utils'
|
||||
import { kinds, UnsignedEvent, Event, nip19 } from 'nostr-tools'
|
||||
import { toast } from 'react-toastify'
|
||||
@ -43,12 +44,9 @@ export const writeRouteAction =
|
||||
}
|
||||
|
||||
// Get the form data from submit request
|
||||
const formData = await request.formData()
|
||||
|
||||
// Parse the the data
|
||||
const formSubmit = parseFormData<BlogEventSubmitForm | BlogEventEditForm>(
|
||||
formData
|
||||
)
|
||||
const formSubmit = (await request.json()) as
|
||||
| BlogEventSubmitForm
|
||||
| BlogEventEditForm
|
||||
|
||||
// Check for errors
|
||||
const formErrors = await validateFormData(formSubmit)
|
||||
@ -80,7 +78,7 @@ export const writeRouteAction =
|
||||
const tTags = formSubmit
|
||||
.tags!.toLowerCase()
|
||||
.split(',')
|
||||
.map((t) => ['t', t])
|
||||
.map((t) => ['t', t.trim()])
|
||||
|
||||
const tags = [
|
||||
['d', uuid],
|
||||
@ -95,7 +93,7 @@ export const writeRouteAction =
|
||||
|
||||
// Add NSFW tag, L label namespace standardized tag
|
||||
// https://github.com/nostr-protocol/nips/blob/2838e3bd51ac00bd63c4cef1601ae09935e7dd56/README.md#standardized-tags
|
||||
if (formSubmit.nsfw === 'on') tags.push(['L', 'content-warning'])
|
||||
if (formSubmit.nsfw) tags.push(['L', 'content-warning'])
|
||||
|
||||
const unsignedEvent: UnsignedEvent = {
|
||||
kind: kinds.LongFormArticle,
|
||||
@ -128,6 +126,9 @@ export const writeRouteAction =
|
||||
'\n'
|
||||
)}`
|
||||
)
|
||||
|
||||
!isEditing && removeLocalStorageItem(BLOG_DRAFT_CACHE_KEY)
|
||||
|
||||
const naddr = nip19.naddrEncode({
|
||||
identifier: uuid,
|
||||
pubkey: signedEvent.pubkey,
|
||||
|
@ -1,60 +1,123 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
Form,
|
||||
useActionData,
|
||||
useLoaderData,
|
||||
useNavigation
|
||||
useNavigation,
|
||||
useSubmit
|
||||
} from 'react-router-dom'
|
||||
import {
|
||||
CheckboxFieldUncontrolled,
|
||||
InputFieldUncontrolled,
|
||||
CheckboxField,
|
||||
InputField,
|
||||
InputFieldWithImageUpload
|
||||
} from '../../components/Inputs'
|
||||
import { ProfileSection } from '../../components/ProfileSection'
|
||||
import { useAppSelector } from '../../hooks'
|
||||
import { BlogFormErrors, BlogPageLoaderResult } from 'types'
|
||||
import '../../styles/innerPage.css'
|
||||
import '../../styles/styles.css'
|
||||
import '../../styles/write.css'
|
||||
} from 'components/Inputs'
|
||||
import { ProfileSection } from 'components/ProfileSection'
|
||||
import { useAppSelector, useLocalCache } from 'hooks'
|
||||
import {
|
||||
BlogEventEditForm,
|
||||
BlogEventSubmitForm,
|
||||
BlogFormErrors,
|
||||
BlogPageLoaderResult
|
||||
} from 'types'
|
||||
import { LoadingSpinner } from 'components/LoadingSpinner'
|
||||
import { AlertPopup } from 'components/AlertPopup'
|
||||
import { Editor, EditorRef } from 'components/Markdown/Editor'
|
||||
import { InputError } from 'components/Inputs/Error'
|
||||
import { BLOG_DRAFT_CACHE_KEY, initializeBlogForm } from 'utils'
|
||||
import 'styles/innerPage.css'
|
||||
import 'styles/styles.css'
|
||||
import 'styles/write.css'
|
||||
|
||||
export const WritePage = () => {
|
||||
const userState = useAppSelector((state) => state.user)
|
||||
const data = useLoaderData() as BlogPageLoaderResult
|
||||
|
||||
const formErrors = useActionData() as BlogFormErrors
|
||||
const navigation = useNavigation()
|
||||
const submit = useSubmit()
|
||||
|
||||
const blog = data?.blog
|
||||
const title = data?.blog ? 'Edit blog post' : 'Submit a blog post'
|
||||
const [content, setContent] = useState(blog?.content || '')
|
||||
const [image, setImage] = useState(blog?.image || '')
|
||||
|
||||
const formRef = useRef<HTMLFormElement>(null)
|
||||
// Enable cache for the new blog
|
||||
const isEditing = typeof data?.blog !== 'undefined'
|
||||
const [cache, setCache, clearCache] =
|
||||
useLocalCache<BlogEventSubmitForm>(BLOG_DRAFT_CACHE_KEY)
|
||||
|
||||
const title = isEditing ? 'Edit blog post' : 'Submit a blog post'
|
||||
const [formState, setFormState] = useState<
|
||||
BlogEventSubmitForm | BlogEventEditForm
|
||||
>(isEditing ? initializeBlogForm(blog) : cache ? cache : initializeBlogForm())
|
||||
|
||||
useEffect(() => {
|
||||
!isEditing && setCache(formState)
|
||||
}, [formState, isEditing, setCache])
|
||||
|
||||
const editorRef = useRef<EditorRef>(null)
|
||||
|
||||
const [showConfirmPopup, setShowConfirmPopup] = useState<boolean>(false)
|
||||
const handleReset = () => {
|
||||
const handleReset = useCallback(() => {
|
||||
setShowConfirmPopup(true)
|
||||
}
|
||||
const handleResetConfirm = (confirm: boolean) => {
|
||||
}, [])
|
||||
const handleResetConfirm = useCallback(
|
||||
(confirm: boolean) => {
|
||||
setShowConfirmPopup(false)
|
||||
|
||||
// Cancel if not confirmed
|
||||
if (!confirm) return
|
||||
|
||||
// Reset featured image
|
||||
setImage(blog?.image || '')
|
||||
const initialState = initializeBlogForm(blog)
|
||||
|
||||
// Reset editor
|
||||
if (blog?.content) {
|
||||
editorRef.current?.setMarkdown(blog?.content)
|
||||
}
|
||||
editorRef.current?.setMarkdown(initialState.content)
|
||||
setFormState(initialState)
|
||||
|
||||
formRef.current?.reset()
|
||||
}
|
||||
// Clear cache
|
||||
!isEditing && clearCache()
|
||||
},
|
||||
[blog, clearCache, isEditing]
|
||||
)
|
||||
|
||||
const handleImageChange = useCallback((_name: string, value: string) => {
|
||||
setFormState((prev) => ({
|
||||
...prev,
|
||||
image: value
|
||||
}))
|
||||
}, [])
|
||||
|
||||
const handleInputChange = useCallback((name: string, value: string) => {
|
||||
setFormState((prevState) => ({
|
||||
...prevState,
|
||||
[name]: value
|
||||
}))
|
||||
}, [])
|
||||
|
||||
const handleEditorChange = useCallback(
|
||||
(md: string) => {
|
||||
handleInputChange('content', md)
|
||||
},
|
||||
[handleInputChange]
|
||||
)
|
||||
|
||||
const handleCheckboxChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, checked } = e.target
|
||||
setFormState((prevState) => ({
|
||||
...prevState,
|
||||
[name]: checked
|
||||
}))
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const handleFormSubmit = useCallback(
|
||||
(e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
submit(JSON.stringify(formState), {
|
||||
method: isEditing ? 'put' : 'post',
|
||||
encType: 'application/json'
|
||||
})
|
||||
},
|
||||
[formState, isEditing, submit]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className='InnerBodyMain'>
|
||||
@ -71,82 +134,63 @@ export const WritePage = () => {
|
||||
{navigation.state === 'submitting' && (
|
||||
<LoadingSpinner desc='Publishing blog to relays' />
|
||||
)}
|
||||
<Form
|
||||
ref={formRef}
|
||||
className='IBMSMSMBS_Write'
|
||||
method={blog ? 'put' : 'post'}
|
||||
>
|
||||
<InputFieldUncontrolled
|
||||
<form className='IBMSMSMBS_Write' onSubmit={handleFormSubmit}>
|
||||
<InputField
|
||||
label='Title'
|
||||
name='title'
|
||||
defaultValue={blog?.title}
|
||||
value={formState.title}
|
||||
error={formErrors?.title}
|
||||
onChange={handleInputChange}
|
||||
placeholder='Blog title'
|
||||
/>
|
||||
<div className='inputLabelWrapperMain'>
|
||||
<label className='form-label labelMain'>Content</label>
|
||||
<div className='inputMain'>
|
||||
<Editor
|
||||
ref={editorRef}
|
||||
markdown={content}
|
||||
onChange={(md) => {
|
||||
setContent(md)
|
||||
}}
|
||||
markdown={formState.content}
|
||||
onChange={handleEditorChange}
|
||||
/>
|
||||
</div>
|
||||
{typeof formErrors?.content !== 'undefined' && (
|
||||
<InputError message={formErrors?.content} />
|
||||
)}
|
||||
{/* encode to keep the markdown formatting */}
|
||||
<input
|
||||
name='content'
|
||||
hidden
|
||||
value={encodeURIComponent(content)}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
<InputFieldWithImageUpload
|
||||
label='Featured Image URL'
|
||||
name='image'
|
||||
inputMode='url'
|
||||
value={image}
|
||||
value={formState.image}
|
||||
error={formErrors?.image}
|
||||
onInputChange={(_, value) => setImage(value)}
|
||||
onInputChange={handleImageChange}
|
||||
placeholder='Image URL'
|
||||
/>
|
||||
<InputFieldUncontrolled
|
||||
<InputField
|
||||
label='Summary'
|
||||
name='summary'
|
||||
type='textarea'
|
||||
defaultValue={blog?.summary}
|
||||
value={formState.summary}
|
||||
error={formErrors?.summary}
|
||||
onChange={handleInputChange}
|
||||
placeholder={'This is a quick description of my blog'}
|
||||
/>
|
||||
<InputFieldUncontrolled
|
||||
<InputField
|
||||
label='Tags'
|
||||
description='Separate each tag with a comma. (Example: tag1, tag2, tag3)'
|
||||
placeholder='Tags'
|
||||
name='tags'
|
||||
defaultValue={blog?.tTags?.join(', ')}
|
||||
value={formState.tags}
|
||||
error={formErrors?.tags}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
<CheckboxFieldUncontrolled
|
||||
<CheckboxField
|
||||
label='This post is not safe for work (NSFW)'
|
||||
name='nsfw'
|
||||
defaultChecked={blog?.nsfw}
|
||||
isChecked={formState.nsfw}
|
||||
handleChange={handleCheckboxChange}
|
||||
type='stylized'
|
||||
/>
|
||||
{typeof blog?.dTag !== 'undefined' && (
|
||||
<input name='dTag' hidden value={blog.dTag} readOnly />
|
||||
)}
|
||||
{typeof blog?.rTag !== 'undefined' && (
|
||||
<input name='rTag' hidden value={blog.rTag} readOnly />
|
||||
)}
|
||||
{typeof blog?.published_at !== 'undefined' && (
|
||||
<input
|
||||
name='published_at'
|
||||
hidden
|
||||
value={blog.published_at}
|
||||
readOnly
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className='IBMSMSMBS_WriteAction'>
|
||||
<button
|
||||
className='btn btnMain'
|
||||
@ -157,7 +201,7 @@ export const WritePage = () => {
|
||||
navigation.state === 'submitting'
|
||||
}
|
||||
>
|
||||
{blog ? 'Reset' : 'Clear fields'}
|
||||
{isEditing ? 'Reset' : 'Clear fields'}
|
||||
</button>
|
||||
<button
|
||||
className='btn btnMain'
|
||||
@ -178,13 +222,13 @@ export const WritePage = () => {
|
||||
handleClose={() => setShowConfirmPopup(false)}
|
||||
header={'Are you sure?'}
|
||||
label={
|
||||
blog
|
||||
isEditing
|
||||
? `Are you sure you want to clear all changes?`
|
||||
: `Are you sure you want to clear all field data?`
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
</form>
|
||||
</div>
|
||||
{userState.auth && userState.user?.pubkey && (
|
||||
<ProfileSection pubkey={userState.user.pubkey as string} />
|
||||
|
@ -20,9 +20,7 @@ export interface BlogDetails extends BlogForm {
|
||||
tTags: string[]
|
||||
}
|
||||
|
||||
export interface BlogEventSubmitForm extends Omit<BlogForm, 'nsfw'> {
|
||||
nsfw: string
|
||||
}
|
||||
export interface BlogEventSubmitForm extends BlogForm {}
|
||||
|
||||
export interface BlogEventEditForm extends BlogEventSubmitForm {
|
||||
dTag: string
|
||||
|
@ -1,5 +1,10 @@
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk'
|
||||
import { BlogCardDetails, BlogDetails } from 'types'
|
||||
import {
|
||||
BlogCardDetails,
|
||||
BlogDetails,
|
||||
BlogEventEditForm,
|
||||
BlogEventSubmitForm
|
||||
} from 'types'
|
||||
import { getFirstTagValue, getFirstTagValueAsInt, getTagValues } from './nostr'
|
||||
import { kinds, nip19 } from 'nostr-tools'
|
||||
|
||||
@ -50,3 +55,25 @@ export const extractBlogCardDetails = (
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
|
||||
export const initializeBlogForm = (
|
||||
blog?: Partial<BlogDetails>
|
||||
): BlogEventSubmitForm | BlogEventEditForm => ({
|
||||
content: blog?.content || '',
|
||||
image: blog?.image || '',
|
||||
nsfw: blog?.nsfw || false,
|
||||
summary: blog?.summary || '',
|
||||
title: blog?.title || '',
|
||||
tags: blog?.tTags?.join(', ') || '',
|
||||
...(blog?.aTag && {
|
||||
aTag: blog.aTag
|
||||
}),
|
||||
...(blog?.dTag && {
|
||||
dTag: blog.dTag
|
||||
}),
|
||||
...(blog?.published_at && {
|
||||
published_at: blog.published_at
|
||||
})
|
||||
})
|
||||
|
||||
export const BLOG_DRAFT_CACHE_KEY = 'draft-blog'
|
||||
|
@ -1,3 +1,4 @@
|
||||
import _ from 'lodash'
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk'
|
||||
import { Event } from 'nostr-tools'
|
||||
import { ModDetails, ModFormState } from '../types'
|
||||
@ -131,9 +132,11 @@ export const initializeFormState = (
|
||||
originalAuthor: existingModData?.originalAuthor || undefined,
|
||||
screenshotsUrls: existingModData?.screenshotsUrls || [''],
|
||||
tags: existingModData?.tags.join(',') || '',
|
||||
lTags: existingModData?.lTags || [],
|
||||
LTags: existingModData?.LTags || [],
|
||||
downloadUrls: existingModData?.downloadUrls || [
|
||||
lTags: existingModData ? _.clone(existingModData.lTags) : [],
|
||||
LTags: existingModData ? _.clone(existingModData.lTags) : [],
|
||||
downloadUrls: existingModData
|
||||
? _.cloneDeep(existingModData.downloadUrls)
|
||||
: [
|
||||
{
|
||||
url: '',
|
||||
hash: '',
|
||||
@ -144,3 +147,5 @@ export const initializeFormState = (
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export const MOD_DRAFT_CACHE_KEY = 'draft-mod'
|
||||
|
@ -180,3 +180,14 @@ export const getFallbackPubkey = () => {
|
||||
// Silently ignore
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeWithInitialValue<T>(storedValue: T, initialValue: T): T {
|
||||
if (
|
||||
!Array.isArray(storedValue) &&
|
||||
typeof storedValue === 'object' &&
|
||||
storedValue !== null
|
||||
) {
|
||||
return { ...initialValue, ...storedValue }
|
||||
}
|
||||
return storedValue
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user