mirror of
https://github.com/Blah-IM/Weblah.git
synced 2025-05-01 00:31:08 +00:00
fix: Add initialDoc prop and BlahRichText conversion utility
Fix typo in schema ("paragragh" -> "paragraph") and implement bidirectional conversion between BlahRichText and ProseMirror formats. Update profile page to use new initialDoc prop instead of children.
This commit is contained in:
parent
aaedd69889
commit
b56b47df82
4 changed files with 48 additions and 12 deletions
|
@ -7,10 +7,22 @@
|
||||||
export interface Props extends Omit<EditorStateConfiguration, 'initialDoc'> {
|
export interface Props extends Omit<EditorStateConfiguration, 'initialDoc'> {
|
||||||
onDocChange?: (doc: Node) => void;
|
onDocChange?: (doc: Node) => void;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
/**
|
||||||
|
* The initial content in editor.
|
||||||
|
*
|
||||||
|
* This is higher priority than `children`.
|
||||||
|
*/
|
||||||
|
initialDoc?: Node | null;
|
||||||
children?: import('svelte').Snippet;
|
children?: import('svelte').Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { onDocChange, placeholder = '', children, ...stateConfiguration }: Props = $props();
|
const {
|
||||||
|
onDocChange,
|
||||||
|
placeholder = '',
|
||||||
|
children,
|
||||||
|
initialDoc: initialDocProp,
|
||||||
|
...stateConfiguration
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
let domEl: HTMLDivElement;
|
let domEl: HTMLDivElement;
|
||||||
let editorView: EditorView;
|
let editorView: EditorView;
|
||||||
|
@ -18,7 +30,8 @@
|
||||||
let isEmpty = $state(!children);
|
let isEmpty = $state(!children);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const initialDoc = DOMParser.fromSchema(stateConfiguration.schema).parse(domEl);
|
const initialDoc =
|
||||||
|
initialDocProp ?? DOMParser.fromSchema(stateConfiguration.schema).parse(domEl);
|
||||||
domEl.replaceChildren();
|
domEl.replaceChildren();
|
||||||
onDocChange?.(initialDoc);
|
onDocChange?.(initialDoc);
|
||||||
isEmpty = initialDoc.textContent.length === 0;
|
isEmpty = initialDoc.textContent.length === 0;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { nodes as basicNodes, marks as basicMarks } from 'prosemirror-schema-bas
|
||||||
export const messageSchema = new Schema({
|
export const messageSchema = new Schema({
|
||||||
nodes: {
|
nodes: {
|
||||||
doc: { content: 'block+' },
|
doc: { content: 'block+' },
|
||||||
paragragh: {
|
paragraph: {
|
||||||
content: 'inline*',
|
content: 'inline*',
|
||||||
...basicNodes.paragraph
|
...basicNodes.paragraph
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { BlahRichText, BlahRichTextSpanAttributes } from '@blah-im/core/richText';
|
import type { BlahRichText, BlahRichTextSpanAttributes } from '@blah-im/core/richText';
|
||||||
import type { Node } from 'prosemirror-model';
|
import type { Node, Schema } from 'prosemirror-model';
|
||||||
|
|
||||||
function isObjectEmpty(obj: object) {
|
function isObjectEmpty(obj: object) {
|
||||||
for (const _ in obj) return false;
|
for (const _ in obj) return false;
|
||||||
|
@ -58,3 +58,28 @@ export function proseMirrorDocToBlahRichText(doc: Node): BlahRichText {
|
||||||
|
|
||||||
return spans;
|
return spans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function blahRichTextToProseMirrorDoc(richText: BlahRichText, schema: Schema): Node {
|
||||||
|
console.log(schema);
|
||||||
|
const paragraphs = richText.flatMap((span) => {
|
||||||
|
if (typeof span === 'string') {
|
||||||
|
if (!span.trim().length) return [];
|
||||||
|
return [schema.nodes.paragraph.create({}, schema.text(span))];
|
||||||
|
} else {
|
||||||
|
const [text, attributes] = span;
|
||||||
|
const marks = [];
|
||||||
|
if (attributes.b) marks.push(schema.marks.strong.create());
|
||||||
|
if (attributes.i) marks.push(schema.marks.em.create());
|
||||||
|
if (attributes.m) marks.push(schema.marks.code.create());
|
||||||
|
if (attributes.link) marks.push(schema.marks.link.create({ href: attributes.link }));
|
||||||
|
if (attributes.u) marks.push(schema.marks.underline.create());
|
||||||
|
if (attributes.s) marks.push(schema.marks.strikethrough.create());
|
||||||
|
if (attributes.tag) marks.push(schema.marks.tag.create());
|
||||||
|
if (attributes.spoiler) marks.push(schema.marks.spoiler.create());
|
||||||
|
|
||||||
|
return [schema.nodes.paragraph.create({}, schema.text(text, marks))];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return schema.nodes.doc.create({}, paragraphs);
|
||||||
|
}
|
||||||
|
|
|
@ -13,9 +13,12 @@
|
||||||
import Button from '$lib/components/Button.svelte';
|
import Button from '$lib/components/Button.svelte';
|
||||||
import RichTextInput from '$lib/components/RichTextInput.svelte';
|
import RichTextInput from '$lib/components/RichTextInput.svelte';
|
||||||
import { messageSchema } from '$lib/components/RichTextInput/schema';
|
import { messageSchema } from '$lib/components/RichTextInput/schema';
|
||||||
|
import { blahRichTextToProseMirrorDoc } from '$lib/richText';
|
||||||
|
import type { Node } from 'prosemirror-model';
|
||||||
|
|
||||||
const currentAccount = $derived(accountsManager.currentAccount);
|
const currentAccount = $derived(accountsManager.currentAccount);
|
||||||
let profile: BlahProfile | null = $state(null);
|
let profile: BlahProfile | null = $state(null);
|
||||||
|
let initialBio: Node | null = $state(null);
|
||||||
|
|
||||||
let isBusy: boolean = $state(false);
|
let isBusy: boolean = $state(false);
|
||||||
|
|
||||||
|
@ -23,21 +26,17 @@
|
||||||
if (currentAccount) {
|
if (currentAccount) {
|
||||||
const snapshot = $state.snapshot(currentAccount.profile.signee.payload);
|
const snapshot = $state.snapshot(currentAccount.profile.signee.payload);
|
||||||
profile = snapshot;
|
profile = snapshot;
|
||||||
console.log('Reloaded');
|
initialBio = blahRichTextToProseMirrorDoc([snapshot.bio ?? ''], messageSchema);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$inspect(profile);
|
|
||||||
|
|
||||||
async function saveProfile() {
|
async function saveProfile() {
|
||||||
if (!currentAccount || !profile) return;
|
if (!currentAccount || !profile) return;
|
||||||
|
|
||||||
console.log('Saving profile', $state.snapshot(profile));
|
|
||||||
isBusy = true;
|
isBusy = true;
|
||||||
const identity = await accountsManager.identityForAccount(currentAccount);
|
const identity = await accountsManager.identityForAccount(currentAccount);
|
||||||
await identity.updateProfile(profile);
|
await identity.updateProfile(profile);
|
||||||
await accountsManager.saveIdentity(identity);
|
await accountsManager.saveIdentity(identity);
|
||||||
console.log('Profile saved', identity.generateIdentityDescription());
|
|
||||||
isBusy = false;
|
isBusy = false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -67,9 +66,8 @@
|
||||||
schema={messageSchema}
|
schema={messageSchema}
|
||||||
onDocChange={(doc) => profile && (profile.bio = doc.textContent)}
|
onDocChange={(doc) => profile && (profile.bio = doc.textContent)}
|
||||||
placeholder="a 25 yo. artist from Paris."
|
placeholder="a 25 yo. artist from Paris."
|
||||||
>
|
initialDoc={initialBio}
|
||||||
{profile.bio ?? ''}
|
/>
|
||||||
</RichTextInput>
|
|
||||||
</GroupedListSection>
|
</GroupedListSection>
|
||||||
</GroupedListContainer>
|
</GroupedListContainer>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
Loading…
Add table
Reference in a new issue