1c013aa8d8
* If a Certified member deletes the final picture from their gallery page, their Certification Photo will be automatically rejected and they are instructed to begin the process again from the beginning. * Add nice Alert and Confirm modals around the website in place of the standard browser feature. Note: the inline confirm on submit buttons are still using the standard feature for now, as intercepting submit buttons named "intent" causes problems in getting the final form to submit.
455 lines
21 KiB
HTML
455 lines
21 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-link 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>
|
|
|
|
{{$Root := .}}
|
|
|
|
<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 has-text-dark">
|
|
{{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">
|
|
<a href="/markdown" target="_blank">Markdown formatting</a> supported.
|
|
</p>
|
|
</div>
|
|
|
|
{{if and (not .EditCommentID) (not .Thread)}}
|
|
<div class="field block">
|
|
<label for="message" class="label">Poll</label>
|
|
<div v-if="pollVisible">
|
|
<!-- MultipleChoice option -->
|
|
<div class="mb-2">
|
|
<label class="checkbox">
|
|
<input type="checkbox"
|
|
name="poll_multiple_choice"
|
|
value="true">
|
|
<small class="ml-2">Allow multiple selections per vote</small>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- 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 mb-0 is-mobile">
|
|
<div class="column is-narrow">
|
|
<small>Poll expires:</small>
|
|
</div>
|
|
<div class="column">
|
|
<div class="select is-small is-fullwidth">
|
|
<select v-model="expires"
|
|
name="poll_expires">
|
|
{{range .PollExpiresOptions}}
|
|
<option :value="{{.Value}}">{{.Label}}</option>
|
|
{{end}}
|
|
</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>
|
|
|
|
<!-- Non-explicit Photo Notice -->
|
|
{{if not .ExplicitPhotoAllowed}}
|
|
<p class="help mb-4">
|
|
<!-- If NEW thread on a non-explicit forum -->
|
|
{{if not .Thread}}
|
|
<strong class="has-text-danger">
|
|
<i class="fa fa-exclamation-triangle mr-1"></i>
|
|
Explicit Photo Notice:
|
|
</strong>
|
|
|
|
The forum you are posting to isn't marked as Explicit. If you are going to share
|
|
an <a href="/faq#define-explicit">Explicit</a> photo, <strong>please also mark this thread
|
|
as Explicit</strong> using the checkbox below!
|
|
{{else}}
|
|
<strong class="has-text-danger">
|
|
<i class="fa fa-ban mr-1"></i>
|
|
No Explicit Photos:
|
|
</strong>
|
|
|
|
This {{if not .Thread}}forum{{else}}thread{{end}} is not marked to accept Explicit
|
|
photos being shared. If you are going to upload a nude picture, <strong>please make sure</strong>
|
|
it is not an <a href="/faq#define-explicit">Explicit</a> photo. Thanks!
|
|
{{end}}
|
|
</p>
|
|
{{end}}
|
|
|
|
<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,.gif">
|
|
<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. -->
|
|
{{if and .CommentPhoto (HasSuffix .CommentPhoto.Filename ".mp4")}}
|
|
<video autoplay loop controls controlsList="nodownload" playsinline>
|
|
<source src="{{PhotoURL .CommentPhoto.Filename}}" type="video/mp4">
|
|
</video>
|
|
{{else}}
|
|
<img id="previewImage"{{if .CommentPhoto}} src="{{PhotoURL .CommentPhoto.Filename}}"{{end}}>
|
|
{{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>
|
|
{{if not .Forum.Explicit}}
|
|
<p class="help">
|
|
The forum you are posting this in is not marked as Explicit, however the occasional
|
|
Explicit thread is allowed as long as it is correctly tagged. <strong>Please</strong>
|
|
check this box if the text or attached photos on this thread will be sexual in nature!
|
|
</p>
|
|
{{end}}
|
|
</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-peace 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 ml-4 is-success">
|
|
Post Message
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
{{define "scripts"}}
|
|
|
|
<!-- 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}}
|
|
|
|
<script type="text/javascript">
|
|
const { createApp } = Vue;
|
|
|
|
// Some help from backend..
|
|
const pollOptions = {{ ToJSON .PollOptions }};
|
|
|
|
const app = createApp({
|
|
delimiters: ['[[', ']]'],
|
|
data() {
|
|
return {
|
|
pollVisible: pollOptions.length > 0,
|
|
answers: [
|
|
{value: ""},
|
|
{value: ""},
|
|
],
|
|
expires: parseInt({{.PollExpires}}), // 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) {
|
|
modalConfirm({
|
|
message: "A poll needs at least two options. Remove the poll?",
|
|
}).then(() => {
|
|
this.removePoll();
|
|
})
|
|
return;
|
|
}
|
|
this.answers.splice(idx, 1);
|
|
},
|
|
|
|
removePoll() {
|
|
this.answers = [
|
|
{value: ""},
|
|
{value: ""},
|
|
],
|
|
this.pollVisible = false;
|
|
}
|
|
},
|
|
});
|
|
app.mount("#app");
|
|
</script>
|
|
{{end}}
|