Introduction
Welcome to the Story Creator tutorial! This guide will walk you through the process of creating a new story using the editor, uploading images, and saving your story as a draft. By the end of this tutorial, you’ll be able to create a compelling story and save it in your account.
Step 1: Setting Up the Story Editor
The Quill Editor is used to write the story content in a rich text format. You can add headings, bold/italic text, images, and much more. Here’s how the editor is set up:
<!-- Quill Editor HTML -->
<div id="editor"></div>
You can type and format your story directly in this editor. Here’s the configuration to initialize Quill:
// Initialize Quill editor
var quill = new Quill("#editor", {
theme: "snow",
modules: {
toolbar: [
[{ header: [1, 2, false] }],
["bold", "italic", "underline"],
[{ list: "ordered" }, { list: "bullet" }],
["image", "code-block"],
],
},
});
Step 2: Handling Image Uploads
You can add images to your story using the toolbar. When you click the image button, an image upload dialog will appear, allowing you to upload an image.
// Handle image upload
const toolbar = quill.getModule("toolbar");
toolbar.addHandler("image", () => {
const input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", "image/png, image/jpeg, image/gif");
input.onchange = async () => {
const file = input.files[0];
if (file) {
const formData = new FormData();
formData.append("image", file);
const response = await fetch("/api/upload", {
method: "POST",
body: formData,
});
if (response.ok) {
const data = await response.json();
const imageUrl = data.imagePath;
const range = quill.getSelection();
quill.insertEmbed(range.index, "image", imageUrl);
} else {
alert("Failed to upload image");
}
}
};
input.click();
});
This code will allow you to upload images and embed them into your story.
Step 3: Creating the Story Form
In addition to the Quill Editor for content, you also need to fill in some basic story details like the title and author name.
<form>
<label for="story-title">Story Title:</label>
<input type="text" id="story-title" required>
<label for="author-first-name">Author First Name:</label>
<input type="text" id="author-first-name" required>
<label for="author-last-name">Author Last Name:</label>
<input type="text" id="author-last-name" required>
<button type="button" class="submit-button">Save Story</button>
</form>
Step 4: Saving the Story as a Draft
Once you’ve finished writing and adding content to your story, you can save it as a draft. The following JavaScript code handles sending the story data to your backend via a POST request:
// Save button functionality
document.querySelector(".submit-button").addEventListener("click", async function () {
const storyTitle = document.getElementById("story-title").value.trim();
const authorFirstName = document.getElementById("author-first-name").value.trim();
const authorLastName = document.getElementById("author-last-name").value.trim();
const storyText = quill.root.innerHTML;
if (storyTitle && storyText.trim()) {
const confirmation = confirm("Are you sure you want to save the story as a draft?");
if (confirmation) {
const token = localStorage.getItem("token");
const userId = localStorage.getItem("user_id");
if (!token) {
alert("You must be logged in to save a story.");
return;
}
try {
const response = await fetch("/api/stories", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
title: storyTitle,
content: storyText,
authorFirstName: authorFirstName,
authorLastName: authorLastName,
}),
});
if (response.ok) {
alert("Your story has been saved as a draft! Redirecting to Drafts to Edit or Publish");
location.reload(); // Refresh the page
} else {
const errorData = await response.json();
alert("Error saving your story: " + (errorData.message || response.statusText));
}
} catch (error) {
console.error("Error during the fetch:", error);
alert("An unexpected error occurred. Please try again.");
}
}
} else {
alert("Please enter a title and write a story before saving!");
}
});
Step 5: Optional Buy Image and Link Fields
In addition to the basic story fields, you can also add optional fields for a buy image and buy link. These fields are useful if you want to include a promotional image and link for users to buy your story.
<!-- Optional fields for image and link -->
<input type="text" id="buy-image" placeholder="Enter Image URL (optional)">
<input type="text" id="buy-link" placeholder="Enter Buy Link (optional)">
In the backend, these fields will be saved along with the story.
Step 6: Backend API to Save the Story
The backend API will handle saving the story to the database. Here’s the code for the backend API:
// API to save a new story with optional buyImage and buyLink
app.post("/api/stories", authenticateToken, async (req, res) => {
const {
title,
content,
buyImage = null,
buyLink = null,
authorFirstName,
authorLastName,
} = req.body;
const submittedBy = req.user.id; // Get the current user's ID
const submittedOn = new Date(); // Current date and time
if (!title || !content || !authorFirstName || !authorLastName) {
return res.status(400).json({
message: "Title, content, authorFirstName, and authorLastName are required",
});
}
const query =
"INSERT INTO stories (title, content, buyImage, buyLink, submittedBy, submittedOn, authorFirstName, authorLastName) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
try {
const result = await executeQuery(query, [
title,
content,
buyImage,
buyLink,
submittedBy,
submittedOn,
authorFirstName,
authorLastName,
]);
res.status(201).json({
id: result.insertId,
title,
content,
buyImage,
buyLink,
submittedBy,
submittedOn,
authorFirstName,
authorLastName,
});
logWithTimestamp(`Story created: ${title} by ${authorFirstName} ${authorLastName}`);
} catch (err) {
logWithTimestamp(`Error saving story: ${err}`);
res.status(500).json({ message: "Internal server error." });
}
});
Conclusion
You’ve now successfully created a story using the Story Creator app. You can add rich content, images, and links to your story, then save it as a draft for future editing or publishing.