diff --git a/packages/ui/src/Slide/index.tsx b/packages/ui/src/Slide/index.tsx index 20cd652..39da3e7 100644 --- a/packages/ui/src/Slide/index.tsx +++ b/packages/ui/src/Slide/index.tsx @@ -2,6 +2,7 @@ import { Box, Text } from "@chakra-ui/react"; type PropTypes = { heading?: string; + headingIsFaded?: boolean; isActive?: boolean; aspectRatio?: number; onClick?: () => void; @@ -10,6 +11,7 @@ type PropTypes = { export const Slide = ({ heading, + headingIsFaded, isActive, aspectRatio = 16 / 9, onClick, @@ -18,7 +20,13 @@ export const Slide = ({ return ( {heading && ( - + {heading} )} diff --git a/plugins/myworshiplist/package.json b/plugins/myworshiplist/package.json index 99c773c..e9ed4dc 100644 --- a/plugins/myworshiplist/package.json +++ b/plugins/myworshiplist/package.json @@ -21,6 +21,13 @@ "@repo/base-plugin": "*", "@repo/ui": "*", "@tanstack/react-query": "^5.59.16", + "@tiptap/core": "^2.9.1", + "@tiptap/extension-document": "^2.9.1", + "@tiptap/extension-history": "^2.9.1", + "@tiptap/extension-paragraph": "^2.9.1", + "@tiptap/extension-text": "^2.9.1", + "@tiptap/pm": "^2.9.1", + "@tiptap/react": "^2.9.1", "@trpc/client": "^11.0.0-rc.593", "@trpc/react-query": "^11.0.0-rc.593", "@trpc/server": "^11.0.0-rc.593", diff --git a/plugins/myworshiplist/src/importer/myworshiplist.ts b/plugins/myworshiplist/src/importer/myworshiplist.ts new file mode 100644 index 0000000..a34dc7e --- /dev/null +++ b/plugins/myworshiplist/src/importer/myworshiplist.ts @@ -0,0 +1,43 @@ +export const convertMWLData = (content: string) => { + return convertHeading( + removeAuxiliaryText( + cleanWhiteSpace(convertChords(content.split(/
|\n/gm) ?? [])), + ), + ).join("\n"); +}; + +const convertChords = (content: string[]) => { + return content.map((val) => { + return val.match(/x[01]/) ? "." + val : val; + }); +}; +const cleanWhiteSpace = (content: string[]) => { + return content.map((x) => x.replace(/\s+/g, " ").trim()); +}; +const removeAuxiliaryText = (content: string[]) => { + return content.filter((songLine) => { + const match1 = songLine.match( + /^(\s*)repeat(\s*)(verse|bridge|pre-? ?chorus|chorus|end|tag|intro)? ?(\d+)?(.*)$/i, + ); + + if (match1 && match1?.length > 0) { + return false; + } + + const match2 = songLine.match(/^(\s*)solo(\s*)$/i); + + if (match2 && match2?.length > 0) { + return false; + } + + return true; + }); +}; +const convertHeading = (content: string[]) => { + return content.map((val) => { + const matches = val.match( + /^(\s*)\[?(verse|bridge|pre-? ?chorus|chorus|end|ending|outro|tag|instrumental|interlude) ?(\d+)?\]?(\s*)$/i, + ); + return matches ? "[" + val.replace(/[\[\]]/g, "") + "]" : val; + }); +}; diff --git a/plugins/myworshiplist/src/index.ts b/plugins/myworshiplist/src/index.ts index c8106ac..a0781bc 100644 --- a/plugins/myworshiplist/src/index.ts +++ b/plugins/myworshiplist/src/index.ts @@ -17,6 +17,7 @@ import { rendererWebComponentTag, } from "./consts"; import { getSongData } from "./data"; +import { convertMWLData } from "./importer/myworshiplist"; import { MyWorshipListData, PluginRendererData, Song } from "./types"; export const init = (serverPluginApi: ServerPluginApi) => { @@ -87,7 +88,10 @@ const onPluginDataLoaded = (pluginInfo: ObjectToTypedMap) => { if (!song.cachedData) { const songData = await getSongData(song.id); - song.cachedData = songData; + song.cachedData = { + ...songData, + content: convertMWLData(songData.content), + }; } } }; @@ -113,7 +117,7 @@ const onRendererDataCreated = ( rendererData: ObjectToTypedMap>, ) => { rendererData.set("songId", null); - rendererData.set("heading", null); + rendererData.set("currentIndex", null); return {}; }; diff --git a/plugins/myworshiplist/src/types.ts b/plugins/myworshiplist/src/types.ts index 35bd6dd..341a301 100644 --- a/plugins/myworshiplist/src/types.ts +++ b/plugins/myworshiplist/src/types.ts @@ -42,5 +42,5 @@ export type SongSetting = z.infer; export type PluginRendererData = { songId: number | null; - heading: string | null; + currentIndex: number | null; }; diff --git a/plugins/myworshiplist/view/MWLRemote/MWLRemoteEditSongModal/SongEditEditor.tsx b/plugins/myworshiplist/view/MWLRemote/MWLRemoteEditSongModal/SongEditEditor.tsx new file mode 100644 index 0000000..c696fc8 --- /dev/null +++ b/plugins/myworshiplist/view/MWLRemote/MWLRemoteEditSongModal/SongEditEditor.tsx @@ -0,0 +1,62 @@ +import { Box } from "@chakra-ui/react"; +import Document from "@tiptap/extension-document"; +import History from "@tiptap/extension-history"; +import Paragraph from "@tiptap/extension-paragraph"; +import TextExtension from "@tiptap/extension-text"; +import { EditorContent, useEditor } from "@tiptap/react"; +import { useEffect } from "react"; + +import { CustomDecorationPlugin } from "./customDecorationPlugin"; + +const SongEditEditor = ({ + initialContent, + onChange, +}: { + initialContent: string; + onChange: (text: string) => void; +}) => { + const editor = useEditor({ + onUpdate: (e) => { + onChange(e.editor.getText({ blockSeparator: "\n" })); + }, + extensions: [ + Document, + Paragraph.extend({ + priority: 8000, + group: "block", + inline: false, + addProseMirrorPlugins() { + return [ + ...(this.parent?.() || []), + CustomDecorationPlugin({ + name: this.name, + }), + ]; + }, + }), + TextExtension, + History, + ], + }); + + useEffect(() => { + if (editor && initialContent !== editor.getHTML()) { + editor?.commands.setContent(initialContent); + } + }, [editor, initialContent]); + + return ( + + + + ); +}; + +export default SongEditEditor; diff --git a/plugins/myworshiplist/view/MWLRemote/MWLRemoteEditSongModal/SongEditInfo.tsx b/plugins/myworshiplist/view/MWLRemote/MWLRemoteEditSongModal/SongEditInfo.tsx new file mode 100644 index 0000000..40287fd --- /dev/null +++ b/plugins/myworshiplist/view/MWLRemote/MWLRemoteEditSongModal/SongEditInfo.tsx @@ -0,0 +1,52 @@ +import { + ListItem, + OrderedList, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverHeader, + Text, +} from "@chakra-ui/react"; + +export const SongEditInfo = () => { + return ( + + + Formatting songs + + + + + We use quite a simple format to show songs inspired by OpenSong.
+
+ Here are some of the basic rules:
+ + + Separate sections with square brackets([ ]) like{" "} + [Verse 1]. +
+ This can be anything from Verse, Chorus, Bridge, and any text you + like. +
+ + Use a single dash(-) to split your section into + multiple slides. + + + Add a dot(.) in front of a line to indicate that + it is a chord line. +
+ Note: At this time, we do not support showing chords yet. +
+
+
+
+ ); +}; diff --git a/plugins/myworshiplist/view/MWLRemote/MWLRemoteEditSongModal/TextareaControl.tsx b/plugins/myworshiplist/view/MWLRemote/MWLRemoteEditSongModal/TextareaControl.tsx deleted file mode 100644 index f0ee9f9..0000000 --- a/plugins/myworshiplist/view/MWLRemote/MWLRemoteEditSongModal/TextareaControl.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Textarea, TextareaProps } from "@chakra-ui/react"; -import { useField, useFormikContext } from "formik"; -import { BaseProps, FormControl } from "formik-chakra-ui"; -import React, { FC } from "react"; - -export type TextareaControlProps = BaseProps & { - textareaProps?: TextareaProps; -}; - -export const TextareaControl: FC = React.forwardRef( - ( - props: TextareaControlProps, - ref: React.ForwardedRef, - ) => { - const { name, label, textareaProps, ...rest } = props; - const [field] = useField(name); - const { isSubmitting } = useFormikContext(); - - return ( - -
-