bb79b5cbf3
Got initial Poll table and UI started: * Polls can be attached to any NEW forum post (can't edit poll details after creation) * Max 100 options (theoretically unlimited), expiration time. * UI: shows radio button list on posts having a poll, no submit handler yet created.
406 lines
18 KiB
HTML
406 lines
18 KiB
HTML
{{define "title"}}
|
|
{{if .EditCommentID}}
|
|
Edit Comment
|
|
{{else if .Thread}}
|
|
Reply to Thread
|
|
{{else}}
|
|
New Forum Post
|
|
{{end}}
|
|
{{end}}
|
|
{{define "content"}}
|
|
<div class="container" id="app">
|
|
<section class="hero is-info is-bold">
|
|
<div class="hero-body">
|
|
<div class="container">
|
|
<h1 class="title">
|
|
{{if .EditCommentID}}
|
|
Edit comment on: {{or .Thread.Title "Untitled Thread"}}
|
|
{{else if .Thread}}
|
|
Reply to: {{or .Thread.Title "Untitled Thread"}}
|
|
{{else}}
|
|
Post to: {{.Forum.Title}}
|
|
{{end}}
|
|
</h1>
|
|
<h2 class="subtitle">
|
|
/f/{{.Forum.Fragment}}
|
|
</h2>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="block p-4">
|
|
<div class="columns is-centered">
|
|
<div class="column is-half">
|
|
|
|
<div class="card" style="width: 100%; max-width: 640px">
|
|
<header class="card-header has-background-link">
|
|
<p class="card-header-title has-text-light">
|
|
<span class="icon"><i class="fa fa-message"></i></span>
|
|
{{if .EditCommentID}}
|
|
Edit Comment
|
|
{{else if .Thread}}
|
|
Reply to Thread
|
|
{{else}}
|
|
New Thread
|
|
{{end}}
|
|
</p>
|
|
</header>
|
|
<div class="card-content">
|
|
|
|
{{if and (eq .Request.Method "POST") (ne .Message "")}}
|
|
<label class="label">Preview:</label>
|
|
<div class="box content has-background-warning-light">
|
|
{{ToMarkdown .Message}}
|
|
</div>
|
|
{{end}}
|
|
|
|
<form action="/forum/post?to={{.Forum.Fragment}}{{if .Thread}}&thread={{.Thread.ID}}{{end}}{{if .EditCommentID}}&edit={{.EditCommentID}}{{end}}" method="POST" enctype="multipart/form-data">
|
|
{{InputCSRF}}
|
|
|
|
{{if not .Thread}}
|
|
<div class="field block">
|
|
<label for="title" class="label">Title</label>
|
|
<input type="text" class="input"
|
|
name="title" id="title"
|
|
placeholder="A title for your post"
|
|
value="{{.PostTitle}}"
|
|
required>
|
|
<p class="help">Required.</p>
|
|
</div>
|
|
{{end}}
|
|
|
|
<div class="field block">
|
|
<label for="message" class="label">Message</label>
|
|
<textarea class="textarea" cols="80" rows="8"
|
|
name="message"
|
|
id="message"
|
|
{{if not .Forum.PermitPhotos}}required{{end}}
|
|
placeholder="Message">{{.Message}}</textarea>
|
|
<p class="help">
|
|
Markdown formatting supported.
|
|
</p>
|
|
</div>
|
|
|
|
{{if not .EditCommentID}}
|
|
<div class="field block">
|
|
<label for="message" class="label">Poll <span class="tag is-success">NEW!</span></label>
|
|
<div v-if="pollVisible">
|
|
<!-- Answer rows -->
|
|
<div v-for="(answer, i) in answers"
|
|
v-bind:key="i"
|
|
class="columns mb-0 is-mobile">
|
|
<div class="column pr-0">
|
|
<input type="text"
|
|
class="input"
|
|
v-model="answer.value"
|
|
:name="'answer'+i"
|
|
:placeholder="`Option ${i+1}`">
|
|
</div>
|
|
<div class="column pl-2 is-narrow">
|
|
<button type="button"
|
|
class="button is-danger is-outlined"
|
|
@click="removeOption(i)">
|
|
<i class="fa fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add option -->
|
|
<div v-if="answers.length < answersLimit">
|
|
<button type="button"
|
|
class="button is-small is-success is-outlined"
|
|
@click="addOption()">
|
|
<i class="fa fa-plus mr-3"></i>
|
|
Add another option
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Settings -->
|
|
<div class="columns mt-2 is-mobile">
|
|
<div class="column is-narrow">
|
|
Poll expires:
|
|
</div>
|
|
<div class="column">
|
|
<div class="select is-small is-fullwidth">
|
|
<select v-model="expires"
|
|
name="poll_expires">
|
|
<option :value="0">Never</option>
|
|
<option :value="1">1 Day</option>
|
|
<option :value="2">2 Days</option>
|
|
<option :value="3">3 Days</option>
|
|
<option :value="4">4 Days</option>
|
|
<option :value="5">5 Days</option>
|
|
<option :value="6">6 Days</option>
|
|
<option :value="7">7 Days</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else>
|
|
<button type="button"
|
|
class="button is-success is-small"
|
|
@click="pollVisible = true">Make this a poll</button>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
<!-- Photo attachment widget -->
|
|
{{if .Forum.PermitPhotos}}
|
|
<!-- Intent: upload, remove, or replace -->
|
|
<input type="hidden" name="photo_intent" id="photoIntent">
|
|
<input type="hidden" name="photo_id" value="{{if .CommentPhoto}}{{.CommentPhoto.ID}}{{end}}">
|
|
|
|
<!-- Drag/Drop Modal -->
|
|
<div class="modal" id="drop-modal">
|
|
<div class="modal-background"></div>
|
|
<div class="modal-content">
|
|
<div class="box content has-text-centered">
|
|
<h1><i class="fa fa-upload mr-2"></i> Drop image to select it for upload</h1>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field block">
|
|
<label class="label">Photo Attachment</label>
|
|
<div class="file has-name is-fullwidth">
|
|
<label class="file-label">
|
|
<input class="file-input" type="file"
|
|
name="file"
|
|
id="file"
|
|
accept=".jpg,.jpeg,.jpe,.png">
|
|
<span class="file-cta">
|
|
<span class="file-icon">
|
|
<i class="fas fa-upload"></i>
|
|
</span>
|
|
<span class="file-label">
|
|
Choose a file…
|
|
</span>
|
|
</span>
|
|
<span class="file-name" id="fileName">
|
|
Select a file
|
|
</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="box" id="imagePreview" {{if not .CommentPhoto}}style="display: none"{{end}}>
|
|
<h3 class="subtitle">
|
|
Selected image:
|
|
<button type="button" class="button is-danger is-small ml-4" id="removePhoto">
|
|
<i class="fa fa-trash"></i>
|
|
</button>
|
|
</h3>
|
|
|
|
<!-- Container of img tags for the selected photo preview. -->
|
|
<img id="previewImage"{{if .CommentPhoto}} src="{{PhotoURL .CommentPhoto.Filename}}"{{end}}>
|
|
</div>
|
|
{{end}}
|
|
|
|
<hr>
|
|
|
|
{{if or (not .Thread) .EditThreadSettings}}
|
|
<div class="field block">
|
|
{{if or .CurrentUser.IsAdmin (and .Forum (eq .Forum.OwnerID .CurrentUser.ID))}}
|
|
<div class="mb-1">
|
|
<label class="checkbox">
|
|
<input type="checkbox"
|
|
name="pinned"
|
|
value="true"
|
|
{{if .IsPinned}}checked{{end}}>
|
|
Pinned to top
|
|
</label>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if or .CurrentUser.Explicit .IsExplicit}}
|
|
<div class="mb-1">
|
|
<label class="checkbox">
|
|
<input type="checkbox"
|
|
name="explicit"
|
|
value="true"
|
|
{{if .IsExplicit}}checked{{end}}>
|
|
Mark as Explicit (NSFW)
|
|
</label>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .CurrentUser.IsAdmin}}
|
|
<div>
|
|
<label class="checkbox has-text-danger">
|
|
<input type="checkbox"
|
|
name="noreply"
|
|
value="true"
|
|
{{if .IsNoReply}}checked{{end}}>
|
|
No replies allowed <i class="fa fa-gavel ml-1"></i>
|
|
</label>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
|
|
<div class="field has-text-centered">
|
|
<button type="submit"
|
|
name="intent"
|
|
value="preview"
|
|
class="button is-link">
|
|
Preview
|
|
</button>
|
|
<button type="submit"
|
|
name="intent"
|
|
value="submit"
|
|
class="button is-success">
|
|
Post Message
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Script for photo upload on photo boards -->
|
|
{{if .Forum.PermitPhotos}}
|
|
<script type="text/javascript">
|
|
window.addEventListener("DOMContentLoaded", (event) => {
|
|
let $file = document.querySelector("#file"),
|
|
$fileName = document.querySelector("#fileName"),
|
|
$hiddenPreview = document.querySelector("#imagePreview"),
|
|
$previewImage = document.querySelector("#previewImage"),
|
|
$dropArea = document.querySelector("#drop-modal"),
|
|
$removePhoto = document.querySelector("#removePhoto"),
|
|
$photoIntent = document.querySelector("#photoIntent"),
|
|
$body = document.querySelector("body");
|
|
|
|
// Common handler for file selection (file input or drag/drop)
|
|
let onFile = (file) => {
|
|
$photoIntent.value = "{{if .CommentPhoto}}replace{{else}}upload{{end}}";
|
|
$fileName.innerHTML = file.name;
|
|
|
|
// Read the image to show the preview on-page.
|
|
const reader = new FileReader();
|
|
reader.addEventListener("load", () => {
|
|
const uploadedImg = reader.result;
|
|
$hiddenPreview.style.display = "block";
|
|
|
|
$previewImage.src = uploadedImg;
|
|
$previewImage.style.display = "block";
|
|
$previewImage.style.maxWidth = "100%";
|
|
$previewImage.style.height = "auto";
|
|
});
|
|
reader.readAsDataURL(file);
|
|
};
|
|
|
|
// Set up drag/drop file upload events.
|
|
$body.addEventListener("dragenter", function(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
$dropArea.classList.add("is-active");
|
|
});
|
|
$body.addEventListener("dragover", function(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
$dropArea.classList.add("is-active");
|
|
});
|
|
$body.addEventListener("dragleave", function(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
$dropArea.classList.remove("is-active");
|
|
});
|
|
$body.addEventListener("drop", function(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
$dropArea.classList.remove("is-active");
|
|
|
|
// Grab the file.
|
|
let dt = e.dataTransfer;
|
|
let file = dt.files[0];
|
|
|
|
// Set the file on the input field too.
|
|
$file.files = dt.files;
|
|
|
|
onFile(file);
|
|
});
|
|
|
|
// File input handler.
|
|
$file.addEventListener("change", function() {
|
|
let file = this.files[0];
|
|
onFile(file);
|
|
});
|
|
|
|
// Removal button.
|
|
$removePhoto.addEventListener("click", function() {
|
|
$photoIntent.value = "remove";
|
|
$fileName.innerHTML = "Select a file";
|
|
$file.value = '';
|
|
$hiddenPreview.style.display = 'none';
|
|
});
|
|
});
|
|
</script>
|
|
{{end}}
|
|
|
|
</div>
|
|
{{end}}
|
|
{{define "scripts"}}
|
|
<script type="text/javascript">
|
|
const { createApp } = Vue;
|
|
|
|
// Some help from backend..
|
|
const pollOptions = {{ ToJSON .PollOptions }};
|
|
|
|
const app = createApp({
|
|
delimiters: ['[[', ']]'],
|
|
data() {
|
|
return {
|
|
pollVisible: true,
|
|
answers: [
|
|
{value: ""},
|
|
{value: ""},
|
|
],
|
|
expires: 3, // days
|
|
answersLimit: 100,
|
|
}
|
|
},
|
|
mounted() {
|
|
// Set the posted poll options.
|
|
if (pollOptions.length > 0) {
|
|
this.answers = [];
|
|
for (let row of pollOptions) {
|
|
this.answers.push({value: row});
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
addOption() {
|
|
console.log(this.answers);
|
|
if (this.answers.length < this.answersLimit) {
|
|
this.answers.push({value: ""});
|
|
}
|
|
},
|
|
|
|
removeOption(idx) {
|
|
if (this.answers.length <= 2) {
|
|
if (!window.confirm("A poll needs at least two options. Remove the poll?")) {
|
|
return;
|
|
}
|
|
this.removePoll();
|
|
return;
|
|
}
|
|
this.answers.splice(idx, 1);
|
|
},
|
|
|
|
removePoll() {
|
|
this.answers = [
|
|
{value: ""},
|
|
{value: ""},
|
|
],
|
|
this.pollVisible = false;
|
|
}
|
|
},
|
|
});
|
|
app.mount("#app");
|
|
</script>
|
|
{{end}} |