Skip to content
Open
Show file tree
Hide file tree
Changes from all 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: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"liveServer.settings.root": "front-end/",
"python.defaultInterpreterPath": "backend/.venv/bin/python"
"python.defaultInterpreterPath": "backend/.venv/bin/python",
"python-envs.defaultEnvManager": "ms-python.python:system"
}
2 changes: 1 addition & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/.env
/.venv/
/venv/
*.pyc
9 changes: 7 additions & 2 deletions backend/data/blooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from data.connection import db_cursor
from data.users import User

MAX_BLOOM_LENGTH = 280

@dataclass
class Bloom:
Expand All @@ -16,9 +17,13 @@ class Bloom:


def add_bloom(*, sender: User, content: str) -> Bloom:
# reject any new posts that are longer than 280 characters
if len(content) > MAX_BLOOM_LENGTH:
raise ValueError("Bloom content cannot exceed 280 characters.")

hashtags = [word[1:] for word in content.split(" ") if word.startswith("#")]

now = datetime.datetime.now(tz=datetime.UTC)
now = datetime.datetime.now(tz=datetime.timezone.utc)
bloom_id = int(now.timestamp() * 1000000)
with db_cursor() as cur:
cur.execute(
Expand All @@ -27,7 +32,7 @@ def add_bloom(*, sender: User, content: str) -> Bloom:
bloom_id=bloom_id,
sender_id=sender.id,
content=content,
timestamp=datetime.datetime.now(datetime.UTC),
timestamp=datetime.datetime.now(datetime.timezone.utc),
),
)
for hashtag in hashtags:
Expand Down
4 changes: 2 additions & 2 deletions front-end/components/bloom-form.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {apiService} from "../index.mjs";

import { MAX_BLOOM_LENGTH } from "./bloom.mjs";
/**
* Create a bloom form component
* @param {string} template - The ID of the template to clone
Expand Down Expand Up @@ -51,7 +51,7 @@ function handleTyping(event) {
const counter = textarea
.closest("[data-form]")
?.querySelector("[data-counter]");
const maxLength = parseInt(textarea.getAttribute("maxlength"), 10);
const maxLength = parseInt(textarea.getAttribute("maxlength"), 10) || MAX_BLOOM_LENGTH;
counter.textContent = `${textarea.value.length} / ${maxLength}`;
}

Expand Down
14 changes: 10 additions & 4 deletions front-end/components/bloom.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* "sent_timestamp": "datetime as ISO 8601 formatted string"}

*/
export const MAX_BLOOM_LENGTH = 280;
const createBloom = (template, bloom) => {
if (!bloom) return;
const bloomFrag = document.getElementById(template).content.cloneNode(true);
Expand All @@ -26,6 +27,11 @@ const createBloom = (template, bloom) => {
bloomUsername.textContent = bloom.sender;
bloomTime.textContent = _formatTimestamp(bloom.sent_timestamp);
bloomTimeLink.setAttribute("href", `/bloom/${bloom.id}`);
// FIX: If a bloom from the database is over 280 characters, cut it short
let displayContent = bloom.content || "";
if (displayContent.length > MAX_BLOOM_LENGTH) {
displayContent = displayContent.slice(0, MAX_BLOOM_LENGTH - 3) + "...";
}
bloomContent.replaceChildren(
...bloomParser.parseFromString(_formatHashtags(bloom.content), "text/html")
.body.childNodes
Expand All @@ -36,10 +42,10 @@ const createBloom = (template, bloom) => {

function _formatHashtags(text) {
if (!text) return text;
return text.replace(
/\B#[^#]+/g,
(match) => `<a href="/hashtag/${match.slice(1)}">${match}</a>`
);
return text.replace(/(^|\s)(#[A-Za-z0-9_]+)/g, (full, prefix, tag) => {
const encoded = encodeURIComponent(tag.slice(1));
return `${prefix}<a href="/hashtag/${encoded}">${tag}</a>`;
});
}

function _formatTimestamp(timestamp) {
Expand Down
2 changes: 2 additions & 0 deletions front-end/components/login.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ async function handleLogin(event) {
const password = formData.get("password");

await apiService.login(username, password);
//the browser refreshes on that exact same URL
window.location.reload();
} catch (error) {
throw error;
} finally {
Expand Down
1 change: 1 addition & 0 deletions front-end/components/logout.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function createLogout(template, isLoggedIn) {
async function handleLogout(event) {
try {
apiService.logout();
window.location.href = "/";
} catch (error) {
throw error;
}
Expand Down
7 changes: 5 additions & 2 deletions front-end/views/hashtag.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ import {createHeading} from "../components/heading.mjs";

// Hashtag view: show all tweets containing this tag

function hashtagView(hashtag) {
async function hashtagView(hashtag) {
destroy();

apiService.getBloomsByHashtag(hashtag);
const normalizedHashtag = hashtag.startsWith("#") ? hashtag : `#${hashtag}`;
if (state.currentHashtag !== normalizedHashtag) {
await apiService.getBloomsByHashtag(hashtag);
}

renderOne(
state.isLoggedIn,
Expand Down