js/template/WriteAnswer/WriteAnswerTemplate.js
import FullScreenModalTemplate from '~/template/FullScreenModalTemplate';
import ButtonTemplate, { ButtonColor, ButtonStyle } from '~/template/ButtonTemplate';
import ProgressButtonTemplate from '~/template/ProgressButtonTemplate';
import LoadingTemplate from '~/template/LoadingTemplate';
import Analytics, { EventType } from '~/models/Analytics';
import LabelGroup from '~/template/Form/LabelGroup';
import Encoding from '~/models/Encoding';
import LanguageInputTemplate from '~/template/Form/LanguageInputTemplate';
import EncodingInputTemplate from '~/template/Form/EncodingInputTemplate';
import CodeEditorTemplate from '~/template/CodeEditorTemplate';
import MarkdownTemplate from '~/template/MarkdownTemplate';
import FormConstraint from '~/controllers/Form/FormConstraint';
import { HandleUnhandledPromise } from '~/helpers/ErrorManager';
import Language from '~/models/Language';
import Answer from '~/models/Request/Answer';
import Theme from '~/models/Theme';
import { merge, combineLatest } from 'rxjs';
import { filter, first, map, share, startWith, withLatestFrom } from 'rxjs/operators';
/**
* Instance of {@link FullScreenModalTemplate}
*/
export default class WriteAnswerTemplate extends FullScreenModalTemplate {
/**
* @param {Post} post - The post we're writing about
*/
constructor(post) {
const root = new LoadingTemplate();
super({
title: <span>Answering <strong>{ post.title }</strong></span>,
body: root.unique(),
icon: <img src={Theme.dark.imageForTheme('answer')}/>,
submitButton: new ProgressButtonTemplate({
text: 'Submit',
color: ButtonColor.activeAxtell,
style: ButtonStyle.plain
})
});
/**
* The body template
* @type {LoadingTemplate}
*/
this.root = root;
/**
* The post that we're writing on
* @type {Post}
*/
this.post = post;
/**
* These are the form elements
* @type {LabelGroup}
*/
this.languageInput = new LanguageInputTemplate();
/**
* Commentary field
* @type {MarkdownTemplate}
*/
this.commentary = new MarkdownTemplate({
placeholder: 'Commentary',
autoResize: true
})
/**
* The validation observable. Only available after load
* @type {?Observable}
*/
this.observeValidation = null;
/**
* The code editor
* @type {?LabelGroup}
*/
this.codeEditor = null;
/**
* The encoding selector
*/
this.encoding = null;
}
async didInitialLoad() {
await super.didInitialLoad();
this.codeEditor = await new CodeEditorTemplate();
this.encoding = new EncodingInputTemplate(await Encoding.query());
// Create labels
const languageLabel = new LabelGroup(
'Language',
this.languageInput,
{
weight: 2,
liveConstraint: new FormConstraint()
.hasValue('Choose a language')
}
);
const encodingLabel = new LabelGroup(
'Encoding',
this.encoding,
{
liveConstraint: new FormConstraint()
.hasValue('Choose an encoding')
}
);
const codeLabel = new LabelGroup(
'Code',
this.codeEditor
);
const commentaryLabel = new LabelGroup(
'Commentary',
this.commentary
);
// Load the view
this.root.displayAlternate(
<div>
<div class="form-grouping form-grouping--responsive-2x">
{ languageLabel.unique() }
{ encodingLabel.unique() }
</div>
{ codeLabel.unique() }
{ commentaryLabel.unique() }
</div>
);
// Update syntax when language changes
this.languageInput
.observeValue()
.subscribe(
language =>
this.codeEditor
.controller
.setLanguage(language)
.catch(HandleUnhandledPromise));
// Update encoding if applicable
this.languageInput
.observeValue()
.pipe(
filter(language => language !== null),
map(language => language.encoding()))
.subscribe(newEncoding => {
this.encoding.value.next(newEncoding);
});
// Observe validation of all fields
this.observeValidation = combineLatest(
languageLabel.observeValidation(),
codeLabel.observeValidation(),
encodingLabel.observeValidation(),
commentaryLabel.observeValidation(),
// Combine is of all errors into one big error array
(...errors) => [].concat(...errors))
.pipe(
// If they are no errors then that means we're good
map(errors => errors.length === 0),
startWith(false),
share());
// Disable submission button when not validated.
this.observeValidation
.subscribe(
isComplete => this.submitButton.setIsDisabled(
!isComplete,
`Complete all required fields.`))
// Handles submit click
this.submitButton
// When we click the submit button...
.observeClick()
.pipe(
// Grab the latest values
withLatestFrom(
// Of t he following items
combineLatest(
this.languageInput
.observeValue(),
this.codeEditor
.observeValue(),
this.encoding
.observeValue(),
this.commentary
.observeValue()),
// Ignore the click data
(click, data) => data),
// Create an object from data
map(([language, code, encoding, commentary]) => ({ language, code, encoding, commentary })),
// Only able to submit once
first())
.subscribe(({ language, code, encoding, commentary }) => {
this.submitButton.controller.setLoadingState(true);
(async () => {
const answer = new Answer({
post: this.post,
language: language,
code: code,
encoding: encoding,
commentary: commentary
});
const redirectURL = await answer.run();
window.location.href = redirectURL;
})().catch(HandleUnhandledPromise);
})
}
didLoad() {
super.didLoad();
Analytics.shared.report(EventType.answerWriteOpen(this.post));
}
didUnload() {
super.didUnload();
Analytics.shared.report(EventType.answerWriteOpen(this.post));
}
}