1
0
Fork 0
mirror of https://github.com/Findus23/RPGnotes.git synced 2024-09-19 15:43:45 +02:00

send drafts to server

This commit is contained in:
Lukas Winkler 2022-07-04 00:35:01 +02:00
parent b34b7ea78d
commit c9eed87644
Signed by: lukas
GPG key ID: 54DE4D798D244853
9 changed files with 112 additions and 4 deletions

5
common/admin.py Normal file
View file

@ -0,0 +1,5 @@
from django.contrib import admin
from common.models import Draft
admin.site.register(Draft)

View file

@ -0,0 +1,27 @@
# Generated by Django 4.0.5 on 2022-07-03 22:19
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Draft',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description_md', models.TextField(blank=True, verbose_name='Description')),
('created', models.DateTimeField(auto_now_add=True)),
('last_modified', models.DateTimeField(auto_now=True)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='drafts', to=settings.AUTH_USER_MODEL, verbose_name='Player')),
],
),
]

View file

View file

@ -1,3 +1,4 @@
from .descriptionmodel import DescriptionModel from .descriptionmodel import DescriptionModel
from .nameslugmodel import NameSlugModel from .nameslugmodel import NameSlugModel
from .historymodel import HistoryModel from .historymodel import HistoryModel
from .draft import Draft

21
common/models/draft.py Normal file
View file

@ -0,0 +1,21 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from rpg_notes.settings import AUTH_USER_MODEL
class Draft(models.Model):
description_md = models.TextField(_("Description"), blank=True)
created = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now=True)
author = models.ForeignKey(
AUTH_USER_MODEL, on_delete=models.PROTECT,
related_name="drafts", verbose_name=_("Player")
)
def __str__(self):
# todo: add which object this is a draft of
return f"{self.created}: {self.author}"
class Meta:
get_latest_by = "created"

7
common/urls.py Normal file
View file

@ -0,0 +1,7 @@
from django.urls import path
from common import views
urlpatterns = [
path("api/draft/save", views.save_draft, name="save_draft"),
]

View file

@ -1,10 +1,13 @@
from django.http import HttpResponse import json
from django.http import HttpResponse, HttpRequest, HttpResponseBadRequest, JsonResponse
from django.shortcuts import render from django.shortcuts import render
from django.views.decorators.http import condition from django.views.decorators.http import condition
from django.views.generic import TemplateView from django.views.generic import TemplateView
from ipware import get_client_ip from ipware import get_client_ip
from sentry_sdk import last_event_id from sentry_sdk import last_event_id
from common.models import Draft
from rpg_notes.secrets import SENTRY_DSN from rpg_notes.secrets import SENTRY_DSN
from utils.assets import get_css, get_file_hash from utils.assets import get_css, get_file_hash
@ -17,7 +20,7 @@ class LanguageSelectView(TemplateView):
template_name = "common/languageselect.jinja" template_name = "common/languageselect.jinja"
def print_ip(request): def print_ip(request: HttpRequest) -> HttpResponse:
client_ip, is_routable = get_client_ip(request) client_ip, is_routable = get_client_ip(request)
return HttpResponse(repr(client_ip), content_type="text/plain") return HttpResponse(repr(client_ip), content_type="text/plain")
@ -26,6 +29,28 @@ def calc_etag(*args, **kwargs):
return get_file_hash()[:6] return get_file_hash()[:6]
def save_draft(request: HttpRequest) -> HttpResponse:
body = json.loads(request.body)
draft_md = body.get("draft_md", None)
if not draft_md:
return HttpResponseBadRequest()
try:
last_draft = Draft.objects.filter(author=request.user).latest()
if last_draft.description_md == draft_md:
return JsonResponse({
"message": "saved (unchanged)"
})
except Draft.DoesNotExist:
pass
draft = Draft()
draft.description_md = draft_md
draft.author = request.user
draft.save()
return JsonResponse({
"message": "saved"
})
@condition(etag_func=calc_etag) @condition(etag_func=calc_etag)
def debug_css(request): def debug_css(request):
css, source_map = get_css(debug=True) css, source_map = get_css(debug=True)

View file

@ -19,6 +19,7 @@ urlpatterns = [
path('note/', include("notes.urls")), path('note/', include("notes.urls")),
path('loot/', include("loot.urls")), path('loot/', include("loot.urls")),
path('search/', include("search.urls")), path('search/', include("search.urls")),
path('', include("common.urls")),
path('', include("campaigns.urls")) path('', include("campaigns.urls"))
] ]

View file

@ -6,7 +6,7 @@
*/ */
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
const ids = ["id_description_md"]; const ids = ["id_description_md"];
ids.forEach(function (id) { ids.forEach(function (id) {
const element = document.getElementById(id); const element = document.getElementById(id);
@ -33,7 +33,28 @@ document.addEventListener('DOMContentLoaded', function () {
second: '2-digit', second: '2-digit',
}, },
}, },
} },
inputStyle: "contenteditable",
status: ["lines", "words", "cursor", "saveStatus"],
}); });
window.editor = easyMDE
setInterval(function () {
const content = easyMDE.value();
fetch("/api/draft/save", {
method: "POST",
body: JSON.stringify({
"draft_md": content
}),
headers: {'X-CSRFToken': csrftoken},
})
.then(response => response.json())
.then(data => {
easyMDE.updateStatusBar("saveStatus", data.message)
setTimeout(e => easyMDE.updateStatusBar("saveStatus", ""), 5000)
}).catch(e => {
easyMDE.updateStatusBar("saveStatus", "error saving draft")
})
}, 1000 * 30)
}); });
}); });