Untitled
unknown
typescript
8 months ago
5.9 kB
9
Indexable
export class PostFormModel {
postPartsAtom = reatomArray<AtomMut<PostPart>>([])
.pipe(withReset())
.pipe(withInit(() => [createPartAtom(generateTextPart()) as AtomMut<PostPart>]))
.pipe(withSetInterceptor(normalizeParts))
focusedTextPart = atom<FocusedTextPartValue>(null).pipe(withReset())
activeSidebar = atom<ActiveSidebarInfo | null>(null).pipe(withReset())
scheduledDatetime = atom<Date | null>(null).pipe(withReset())
constructor(post?: CommunityPost) {
if (post) {
const segments = splitKeep(post.content, /(\[image]|\[poll]|\[book]|\[bookmark])/g)
const parts: PostPart[] = []
let imageIdx = 0
for (const segment of segments) {
if (segment === '[book]') {
parts.push(generateBookPart(post.book_meta))
} else if (segment === '[bookmark]') {
parts.push(generateBookmarkPart(post.bookmark_meta))
} else if (segment === '[poll]') {
parts.push(generatePollPart(new PollFormModel({
title: post.poll_meta.title,
multipleChoices: post.poll_meta.multiple,
choices: post.poll_meta.options.map(opt => opt.option),
})))
} else if (segment === '[image]') {
// parts.push(atom(generateImagesPart(post.images[imageIdx])))
imageIdx++
} else {
parts.push(generateTextPart(segment.trim().replaceAll('\r\n', '\n')))
}
}
this.postPartsAtom.change(ctx, parts.map(part => createPartAtom(part)))
}
}
resetAll = action((ctx) => {
this.postPartsAtom.reset(ctx)
this.focusedTextPart.reset(ctx)
this.activeSidebar.reset(ctx)
this.scheduledDatetime.reset(ctx)
})
setFocusedTextAtom = action((ctx, part: AtomMut<TextPart>, ref: HTMLTextAreaElement | null) => {
this.focusedTextPart(ctx, [part, ref])
})
unsetFocusedTextAtom = action((ctx) => {
this.focusedTextPart(ctx, null)
})
insertMedia = action((ctx, part: PostPart, focusedText: FocusedTextPartValue) => {
const newAtoms = [createPartAtom(part)]
const parts = ctx.get(this.postPartsAtom)
if (focusedText) {
const [focusedTextAtom, focusedTextRef] = focusedText
const focusedTextAtomIdx = parts.findIndex(item => item === focusedTextAtom)
const focusPosition = focusedTextRef?.selectionStart
if (focusedTextAtomIdx > -1 && focusPosition !== undefined) {
const textValue = ctx.get(focusedTextAtom)
const [before, after] = splitInPosition(textValue.content, focusPosition)
before.trim().length && newAtoms.unshift(createPartAtom(generateTextPart(before.trim())) as AtomMut<PostPart>)
after.trim().length && newAtoms.push(createPartAtom(generateTextPart(after.trim())) as AtomMut<PostPart>)
}
this.postPartsAtom.change(ctx, [
...parts.slice(0, focusedTextAtomIdx),
...newAtoms,
...parts.slice(focusedTextAtomIdx + 1),
])
return
}
this.postPartsAtom.change(ctx, [...parts, ...newAtoms])
})
insertPart = action((ctx, {atom, i}: { atom: AtomMut<PostPart>, i: number }) => {
this.postPartsAtom.change(ctx, v => [...v.slice(0, i), atom, ...v.slice(i)])
})
addPart = action((ctx, part: PostPart) => {
const newAtoms = [createPartAtom(part)]
if (part.type !== 'text') {
newAtoms.push(createPartAtom(generateTextPart('')) as AtomMut<PostPart>)
}
this.postPartsAtom.change(ctx, v => [...v, ...newAtoms])
})
removePart = action((ctx, id: string) => {
const partsValue = ctx.get(this.postPartsAtom)
const removingI = partsValue.findIndex(atom => ctx.get(atom).id === id)
this.postPartsAtom.change(ctx, [...partsValue.slice(0, removingI), ...partsValue.slice(removingI + 1)])
})
rearrangeParts = action(ctx => {
this.postPartsAtom.change(ctx, v => v)
})
getSerialised() {
const totalImages: File[] = []
let totalText = ''
let poll: PollForm | undefined
let bookId: Book["id"] | undefined
let bookmarkId: Bookmark["id"] | undefined
const partsValue = ctx.get(this.postPartsAtom)
for (const partAtom of partsValue) {
const partValue = ctx.get(partAtom)
switch (partValue.type) {
case 'text':
totalText += partValue.content
break
case 'images':
totalText += partValue.images.map(() => '[image]').join('')
totalImages.push(...partValue.images.map(item => item.file))
break
case 'book':
totalText += '[book]'
bookId = partValue.book.id
break
case 'audio_bookmark':
totalText += '[bookmark]'
bookmarkId = partValue.bookmark.id
break
case 'poll':
poll = partValue.poll.toObject()
break
}
}
return {
totalText,
totalImages,
bookId,
bookmarkId,
poll,
publicationDatetime: ctx.get(this.scheduledDatetime),
}
}
symbolsCount = atom(ctx => ctx.spy(this.postPartsAtom)
.reduce((count, partAtom) => {
const part = ctx.spy(partAtom)
return part.type === 'text'
? count + part.content?.length
: count
}, 0))
books = atom(ctx => ctx.spy(this.postPartsAtom)
.map(partAtom => ctx.get(partAtom))
.filter(part => part?.type === 'book'))
bookmarks = atom(ctx => ctx.spy(this.postPartsAtom)
.map(partAtom => ctx.get(partAtom))
.filter(part => part?.type === 'audio_bookmark'))
isEmpty = atom(ctx => ctx.spy(this.postPartsAtom)
.map(partAtom => ctx.spy(partAtom))
.find(part => {
if (part.type === 'text') return part.content.trim().length
if (part.type === 'images') return part.images.length
return true
}) === undefined)
lastPostPart = atom<PostPart | undefined>(ctx => {
const lastAtom = ctx.spy(this.postPartsAtom).at(-1)
return lastAtom ? ctx.spy(lastAtom) : undefined
})
}
Editor is loading...
Leave a Comment