import { CurrentUserMark, Mark } from '../types/mark.ts' import { hexToNpub } from './nostr.ts' import { Meta, SignedEventContent } from '../types' import { Event } from 'nostr-tools' import { EMPTY } from './const.ts' import { DrawTool, MarkType } from '../types/drawing.ts' import { faT, faSignature, faBriefcase, faIdCard, faClock, fa1, faCalendarDays, faCheckDouble, faCircleDot, faCreditCard, faHeading, faImage, faPaperclip, faPhone, faSquareCaretDown, faSquareCheck, faStamp, faTableCellsLarge } from '@fortawesome/free-solid-svg-icons' import { MARK_TYPE_CONFIG } from '../components/MarkTypeStrategy/MarkStrategy.tsx' /** * Takes in an array of Marks already filtered by User. * Returns an array of CurrentUserMarks with correct values mix-and-matched. * @param marks - default Marks extracted from Meta * @param signedMetaMarks - signed user Marks extracted from DocSignatures */ const getCurrentUserMarks = ( marks: Mark[], signedMetaMarks: Mark[] ): CurrentUserMark[] => { return marks.map((mark, index, arr) => { const signedMark = signedMetaMarks.find((m) => m.id === mark.id) return { mark, currentValue: signedMark?.value ?? EMPTY, id: index + 1, isLast: isLast(index, arr), isCompleted: !!signedMark?.value } }) } /** * Returns next incomplete CurrentUserMark if there is one * @param usersMarks */ const findNextIncompleteCurrentUserMark = ( usersMarks: CurrentUserMark[] ): CurrentUserMark | undefined => { return usersMarks.find((mark) => !mark.isCompleted) } /** * Returns Marks that are assigned to a specific user * @param marks * @param pubkey */ const filterMarksByPubkey = (marks: Mark[], pubkey: string): Mark[] => { return marks.filter((mark) => mark.npub === hexToNpub(pubkey)) } /** * Takes Signed Doc Signatures part of Meta and extracts * all Marks into one flat array, regardless of the user. * @param meta */ const extractMarksFromSignedMeta = (meta: Meta): Mark[] => { return Object.values(meta.docSignatures) .map((val: string) => JSON.parse(val as string)) .map((val: Event) => JSON.parse(val.content)) .flatMap((val: SignedEventContent) => val.marks) } /** * Checks the CurrentUserMarks array that every element in that array has been * marked as complete. * @param currentUserMarks */ const isCurrentUserMarksComplete = ( currentUserMarks: CurrentUserMark[] ): boolean => { return currentUserMarks.every((mark) => mark.isCompleted) } /** * Inserts an updated mark into an existing array of marks. Returns a copy of the * existing array with a new value inserted * @param marks * @param markToUpdate */ const updateMarks = (marks: Mark[], markToUpdate: Mark): Mark[] => { const indexToUpdate = marks.findIndex((mark) => mark.id === markToUpdate.id) return [ ...marks.slice(0, indexToUpdate), markToUpdate, ...marks.slice(indexToUpdate + 1) ] } const updateCurrentUserMarks = ( currentUserMarks: CurrentUserMark[], markToUpdate: CurrentUserMark ): CurrentUserMark[] => { const indexToUpdate = currentUserMarks.findIndex( (m) => m.mark.id === markToUpdate.mark.id ) return [ ...currentUserMarks.slice(0, indexToUpdate), markToUpdate, ...currentUserMarks.slice(indexToUpdate + 1) ] } const isLast = (index: number, arr: T[]) => index === arr.length - 1 const isCurrentValueLast = ( currentUserMarks: CurrentUserMark[], selectedMark: CurrentUserMark | null, selectedMarkValue: string ) => { if (selectedMark && currentUserMarks.length > 0) { const filteredMarks = currentUserMarks.filter( (mark) => mark.id !== selectedMark.id ) return ( isCurrentUserMarksComplete(filteredMarks) && selectedMarkValue.length > 0 ) } return true } const getUpdatedMark = ( selectedMark: CurrentUserMark, selectedMarkValue: string ): CurrentUserMark => { return { ...selectedMark, currentValue: selectedMarkValue, isCompleted: !!selectedMarkValue, mark: { ...selectedMark.mark, value: selectedMarkValue } } } const findOtherUserMarks = (marks: Mark[], pubkey: string): Mark[] => { return marks.filter((mark) => mark.npub !== hexToNpub(pubkey)) } export const DEFAULT_TOOLBOX: DrawTool[] = [ { identifier: MarkType.TEXT, icon: faT, label: 'Text' }, { identifier: MarkType.SIGNATURE, icon: faSignature, label: 'Signature' }, { identifier: MarkType.FULLNAME, icon: faIdCard, label: 'Full Name', isComingSoon: true }, { identifier: MarkType.JOBTITLE, icon: faBriefcase, label: 'Job Title', isComingSoon: true }, { identifier: MarkType.DATETIME, icon: faClock, label: 'Date Time', isComingSoon: true }, { identifier: MarkType.NUMBER, icon: fa1, label: 'Number', isComingSoon: true }, { identifier: MarkType.INITIALS, icon: faHeading, label: 'Initials', isHidden: true }, { identifier: MarkType.DATE, icon: faCalendarDays, label: 'Date', isHidden: true }, { identifier: MarkType.IMAGES, icon: faImage, label: 'Images', isHidden: true }, { identifier: MarkType.CHECKBOX, icon: faSquareCheck, label: 'Checkbox', isHidden: true }, { identifier: MarkType.MULTIPLE, icon: faCheckDouble, label: 'Multiple', isHidden: true }, { identifier: MarkType.FILE, icon: faPaperclip, label: 'File', isHidden: true }, { identifier: MarkType.RADIO, icon: faCircleDot, label: 'Radio', isHidden: true }, { identifier: MarkType.SELECT, icon: faSquareCaretDown, label: 'Select', isHidden: true }, { identifier: MarkType.CELLS, icon: faTableCellsLarge, label: 'Cells', isHidden: true }, { identifier: MarkType.STAMP, icon: faStamp, label: 'Stamp', isHidden: true }, { identifier: MarkType.PAYMENT, icon: faCreditCard, label: 'Payment', isHidden: true }, { identifier: MarkType.PHONE, icon: faPhone, label: 'Phone', isHidden: true } ] export const getToolboxLabelByMarkType = (markType: MarkType) => { return DEFAULT_TOOLBOX.find((t) => t.identifier === markType)?.label } export const encryptAndUploadMarks = async ( marks: Mark[], encryptionKey?: string ) => { const _marks = [...marks] for (let i = 0; i < _marks.length; i++) { const mark = _marks[i] const hasProcess = mark.type in MARK_TYPE_CONFIG && typeof MARK_TYPE_CONFIG[mark.type]?.encryptAndUpload === 'function' if (hasProcess) { const value = mark.value! const processFn = MARK_TYPE_CONFIG[mark.type]?.encryptAndUpload if (processFn) { mark.value = await processFn(value, encryptionKey) } } } return _marks } export { getCurrentUserMarks, filterMarksByPubkey, extractMarksFromSignedMeta, isCurrentUserMarksComplete, findNextIncompleteCurrentUserMark, updateMarks, updateCurrentUserMarks, isCurrentValueLast, getUpdatedMark, findOtherUserMarks }