Skip to content

Commit

Permalink
Move save announecement creation to the frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
jorg-vr committed Jun 17, 2022
1 parent 361bc86 commit 0118b8f
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 38 deletions.
22 changes: 6 additions & 16 deletions app/assets/javascripts/code_listing/annotation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { NewSavedAnnotation } from "components/saved_annotations/new_saved_annotation";

export type AnnotationType = "error" | "info" | "user" | "warning" | "question";
export type QuestionState = "unanswered" | "answered" | "in_progress";

Expand All @@ -10,6 +12,7 @@ export abstract class Annotation {
public readonly line: number | null;
public readonly text: string;
public readonly type: AnnotationType;
public readonly id: number;

protected constructor(line: number | null, text: string, type: AnnotationType) {
this.__html = null;
Expand All @@ -34,10 +37,6 @@ export abstract class Annotation {
// Do nothing.
}

protected save(): void {
// Do nothing.
}

public get global(): boolean {
return this.line === null;
}
Expand Down Expand Up @@ -96,14 +95,9 @@ export abstract class Annotation {

header.appendChild(editLink);

const saveLink = document.createElement("a");
saveLink.addEventListener("click", () => this.save());
saveLink.classList.add("btn", "btn-icon", "annotation-control-button", "annotation-edit");
saveLink.title = this.saveTitle;

const saveIcon = document.createElement("i");
saveIcon.classList.add("mdi", "mdi-content-save");
saveLink.appendChild(saveIcon);
const saveLink = new NewSavedAnnotation();
saveLink.fromAnnotationId = this.id;
saveLink.annotationText = this.rawText;

header.appendChild(saveLink);
}
Expand Down Expand Up @@ -215,10 +209,6 @@ export abstract class Annotation {
return "";
}

protected get saveTitle(): string {
return "";
}

protected get hasNotice(): boolean {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/code_listing/code_listing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
UserAnnotationFormData
} from "code_listing/user_annotation";
import { createUserAnnotation, getAllUserAnnotations } from "code_listing/question_annotation";
import "components/saved_annotation_input";
import "components/saved_annotations/saved_annotation_input";

const annotationGlobalAdd = "#add_global_annotation";
const annotationsGlobal = "#feedback-table-global-annotations";
Expand Down
14 changes: 0 additions & 14 deletions app/assets/javascripts/code_listing/user_annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,6 @@ export class UserAnnotation extends Annotation {
this.__html.querySelector(".annotation-text").replaceWith(editor);
}

protected save(): void {
const modal = new bootstrap.Modal(document.getElementById("save-annotation"));
const fromField = document.getElementById("saved_annotation_from");
const textField = document.getElementById("saved_annotation_annotation_text");

fromField.value = this.id;
textField.value = this.__rawText;
modal.show();
}

protected get meta(): string {
const timestamp = I18n.l("time.formats.annotation", this.createdAt);
const user = this.user.name;
Expand Down Expand Up @@ -156,8 +146,4 @@ export class UserAnnotation extends Annotation {
protected get editTitle(): string {
return I18n.t("js.user_annotation.edit");
}

protected get saveTitle(): string {
return I18n.t("js.user_annotation.save");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { customElement, property } from "lit/decorators.js";
import { html, TemplateResult } from "lit";
import { ShadowlessLitElement } from "components/shadowless_lit_element";
import { createSavedAnnotation, SavedAnnotation } from "state/SavedAnnotations";
import { ref } from "lit/directives/ref.js";
import { Modal } from "bootstrap";
import "./saved_annotation_form";

@customElement("d-new-saved-annotation")
export class NewSavedAnnotation extends ShadowlessLitElement {
@property({ type: Number, attribute: "from-annotation-id" })
fromAnnotationId: number;
@property({ type: String, attribute: "annotation-text" })
annotationText: string;

@property({ state: true })
errors: string[];

savedAnnotation: SavedAnnotation;
modal: Modal;

get newSavedAnnotation(): SavedAnnotation {
return {
id: undefined,
title: "",
annotation_text: this.annotationText
};
}

async createSavedAnnotation(): Promise<void> {
try {
await createSavedAnnotation({
from: this.fromAnnotationId,
saved_annotation: this.savedAnnotation
});
this.errors = undefined;
this.modal?.hide();
} catch (errors) {
this.errors = errors;
}
}

initModal(el: Element): void {
if (!this.modal) {
this.modal = new Modal(el);
}
}

render(): TemplateResult {
return html`
<a class="btn btn-icon annotation-control-button annotation-edit"
title="${I18n.t("js.saved_annotation.new.button_title")}"
@click=${() => this.modal.show()}
>
<i class="mdi mdi-content-save"></i>
</a>
<div class="modal fade" ${ref(el => this.initModal(el))} tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">${I18n.t("js.saved_annotation.new.title")}</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
${this.errors !== undefined ? html`
<div class="callout callout-danger">
<h4>${I18n.t("js.saved_annotation.new.errors", { count: this.errors.length })}</h4>
<ul>
${this.errors.map(error => html`<li>${error}</li>`)}
</ul>
</div>
` : ""}
<d-saved-annotation-form
.savedAnnotation=${this.newSavedAnnotation}
@change=${e => this.savedAnnotation = e.detail}
></d-saved-annotation-form>
</div>
<div class="modal-footer">
<button class="btn btn-primary btn-text" @click=${() => this.createSavedAnnotation()}>
${I18n.t("js.saved_annotation.new.save")}
</button>
</div>
</div>
</div>
</div>`;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { customElement, property } from "lit/decorators.js";
import { html, TemplateResult } from "lit";
import { ShadowlessLitElement } from "components/shadowless_lit_element";
import { SavedAnnotation } from "state/SavedAnnotations";
import {unsafeHTML} from "lit/directives/unsafe-html.js";

@customElement("d-saved-annotation-form")
export class SavedAnnotationForm extends ShadowlessLitElement {
@property({ type: Object })
savedAnnotation: SavedAnnotation;

savedAnnotationChanged(): void {
const event = new CustomEvent("change", {
detail: this.savedAnnotation,
bubbles: true,
composed: true }
);
this.dispatchEvent(event);
}

updateTitle(e: Event): void {
this.savedAnnotation.title = (e.target as HTMLInputElement).value;
e.stopPropagation();
this.savedAnnotationChanged();
}

updateText(e: Event): void {
this.savedAnnotation.annotation_text = (e.target as HTMLTextAreaElement).value;
e.stopPropagation();
this.savedAnnotationChanged();
}

render(): TemplateResult {
return html`
<form class="form">
<div class="field form-group row">
<label class="col-sm-4 col-form-label">
${I18n.t("js.saved_annotation.title")}
</label>
<div class="col-sm-8">
<input required="required" class="form-control" type="text"
.value=${this.savedAnnotation.title} @change=${e => this.updateTitle(e)}>
</div>
</div>
<div class="field form-group row">
<label class="col-sm-4 col-form-label">
${I18n.t("js.saved_annotation.annotation_text")}
</label>
<div class="col-sm-8">
<textarea required="required" class="form-control" rows="4"
.value=${this.savedAnnotation.annotation_text} @change=${e => this.updateText(e)}></textarea>
</div>
<span class="help-block offset-sm-4 col-sm-8">
${unsafeHTML(I18n.t("js.saved_annotation.form.markdown_html"))}
</span>
</div>
</form>`;
}
}
4 changes: 2 additions & 2 deletions app/assets/javascripts/i18n/translations.js

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions app/assets/javascripts/state/SavedAnnotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,31 @@ export async function fetchSavedAnnotation(id: number): Promise<SavedAnnotation>
return savedAnnotationsById.get(id);
}

export async function createSavedAnnotation(form: FormData): Promise<number> {
export async function createSavedAnnotation(data: { from: number, saved_annotation: SavedAnnotation} ): Promise<number> {
const url = `${URL}.json`;
const response = await fetch(url, {
method: "post",
body: form,
body: JSON.stringify(data),
headers: {
"X-CSRF-Token": $("meta[name='csrf-token']").attr("content"),
"Content-type": "application/json"
},
});
if (response.status === 422) {
const errors = await response.json();
throw errors;
}
const savedAnnotation: SavedAnnotation = await response.json();
events.publish("fetchSavedAnnotations");
events.publish(`fetchSavedAnnotation${savedAnnotation.id}`, savedAnnotation.id);
return savedAnnotation.id;
}

export async function updateSavedAnnotation(number, id: number, form: FormData): Promise<void> {
export async function updateSavedAnnotation(number, id: number, data: {saved_annotation: SavedAnnotation}): Promise<void> {
const url = `${URL}/${id}`;
await fetch(url, {
method: "put",
body: form,
body: JSON.stringify(data),
});
events.publish("fetchSavedAnnotations");
events.publish(`fetchSavedAnnotation${id}`, id);
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/saved_annotations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def index
def show; end

def create
Rails.logger.info params
annotation = Annotation.find(params[:from])
authorize annotation, :show?
@saved_annotation = SavedAnnotation.new(permitted_attributes(SavedAnnotation).merge({ user: current_user, course: annotation.course, exercise: annotation.submission.exercise }))
Expand All @@ -24,7 +25,7 @@ def create
format.json { render :show, status: :created, location: @saved_annotation }
format.js { render :show, status: :created }
else
format.json { render json: @saved_annotation.errors, status: :unprocessable_entity }
format.json { render json: @saved_annotation.errors.full_messages, status: :unprocessable_entity }
format.js { render :new, status: :unprocessable_entity }
end
end
Expand Down
1 change: 1 addition & 0 deletions app/views/saved_annotations/show.json.jbuilder
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
json.extract! @saved_annotation, :id, :title, :annotation_text, :user_id, :exercise_id, :course_id, :created_at, :updated_at
10 changes: 10 additions & 0 deletions config/locales/js/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,13 @@ en:
description_languages: Language
courses: Course
question_states: Status
saved_annotation:
title: Title
annotation_text: Text
form:
markdown_html: '<a href="https://docs.dodona.be/en/references/exercise-description/#markdown" target="_blank">Markdown</a> is supported.'
new:
button_title: 'Save comment'
save: "Save"
title: "Save comment for re-use"
errors: "%{count} errors prevented saving"
11 changes: 11 additions & 0 deletions config/locales/js/nl.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,14 @@ nl:
description_languages: Taal
courses: Cursus
question_states: Toestand
saved_annotation:
title: Titel
annotation_text: Tekst
form:
markdown_html: '<a href="https://docs.dodona.be/nl/references/exercise-description/#markdown" target="_blank">Markdown</a> wordt ondersteund.'
new:
button_title: 'Opmerking opslaan'
save: "Opslaan"
title: "Opmerking opslaan om later te hergebruiken"
errors: "%{count} errors verhinderen het opslaan"

0 comments on commit 0118b8f

Please sign in to comment.