Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/data/blooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class Bloom:


def add_bloom(*, sender: User, content: str) -> Bloom:
# // 280 char limit
if len(content) > 280:
return {"error": "Too long!"}, 400
hashtags = [word[1:] for word in content.split(" ") if word.startswith("#")]

now = datetime.datetime.now(tz=datetime.UTC)
Expand Down
2 changes: 1 addition & 1 deletion backend/data/users.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass
from hashlib import scrypt
# from hashlib import scrypt
import hashlib
import random
import string
Expand Down
70 changes: 38 additions & 32 deletions front-end/components/bloom-form.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {apiService} from "../index.mjs";
import { apiService } from "../index.mjs";

/**
* Create a bloom form component
Expand All @@ -7,52 +7,58 @@ import {apiService} from "../index.mjs";
* @returns {DocumentFragment} - The bloom form fragment
*/
function createBloomForm(template, isLoggedIn) {
if (!isLoggedIn) return;
const bloomFormElement = document
.getElementById(template)
.content.cloneNode(true);
if (!isLoggedIn) return;
const bloomFormElement = document
.getElementById(template)
.content.cloneNode(true);

return bloomFormElement;
return bloomFormElement;
}

/**
* Handle bloom form submission
* @param {Event} event - The form submission event
*/
async function handleBloomSubmit(event) {
event.preventDefault();
const form = event.target;
const submitButton = form.querySelector("[data-submit]");
const originalText = submitButton.textContent;
const textarea = form.querySelector("textarea");
const content = textarea.value.trim();
event.preventDefault();
const form = event.target;
const submitButton = form.querySelector("[data-submit]");
const originalText = submitButton.textContent;
const textarea = form.querySelector("textarea");
const content = textarea.value.trim();

try {
// Make form inert while we call the back end
form.inert = true;
submitButton.textContent = "Posting...";
await apiService.postBloom(content);
textarea.value = "";
} catch (error) {
throw error;
} finally {
// Restore form
submitButton.textContent = originalText;
form.inert = false;
}
//add char limit
if (content.length > 280) {
alert("Too long! Maximum 280 characters.");
return;
}

try {
// Make form inert while we call the back end
form.inert = true;
submitButton.textContent = "Posting...";
await apiService.postBloom(content);
textarea.value = "";
} catch (error) {
throw error;
} finally {
// Restore form
submitButton.textContent = originalText;
form.inert = false;
}
}

/**
* Handle textarea input for bloom form
* @param {Event} event - The input event from textarea drives the character counter
*/
function handleTyping(event) {
const textarea = event.target;
const counter = textarea
.closest("[data-form]")
?.querySelector("[data-counter]");
const maxLength = parseInt(textarea.getAttribute("maxlength"), 10);
counter.textContent = `${textarea.value.length} / ${maxLength}`;
const textarea = event.target;
const counter = textarea
.closest("[data-form]")
?.querySelector("[data-counter]");
const maxLength = parseInt(textarea.getAttribute("maxlength"), 10);
counter.textContent = `${textarea.value.length} / ${maxLength}`;
}

export {createBloomForm, handleBloomSubmit, handleTyping};
export { createBloomForm, handleBloomSubmit, handleTyping };
112 changes: 57 additions & 55 deletions front-end/views/profile.mjs
Original file line number Diff line number Diff line change
@@ -1,66 +1,68 @@
import {renderEach, renderOne, destroy} from "../lib/render.mjs";
import { renderEach, renderOne, destroy } from "../lib/render.mjs";
import {
apiService,
state,
getLogoutContainer,
getLoginContainer,
getProfileContainer,
getTimelineContainer,
apiService,
state,
getLogoutContainer,
getLoginContainer,
getProfileContainer,
getTimelineContainer,
} from "../index.mjs";
import {createLogin, handleLogin} from "../components/login.mjs";
import {createLogout, handleLogout} from "../components/logout.mjs";
import {createProfile, handleFollow} from "../components/profile.mjs";
import {createBloom} from "../components/bloom.mjs";
import { createLogin, handleLogin } from "../components/login.mjs";
import { createLogout, handleLogout } from "../components/logout.mjs";
import { createProfile, handleFollow } from "../components/profile.mjs";
import { createBloom } from "../components/bloom.mjs";

// Profile view - just this person's blooms and their profile
function profileView(username) {
destroy();
destroy();

const existingProfile = state.profiles.find((p) => p.username === username);
const existingProfile = state.profiles.find((p) => p.username === username);

// Only fetch profile if we don't have it or if it's incomplete
if (!existingProfile || !existingProfile.recent_blooms) {
apiService.getProfile(username);
}
// Only fetch profile if we don't have it or if it's incomplete
if (!existingProfile || !existingProfile.recent_blooms) {
apiService.getProfile(username);
}

renderOne(
state.isLoggedIn,
getLogoutContainer(),
"logout-template",
createLogout
);
document
.querySelector("[data-action='logout']")
?.addEventListener("click", handleLogout);
renderOne(
state.isLoggedIn,
getLoginContainer(),
"login-template",
createLogin
);
document
.querySelector("[data-action='login']")
?.addEventListener("click", handleLogin);
renderOne(
state.isLoggedIn,
getLogoutContainer(),
"logout-template",
createLogout
);
document
.querySelector("[data-action='logout']")
?.addEventListener("click", handleLogout);
renderOne(
state.isLoggedIn,
getLoginContainer(),
"login-template",
createLogin
);
document
.querySelector("[data-action='login']")
?.addEventListener("click", handleLogin);
// .querySelector(".login__form")
// ?.addEventListener("submit", handleLogin);

const profileData = state.profiles.find((p) => p.username === username);
if (profileData) {
renderOne(
{
profileData,
whoToFollow: state.isLoggedIn ? state.whoToFollow : [],
isLoggedIn: state.isLoggedIn,
},
getProfileContainer(),
"profile-template",
createProfile
);
renderEach(
profileData.recent_blooms || [],
getTimelineContainer(),
"bloom-template",
createBloom
);
}
const profileData = state.profiles.find((p) => p.username === username);
if (profileData) {
renderOne(
{
profileData,
whoToFollow: state.isLoggedIn ? state.whoToFollow : [],
isLoggedIn: state.isLoggedIn,
},
getProfileContainer(),
"profile-template",
createProfile
);
renderEach(
profileData.recent_blooms || [],
getTimelineContainer(),
"bloom-template",
createBloom
);
}
}

export {profileView};
export { profileView };