Skip to content

Commit fe5d9d5

Browse files
authored
feat: added markdown preview in the detail view
1 parent ac62dfb commit fe5d9d5

7 files changed

Lines changed: 100 additions & 46 deletions

File tree

lib/public/components/Post/index.js

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,34 @@ const replyButton = (model, post) => h('a.btn.btn-primary', {
6969
* @param {Boolean} highlight indicator if this post should be highlighted
7070
* @return {vnode} Returns a post
7171
*/
72-
const entry = (model, post, index, highlight) => h(`.w-100.flex-row.shadow-level1${highlight ? '.bg-success' : ''}`, {
73-
id: `post${index}`,
74-
style: {
75-
margin: '5px',
76-
},
77-
}, [
78-
h('.w-20.shadow-level', { style: { padding: '10px' } }, [
79-
h('table', [
80-
h('tbody', Object.entries(ACTIVE_FIELDS).map(([key, { name, format, visible }]) => visible && h('tr', [
81-
h('td', { style: { 'text-align': 'right', 'font-weight': 'bold' } }, `${name}:`),
82-
h('td', post[key] ? format ? format(post[key]) : post[key] : 'None'),
83-
]))),
72+
const entry = (model, post, index, highlight) =>
73+
h('.w-100.flex-row.shadow-level1', {
74+
id: `post${index}`,
75+
style: {
76+
margin: '5px',
77+
},
78+
}, [
79+
h(`.w-20.shadow-level1${highlight && '.bg-gray-light'}`, {
80+
style: { padding: '10px' },
81+
}, [
82+
h('table', [
83+
h('tbody', Object.entries(ACTIVE_FIELDS).map(([key, { name, format, visible }]) => visible && h('tr', [
84+
h('td', { style: { 'text-align': 'right', 'font-weight': 'bold' } }, `${name}:`),
85+
h('td', post[key] && format ? format(post[key]) : post[key] || 'None'),
86+
]))),
87+
]),
88+
]),
89+
h('.w-80.shadow-level1', { style: { padding: '10px' } }, [
90+
h('h4', post.title, replyButton(model, post)),
91+
h(`textarea.w-100#post-content${index}`, {
92+
oninit: () => {
93+
const eventHandler = { location: '', name: '' };
94+
const readOnlyProps = { isReadOnly: true, textValue: post.text };
95+
model.logs.clearSingleEditor();
96+
model.logs.setMarkdownBox(`post-content${index}`, eventHandler, readOnlyProps, true);
97+
},
98+
}, post.text),
8499
]),
85-
]),
86-
h('.w-80.shadow-level1', { style: { padding: '10px' } }, [
87-
h('h4', post.title, replyButton(model, post)),
88-
h('.w-100#post-content', post.text),
89-
]),
90-
]);
100+
]);
91101

92102
export default entry;
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
const defaultSize = { width: '80rem', height: '40rem' };
2-
31
/**
42
* A function to switch to HyperMD
53
* @param {String} textAreaId The Id for the text area
@@ -10,21 +8,24 @@ const defaultSize = { width: '80rem', height: '40rem' };
108
* @returns {vnode} The converted editor
119
*/
1210
const setMarkDownBox = (textAreaId, model,
13-
changeHandler = { location: '', name: '' }, readOnly = false, size = { ...defaultSize }) => {
11+
changeHandler = { location: '', name: '' }, readOnly, size) => {
1412
const { HyperMD } = model;
1513
const { location, name } = changeHandler;
1614
// eslint-disable-next-line no-undef
1715
const textArea = document.getElementById(textAreaId);
18-
const editor = HyperMD.fromTextArea(textArea, {
19-
hmdModeLoader: '../../assets/SmartEditor/codemirror',
20-
readOnly: readOnly,
21-
});
22-
editor.setSize(size.width, size.height);
23-
if (name && location) {
24-
editor.on('change', (cm) =>
25-
model[location][name](cm.getValue()));
16+
if (textArea) {
17+
const editor = HyperMD.fromTextArea(textArea, {
18+
hmdModeLoader: '../../assets/SmartEditor/codemirror',
19+
readOnly: readOnly,
20+
lineNumbers: !readOnly,
21+
});
22+
editor.setSize(size.width, size.height);
23+
if (name && location) {
24+
editor.on('change', (cm) =>
25+
model[location][name](cm.getValue()));
26+
}
27+
return editor;
2628
}
27-
return editor;
2829
};
2930

3031
export { setMarkDownBox };

lib/public/utilities/scrollTo.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,33 @@
1111
* or submit itself to any jurisdiction.
1212
*/
1313

14+
/**
15+
* Scroll to the position in the window
16+
* @param {Number} x The x value
17+
* @param {Number} y The y value
18+
* @returns {undefined}
19+
*/
20+
const scrollToPosition = (x, y) => {
21+
// eslint-disable-next-line no-undef
22+
window.scrollTo(x, y);
23+
};
24+
1425
/**
1526
* Scrolls to a particular selector in the document.
1627
*
1728
* @param {*} selector A DOMString containing one or more selectors to match.
29+
* @param {Number} timeout The timeout for the function to execute
1830
* @returns {undefined}
1931
*/
20-
const scrollTo = (selector) => {
32+
const scrollTo = (selector, timeout = 0) => {
2133
// eslint-disable-next-line no-undef
2234
const element = document.querySelector(selector);
2335
const x = 0;
2436
const y = element === null || isNaN(element.offsetTop) ? 0 : element.offsetTop;
2537

26-
// eslint-disable-next-line no-undef
27-
window.scrollTo(x, y);
38+
setTimeout(() => {
39+
scrollToPosition(x, y);
40+
}, timeout);
2841
};
2942

3043
export default scrollTo;

lib/public/views/Logs/Create/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ import { h } from '/js/src/index.js';
1919
* @returns {vnode} returns the preview node
2020
*/
2121
const mdBox = (model, text) => h('', {
22-
onremove: () => model.logs.clearEditor(),
22+
onremove: () => model.logs.flushModel(),
2323
}, [
2424
h('textarea#text.w-75.form-control', {
2525
placeholder: 'Your message...',
2626
disabled: true,
2727
onchange: (e) => model.logs.setText(e.target.value),
28-
onload: () => model.logs.setMarkdownBox('text', { location: 'logs', name: 'setText' }),
28+
oninit: () => {
29+
model.logs.setMarkdownBox('text', { location: 'logs', name: 'setText' }, { isReadOnly: false });
30+
},
2931
}, text),
3032
]);
3133

lib/public/views/Logs/Details/index.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,15 @@ const logDetailScreen = (model) => {
4949
};
5050

5151
return h('', [
52-
h('h2.mv2', { onremove: () => model.logs.clearLogs() }, 'Log Tree'),
52+
h('h2.mv2', {
53+
onremove: () => {
54+
model.logs.clearLogs(),
55+
model.logs.flushModel();
56+
},
57+
}, 'Log Tree'),
5358
h('.w-100.flex-column', {
54-
oncreate: () => scrollTo(`#post${id}`),
55-
onupdate: () => scrollTo(`#post${id}`),
59+
oncreate: () => scrollTo(`#post${id}`, 100),
60+
onupdate: () => scrollTo(`#post${id}`, 100),
5661
}, tree(data.payload[0])),
5762
]);
5863
} else if (data.isFailure()) {

lib/public/views/Logs/Logs.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export default class Overview extends Observable {
3333
this.text = '';
3434

3535
this.isPreviewActive = false;
36+
this.editors = [];
3637
}
3738

3839
/**
@@ -353,28 +354,50 @@ export default class Overview extends Observable {
353354
}
354355

355356
/**
356-
* Clear the active editor in the window
357-
* Clear the active editor
357+
* Clear the single editor in the model
358358
* @returns {undefined}
359359
*/
360-
clearEditor() {
360+
clearSingleEditor() {
361+
this.editor = null;
362+
this.isPreviewActive = false;
363+
}
364+
365+
/**
366+
* Clear the model variables to prevent memory leaks
367+
* @returns {undefined}
368+
*/
369+
flushModel() {
370+
this.rootLogId = -1;
361371
this.parentLogId = -1;
362372
this.isPreviewActive = false;
363373
this.text = '';
364374
this.title = '';
365375
this.editor = null;
376+
this.editors = [];
366377
}
367378

368379
/**
369380
* Convert the textarea of the preview to a Markdown box
370381
* @param {String} textAreaId The id of the preview box
371382
* @param {Object} changeHandler The optional changehandler with 2 keys: locaiton and name to get the setter method
383+
* @param {Object} readOnlyProperties Properties of the readOnlyBox
372384
* @returns {undefined}
373385
*/
374-
setMarkdownBox(textAreaId, changeHandler) {
386+
setMarkdownBox(textAreaId, changeHandler = { location: '', name: '' },
387+
readOnlyProperties = { isReadOnly: false, textValue: '' }) {
388+
const { isReadOnly, textValue } = readOnlyProperties;
389+
const shouldRenderReadOnly = textValue && isReadOnly;
390+
const mdBoxStyling = shouldRenderReadOnly ?
391+
{ width: 'auto', height: 'auto' } : { width: '80rem', height: '40rem' };
392+
375393
!this.isPreviewActive && setTimeout(() => {
376-
this.editor = setMarkDownBox(textAreaId, this.model, changeHandler);
394+
this.editor = setMarkDownBox(textAreaId, this.model, changeHandler, isReadOnly, mdBoxStyling);
377395
}, 40);
396+
397+
!this.editor && setTimeout(() => {
398+
this.editor !== undefined && this.editors.push(this.editor);
399+
}, 50);
400+
378401
this.isPreviewActive = !this.isPreviewActive;
379402
this.notify();
380403
}

test/public/logs/overview.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,11 +266,11 @@ module.exports = () => {
266266
// Create the new log
267267
const buttonSend = await page.$('button#send');
268268
await buttonSend.evaluate((button) => button.click());
269-
await page.waitFor(100);
269+
await page.waitFor(150);
270270

271271
// Verify that the text from the first matches with the text posted and correct working of the redirect
272-
const firstPost = await page.$('#post-content');
273-
const doesContentMatch = JSON.stringify(await page.evaluate((element) => element.innerText, firstPost))
272+
// eslint-disable-next-line no-undef
273+
const doesContentMatch = JSON.stringify(await page.evaluate(() => model.logs.editors[0].getValue()))
274274
.includes(text);
275275

276276
// Verify that the first post is equal to the title provided as input when creating the log

0 commit comments

Comments
 (0)