Skip to content

Commit

Permalink
Frontend fixes (#233)
Browse files Browse the repository at this point in the history
* Temp

* Fixes editors.

* Reuse hooks.

* Simplify code.

* Simplify code.
  • Loading branch information
SebastianStehle authored Apr 11, 2024
1 parent cf6f9e3 commit c0f58f7
Show file tree
Hide file tree
Showing 12 changed files with 332 additions and 255 deletions.
10 changes: 5 additions & 5 deletions frontend/src/app/framework/react/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ type QueryState<T> = {

type QueryParams<T> = {
queryKey: string[];
queryFn: (abort: AbortController) => Promise<T>;
queryFn: (abort: AbortSignal) => Promise<T>;
defaultValue: T;
};

Expand All @@ -168,7 +168,7 @@ export function useSimpleQuery<T>(params: QueryParams<T>): QueryState<T> {

setState(s => ({ ...s, isLoading: true }));
try {
const value = await params.queryFn(abort);
const value = await params.queryFn(abort.signal);

if (request.current === id) {
setState(s => ({ ...s, error: undefined, value }));
Expand All @@ -184,11 +184,11 @@ export function useSimpleQuery<T>(params: QueryParams<T>): QueryState<T> {
}
}

const abort = new AbortController();
loadData(new Date().getTime(), abort);
const controller = new AbortController();
loadData(new Date().getTime(), controller);

return () => {
abort.abort();
controller.abort();
};
}, params.queryKey);

Expand Down
6 changes: 3 additions & 3 deletions frontend/src/app/pages/email-templates/EmailTemplatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ export const EmailTemplatePage = () => {
const [language, setLanguage] = React.useState(appLanguages[0]);

const properties = useSimpleQuery<TemplatePropertyDto[]>({
queryKey: [],
queryFn: async () => {
const result = await Clients.SmsTemplates.getProperties(appId);
queryKey: [appId],
queryFn: async abort => {
const result = await Clients.SmsTemplates.getProperties(appId, abort);

return result.items;
},
Expand Down
85 changes: 59 additions & 26 deletions frontend/src/app/pages/email-templates/editor/EmailHtmlEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,90 @@
* Copyright (c) Sebastian Stehle. All rights reserved.
*/

// tslint:disable: quotemark

import * as React from 'react';
import Split from 'react-split';
import { Alert } from 'reactstrap';
import { IFrame } from '@app/framework';
import { CodeEditor, CodeEditorProps, IFrame } from '@app/framework';
import { MjmlSchema } from '@app/service';
import { EmailHtmlTextEditor } from './EmailHtmlTextEditor';
import { usePreview } from './helpers';

export interface EmailHtmlEditorProps {
// The value.
value: string;
import { useErrors, usePreview } from './helpers';
import { completeAfter, completeIfAfterLt, completeIfInTag } from './helpers';
import 'codemirror/addon/dialog/dialog';
import 'codemirror/addon/edit/closetag';
import 'codemirror/addon/edit/matchtags';
import 'codemirror/addon/hint/show-hint';
import 'codemirror/addon/hint/xml-hint';
import 'codemirror/addon/lint/lint';
import 'codemirror/addon/scroll/annotatescrollbar';
import 'codemirror/addon/search/jump-to-line';
import 'codemirror/addon/search/match-highlighter';
import 'codemirror/addon/search/matchesonscrollbar';
import 'codemirror/addon/search/search';
import 'codemirror/addon/search/searchcursor';
import 'codemirror/addon/selection/active-line';
import 'codemirror/mode/xml/xml';

export interface EmailHtmlEditorProps extends CodeEditorProps {
// The app name.
appId: string;

// The schema.
schema?: MjmlSchema;

// When the html has changed.
onChange: (value: string) => void;

// Called when the focus has been lost.
onBlur: () => void;
}

export const EmailHtmlEditor = (props: EmailHtmlEditorProps) => {
const {
appId,
onBlur,
onChange,
schema,
value,
...other
} = props;

const emailMarkup = value || '';
const emailPreview = usePreview(appId, emailMarkup, 'Html');

const error = emailPreview.rendering.errors?.[0];
const initialOptions = React.useMemo(() => {
const result: CodeEditorProps['options'] = {
autoCloseTags: true,
mode: 'xml',
extraKeys: {
Tab: (cm) => {
if (cm.getMode().name === 'null') {
cm.execCommand('insertTab');
} else if (cm.somethingSelected()) {
cm.execCommand('indentMore');
} else {
cm.execCommand('insertSoftTab');
}
},
'\'<\'': completeAfter,
'\'/\'': completeIfAfterLt,
'\' \'': completeIfInTag,
'\'=\'': completeIfInTag,
'Ctrl-Space': 'autocomplete',
},
matchTags: true,
};

return result;
}, []);

const { lint, error } = useErrors(emailPreview.rendering);

const options = React.useMemo(() => {
const result: CodeEditorProps['options'] = {
hintOptions: schema ? { schemaInfo: schema } : undefined,
lint,
};

return result;
}, [lint, schema]);

return (
<div className='email-editor'>
<div className='email-editor white'>
<Split direction='horizontal'>
<div className='left'>
<EmailHtmlTextEditor
value={emailMarkup}
errors={emailPreview.rendering?.errors}
onBlur={onBlur}
onChange={onChange}
schema={schema}
/>
<CodeEditor initialOptions={initialOptions} options={options} value={emailMarkup}
className='email-editor' {...other} />
</div>

<div className='right'>
Expand Down

This file was deleted.

19 changes: 14 additions & 5 deletions frontend/src/app/pages/email-templates/editor/EmailTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@
* Copyright (c) Sebastian Stehle. All rights reserved.
*/

import * as React from 'react';
import Split from 'react-split';
import { Alert } from 'reactstrap';
import { CodeEditor, CodeEditorProps } from '@app/framework';
import { usePreview } from './helpers';
import { useErrors, usePreview } from './helpers';
import 'codemirror/mode/django/django';

export interface EmailTextEditorProps extends CodeEditorProps {
// The app name.
appId: string;
}

const OPTIONS = {
const INITIAL_OPTIONS = {
mode: 'django',
};

Expand All @@ -30,14 +31,22 @@ export const EmailTextEditor = (props: EmailTextEditorProps) => {
const emailMarkup = value || '';
const emailPreview = usePreview(appId, emailMarkup, 'Text');

const error = emailPreview.rendering.errors?.find(x => !x.lineNumber || x.lineNumber < 0);
const { lint, error } = useErrors(emailPreview.rendering);

const options = React.useMemo(() => {
const result: CodeEditorProps['options'] = {
lint,
};

return result;
}, [lint]);

return (
<div className='email-editor white'>
<Split direction='horizontal'>
<div className='left'>
<CodeEditor value={value} {...other}
initialOptions={OPTIONS} />
<CodeEditor initialOptions={INITIAL_OPTIONS} options={options} value={emailMarkup}
className='email-editor' {...other} />
</div>

<div className='right'>
Expand Down
31 changes: 28 additions & 3 deletions frontend/src/app/pages/email-templates/editor/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/* eslint-disable quote-props */

import * as CodeMirror from 'codemirror';
import * as React from 'react';
import { useDebounce, useSimpleQuery } from '@app/framework';
import { Clients, EmailPreviewDto, EmailPreviewType } from '@app/service';

Expand All @@ -19,14 +20,14 @@ export function usePreview(appId: string, sourceTemplate: string, type: EmailPre
const template = useDebounce(sourceTemplate, 500);

const query = useSimpleQuery<MarkupResponse>({
queryKey: [template, type],
queryFn: async () => {
queryKey: [appId, template, type],
queryFn: async abort => {
if (!template) {
return { rendering: {} };
}

try {
const rendering = await Clients.EmailTemplates.postPreview(appId, { template, type });
const rendering = await Clients.EmailTemplates.postPreview(appId, { template, type }, abort);

return { rendering, template };
} catch (ex: any) {
Expand All @@ -45,6 +46,30 @@ export function usePreview(appId: string, sourceTemplate: string, type: EmailPre
return query.value;
}

export function useErrors(rendering?: EmailPreviewDto) {
const errors = rendering?.errors;

return React.useMemo(() => {
const error = errors?.find(x => !x.lineNumber || x.lineNumber < 0);

const lint = {
getAnnotations: () => {
if (!errors) {
return [];
}

return errors.filter(x => x.lineNumber >= 0).map(({ message, lineNumber }) => {
const from = CodeMirror.Pos(lineNumber! - 1, 1);

return { message, severity: 'error', from, to: from };
});
},
};

return { error, lint };
}, [errors]);
}

export function completeAfter(editor: CodeMirror.Editor, predicate?: (cursor: CodeMirror.Position) => boolean) {
const cursor = editor.getCursor();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ export const MessagingTemplatePage = () => {
const [language, setLanguage] = React.useState(appLanguages[0]);

const properties = useSimpleQuery<TemplatePropertyDto[]>({
queryKey: [],
queryFn: async () => {
const result = await Clients.MessagingTemplates.getProperties(appId);
queryKey: [appId],
queryFn: async abort => {
const result = await Clients.MessagingTemplates.getProperties(appId, abort);

return result.items;
},
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/pages/sms-templates/SmsTemplatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ export const SmsTemplatePage = () => {
const [language, setLanguage] = React.useState(appLanguages[0]);

const properties = useSimpleQuery<TemplatePropertyDto[]>({
queryKey: [],
queryKey: [appId],
queryFn: async () => {
const result = await Clients.SmsTemplates.getProperties(appId);
const result = await Clients.SmsTemplates.getProperties(appId, abort);

return result.items;
},
Expand Down
Loading

0 comments on commit c0f58f7

Please sign in to comment.