299 lines
7.0 KiB
TypeScript
Raw Normal View History

2024-10-25 18:38:47 +02:00
import { CurrentUserMark, Mark, MarkLocation } from '../types/mark.ts'
import { hexToNpub } from './nostr.ts'
import { Meta, SignedEventContent } from '../types'
import { Event } from 'nostr-tools'
import { EMPTY } from './const.ts'
2024-09-19 13:15:54 +02:00
import { DrawTool, MarkType } from '../types/drawing.ts'
import {
faT,
faSignature,
faBriefcase,
faIdCard,
faClock,
2024-09-19 13:15:54 +02:00
fa1,
faCalendarDays,
faCheckDouble,
faCircleDot,
faCreditCard,
faHeading,
faImage,
faPaperclip,
faPhone,
faSquareCaretDown,
faSquareCheck,
faStamp,
faTableCellsLarge
} from '@fortawesome/free-solid-svg-icons'
2024-10-25 18:38:47 +02:00
import { Config, optimize } from 'svgo'
/**
* 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
2024-08-27 17:47:19 +02:00
* 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 = <T>(index: number, arr: T[]) => index === arr.length - 1
const isCurrentValueLast = (
currentUserMarks: CurrentUserMark[],
selectedMark: CurrentUserMark,
selectedMarkValue: string
) => {
const filteredMarks = currentUserMarks.filter(
(mark) => mark.id !== selectedMark.id
)
return (
isCurrentUserMarksComplete(filteredMarks) && selectedMarkValue.length > 0
)
}
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))
}
2024-09-19 13:15:54 +02:00
export const DEFAULT_TOOLBOX: DrawTool[] = [
2024-10-07 12:53:43 +02:00
{
identifier: MarkType.TEXT,
icon: faT,
label: 'Text'
},
2024-10-25 18:38:47 +02:00
{
identifier: MarkType.SIGNATURE,
icon: faSignature,
label: 'Signature'
},
{
identifier: MarkType.FULLNAME,
icon: faIdCard,
label: 'Full Name',
2024-09-19 13:15:54 +02:00
isComingSoon: true
},
{
identifier: MarkType.JOBTITLE,
icon: faBriefcase,
label: 'Job Title',
2024-09-19 13:15:54 +02:00
isComingSoon: true
},
{
identifier: MarkType.DATETIME,
icon: faClock,
label: 'Date Time',
2024-09-19 13:15:54 +02:00
isComingSoon: true
},
{
identifier: MarkType.NUMBER,
icon: fa1,
label: 'Number',
2024-09-19 13:15:54 +02:00
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
}
2024-10-25 18:38:47 +02:00
export const optimizeSVG = (location: MarkLocation, paths: string[]) => {
const svgContent = `<svg xmlns="http://www.w3.org/2000/svg" width="${location.width}" height="${location.height}">${paths.map((path) => `<path d="${path}" stroke="black" fill="none" />`).join('')}</svg>`
const optimizedSVG = optimize(svgContent, {
multipass: true, // Optimize multiple times if needed
floatPrecision: 2 // Adjust precision
} as Config)
return optimizedSVG.data
}
export const getOptimizedPaths = (svgString: string) => {
const regex = / d="([^"]*)"/g
const matches = [...svgString.matchAll(regex)]
const pathValues = matches.map((match) => match[1])
return pathValues
}
export {
getCurrentUserMarks,
filterMarksByPubkey,
extractMarksFromSignedMeta,
isCurrentUserMarksComplete,
findNextIncompleteCurrentUserMark,
updateMarks,
updateCurrentUserMarks,
isCurrentValueLast,
getUpdatedMark,
findOtherUserMarks
}