Skip to content

Commit

Permalink
feat: new comments form with image upload button (#1221)
Browse files Browse the repository at this point in the history
* refactor: one form for comment replies

replaces a hidden form for each element with one form managed by ReplyContainer vue component

* feat: adds file upload button to the comment editor

* feat: polish layout for main comment form

* feat: attach images in battle main comments

* feat: attach images in replies

* feat: enable markdown editor for everyone

* fix: always show dark color on white

* refactor: remove unused code

* fix: incorrect cursor when hovered upload image button

* fix: use image icon instead of paperclip

* fix: do not init full markdown editor on mobile for SSR pages

* feat: support image upload for simple textarea editor
  • Loading branch information
nlopin committed Jul 19, 2024
1 parent 099d6ad commit 4485776
Show file tree
Hide file tree
Showing 12 changed files with 506 additions and 283 deletions.
4 changes: 2 additions & 2 deletions comments/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class CommentForm(forms.ModelForm):
),
)
subscribe_to_post = forms.BooleanField(
label="подписаться на новые комментарии",
label="подписаться на комментарии",
label_suffix="",
initial=True,
required=False,
Expand Down Expand Up @@ -90,7 +90,7 @@ class BattleCommentForm(forms.ModelForm):
),
)
subscribe_to_post = forms.BooleanField(
label="подписаться на новые комментарии",
label="подписаться на комментарии",
label_suffix="",
initial=True,
required=False,
Expand Down
30 changes: 18 additions & 12 deletions frontend/html/comments/forms/battle.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,25 @@
</select>
</div>
<div class="comment-form-body-title">{{ form.title }}</div>
<comment-markdown-editor class="comment-form-body-text" post-slug="{{ post.slug }}"
{% if comment.text %}
value="{{ comment.text }}" focused
{% endif %}
>
</comment-markdown-editor>
{% if form.text.errors %}<span class="form-errors">{{ form.full_name.errors }}</span>{% endif %}
</div>
<div class="comment-form-footer">
<div class="comment-form-subscribe">
{{ form.subscribe_to_post }} {{ form.subscribe_to_post.label_tag }}
<div class="comment-form-editor-container">
<comment-markdown-editor class="comment-form-body-text" post-slug="{{ post.slug }}"
{% if comment.text %}
value="{{ comment.text }}" focused
{% endif %}
>
</comment-markdown-editor>
<div class="comment-form-footer">
<label class="comment-form-attach-image">
<input type="file" name="attach-image" alt="Добавить картинку" />
<i class="fa fa-image"></i>
</label>
<div class="comment-form-subscribe">
{{ form.subscribe_to_post }} {{ form.subscribe_to_post.label_tag }}
</div>
<button type="submit" class="button comment-form-button"> {{save_message}} </button>
</div>
</div>
<button type="submit" class="button comment-form-button"> {{save_message}} </button>
{% if form.text.errors %}<span class="form-errors">{{ form.full_name.errors }}</span>{% endif %}
</div>
</div>
</form>
18 changes: 11 additions & 7 deletions frontend/html/comments/forms/comment.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@
<div class="comment-form-avatar">
<div class="avatar"><img src="{{ me.get_avatar }}" alt="Аватар {{ me.full_name }}" loading="lazy" /></div>
</div>
<div class="comment-form-body">
<div class="comment-form-body comment-form-editor-container">
<comment-markdown-editor class="comment-form-body-text" post-slug="{{ post.slug }}"
{% if comment.text %}
value="{{ comment.text }}" focused
{% endif %}
>
</comment-markdown-editor>
{% if form.text.errors %}<span class="form-errors">{{ form.full_name.errors }}</span>{% endif %}
</div>
<div class="comment-form-footer">
<div class="comment-form-subscribe">
{{ form.subscribe_to_post }} {{ form.subscribe_to_post.label_tag }}
<div class="comment-form-footer">
<label class="comment-form-attach-image">
<input type="file" name="attach-image" alt="Добавить картинку" />
<i class="fa fa-image"></i>
</label>
<div class="comment-form-subscribe">
{{ form.subscribe_to_post }} {{ form.subscribe_to_post.label_tag }}
</div>
<button type="submit" class="button comment-form-button">{{ save_message }}</button>
</div>
<button type="submit" class="button comment-form-button">{{ save_message }}</button>
{% if form.text.errors %}<span class="form-errors">{{ form.full_name.errors }}</span>{% endif %}
</div>
</div>
</form>
3 changes: 2 additions & 1 deletion frontend/static/css/components/comments.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@
}

.comment-form-subscribe {
opacity: 0.5;
opacity: 0.75;
padding: 0 20px;
margin-left: auto;
}

.reply-form-form {
Expand Down
117 changes: 92 additions & 25 deletions frontend/static/css/layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -280,38 +280,71 @@
}

.comment-form {
display: grid;
grid-template-columns: min-content minmax(auto, 1fr);
grid-template-rows: auto 60px;
justify-content: stretch;
display: flex;
--form-border-radius: 5px;
}

.comment-form-avatar {
grid-column-start: 1;
grid-column-end: 2;
grid-row-start: 1;
grid-row-end: 3;
justify-self: start;
width: min-content;
}

.comment-form-body {
grid-column-start: 2;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 2;
justify-self: stretch;
flex: 1;
}

.comment-form-footer {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 2;
grid-row-end: 3;
justify-self: end;
align-self: end;
.comment-form-editor-container,
.reply-form-editor-container {
box-shadow: 0 4px 8px -2px rgba(9,30,66,.25),0 0 0 1px rgba(9,30,66,.08);
border-radius: var(--form-border-radius);
overflow: hidden;
}

.comment-form-editor-container:focus-within,
.reply-form-editor-container:focus-within {
box-shadow: 0 4px 12px -2px rgba(9,30,66,.45),0 0 0 1px rgba(9,30,66,.32);
}

.comment-form-footer,
.reply-form-footer {
background-color: #fff;
color: #333;
display: flex;
flex-direction: row;
align-items: center;
padding: 6px;
}

.comment-form-footer .button {
padding: 10px 16px;
border-radius: var(--form-border-radius, var(--button-border-radius));
}

.comment-form-attach-image {
display: block;
position: relative;
top: 1px; /* visual center adjustment */
font-size: 1.125rem;
padding: 3px 9px;
border-radius: var(--form-border-radius);
border: var(--button-border);
border-color: transparent;
}

.comment-form-attach-image:hover,
.comment-form-attach-image:focus-within {
border-color: inherit;
cursor: pointer;
}

.comment-form-attach-image input[type=file] {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
padding: 0;
resize: none;
}

.comment-form-body {
Expand Down Expand Up @@ -370,6 +403,38 @@
align-self: end;
}

.reply-form-attach-image {
display: block;
position: relative;
font-size: 1rem;
padding: 0 6px;
border: var(--button-border);
border-color: transparent;
}

.reply-form-attach-image:hover,
.reply-form-attach-image:focus-within {
border-color: inherit;
cursor: pointer;
}

.reply-form-attach-image input[type=file] {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
padding: 0;
resize: none;
}

.reply-form-attach-image input[type=file]:hover,
.reply-form-attach-image input[type=file]:focus {
cursor: pointer;
}

@media only screen and (max-width : 570px) {
.reply-form {
display: grid;
Expand All @@ -379,10 +444,12 @@
}

.reply-form-button {
grid-column-start: 1;
margin-bottom: 6px;
margin-right: 6px;
grid-column-start: 2;
grid-column-end: 3;
grid-row-start: 2;
grid-row-end: 3;
grid-row-start: 1;
grid-row-end: 2;
justify-self: end;
align-self: end;
}
Expand Down
39 changes: 28 additions & 11 deletions frontend/static/js/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import EasyMDE from "easymde";
import Lightense from "lightense-images";

import { isCommunicationForm, isMobile } from "./common/utils";
import { imageUploadOptions, createMarkdownEditor, handleFormSubmissionShortcuts } from "./common/markdown-editor";
import {
createFileInput,
createMarkdownEditor,
handleFormSubmissionShortcuts,
imageUploadOptions
} from "./common/markdown-editor";
import { getCollapsedCommentThreadsSet } from "./common/comments";

const INITIAL_SYNC_DELAY = 50;
Expand Down Expand Up @@ -70,12 +75,20 @@ const App = {
setFaviconHref(mediaQueryList);
mediaQueryList.addListener(setFaviconHref);
},

/**
* Initializes markdown editor with toolbar **for server-side rendered pages**
* e.g. Create post page
*
* Simple editors are initialized in the `CommentMarkdownEditor` Vue component
*
* @returns {CodeMirrorEditor[]}
*/
initializeMarkdownEditor() {
if (isMobile()) return []; // we don't need fancy features on mobiles

const fullMarkdownEditors = [...document.querySelectorAll(".markdown-editor-full")].reduce(
(editors, element) => {
const fileInputEl = createFileInput({ allowedTypes: imageUploadOptions.allowedTypes })
const editor = createMarkdownEditor(element, {
autosave: {
enabled: false,
Expand Down Expand Up @@ -126,6 +139,15 @@ const App = {
className: "fas fa-image",
title: "Insert an image",
},
{
name: "upload-file",
action: () => {
fileInputEl.click()
},
className: "fa fa-paperclip",
text: "Upload image",
title: "Upload image",
},
{
name: "code",
action: EasyMDE.toggleCodeBlock,
Expand All @@ -135,20 +157,15 @@ const App = {
],
});

editor.element.form.addEventListener("keydown", handleFormSubmissionShortcuts);
inlineAttachment.editors.codemirror4.attach(editor.codemirror, { ...imageUploadOptions, fileInputEl });

return [...editors, editor];
},
[]
);

const allEditors = fullMarkdownEditors;

allEditors.forEach((editor) => {
editor.element.form.addEventListener("keydown", handleFormSubmissionShortcuts);

inlineAttachment.editors.codemirror4.attach(editor.codemirror, imageUploadOptions);
});

return allEditors;
return fullMarkdownEditors;
},
addTargetBlankToExternalLinks() {
let internal = location.host.replace("www.", "");
Expand Down
8 changes: 8 additions & 0 deletions frontend/static/js/codemirror-4.inline-attachment.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
inlineattach = new inlineAttachment(options, editor),
el = codeMirror.getWrapperElement();

const fileInput = options.fileInputEl || el.closest("form").querySelector("input[type=file]")
el.addEventListener(
"paste",
function (e) {
Expand All @@ -87,6 +88,13 @@
return false;
}
});

fileInput && fileInput.addEventListener("change", function (e) {
e.stopPropagation()
e.preventDefault()

return inlineattach.onFileInputUpload(e);
})
};

inlineAttachment.editors.codemirror4 = codeMirrorEditor4;
Expand Down
11 changes: 11 additions & 0 deletions frontend/static/js/common/markdown-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ export const imageUploadOptions = {
},
};

export function createFileInput({ allowedTypes = [] }) {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.name = "attach-image"
if (allowedTypes) {
fileInput.accept = allowedTypes.join();
}

return fileInput;
}

export function handleFormSubmissionShortcuts(event) {
const isEnter = event.key === "Enter";
const isCtrlOrCmd = event.ctrlKey || event.metaKey;
Expand Down
Loading

0 comments on commit 4485776

Please sign in to comment.