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
26 changes: 18 additions & 8 deletions backend/data/blooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,24 @@ class Bloom:
sender: User
content: str
sent_timestamp: datetime.datetime
original_sender: Optional[str] = None
rebloom_count: int = 0


def add_bloom(*, sender: User, content: str) -> Bloom:
def add_bloom(*, sender: User, content: str, original_sender: str = None) -> Bloom:
hashtags = [word[1:] for word in content.split(" ") if word.startswith("#")]

now = datetime.datetime.now(tz=datetime.UTC)
bloom_id = int(now.timestamp() * 1000000)
with db_cursor() as cur:
cur.execute(
"INSERT INTO blooms (id, sender_id, content, send_timestamp) VALUES (%(bloom_id)s, %(sender_id)s, %(content)s, %(timestamp)s)",
"INSERT INTO blooms (id, sender_id, content, send_timestamp, original_sender) VALUES (%(bloom_id)s, %(sender_id)s, %(content)s, %(timestamp)s, %(og_sender)s)",
dict(
bloom_id=bloom_id,
sender_id=sender.id,
content=content,
timestamp=datetime.datetime.now(datetime.UTC),
og_sender=original_sender,
),
)
for hashtag in hashtags:
Expand All @@ -54,7 +57,7 @@ def get_blooms_for_user(

cur.execute(
f"""SELECT
blooms.id, users.username, content, send_timestamp
blooms.id, users.username, content, send_timestamp, original_sender, rebloom_count
FROM
blooms INNER JOIN users ON users.id = blooms.sender_id
WHERE
Expand All @@ -68,13 +71,15 @@ def get_blooms_for_user(
rows = cur.fetchall()
blooms = []
for row in rows:
bloom_id, sender_username, content, timestamp = row
bloom_id, sender_username, content, timestamp, og_sender, count = row
blooms.append(
Bloom(
id=bloom_id,
sender=sender_username,
content=content,
sent_timestamp=timestamp,
original_sender=og_sender,
rebloom_count=count,
)
)
return blooms
Expand All @@ -83,18 +88,21 @@ def get_blooms_for_user(
def get_bloom(bloom_id: int) -> Optional[Bloom]:
with db_cursor() as cur:
cur.execute(
"SELECT blooms.id, users.username, content, send_timestamp FROM blooms INNER JOIN users ON users.id = blooms.sender_id WHERE blooms.id = %s",
"SELECT blooms.id, users.username, content, send_timestamp, original_sender, rebloom_count FROM blooms INNER JOIN users ON users.id = blooms.sender_id WHERE blooms.id = %s",
(bloom_id,),
)
row = cur.fetchone()
if row is None:
return None
bloom_id, sender_username, content, timestamp = row
bloom_id, sender_username, content, timestamp, og_sender, count = row
return Bloom(
id=bloom_id,
sender=sender_username,
content=content,
sent_timestamp=timestamp,
original_sender=og_sender,
rebloom_count=count,

)


Expand All @@ -108,7 +116,7 @@ def get_blooms_with_hashtag(
with db_cursor() as cur:
cur.execute(
f"""SELECT
blooms.id, users.username, content, send_timestamp
blooms.id, users.username, content, send_timestamp, original_sender, rebloom_count
FROM
blooms INNER JOIN hashtags ON blooms.id = hashtags.bloom_id INNER JOIN users ON blooms.sender_id = users.id
WHERE
Expand All @@ -121,13 +129,15 @@ def get_blooms_with_hashtag(
rows = cur.fetchall()
blooms = []
for row in rows:
bloom_id, sender_username, content, timestamp = row
bloom_id, sender_username, content, timestamp, og_sender, count = row
blooms.append(
Bloom(
id=bloom_id,
sender=sender_username,
content=content,
sent_timestamp=timestamp,
original_sender=og_sender,
rebloom_count=count,
)
)
return blooms
Expand Down
6 changes: 4 additions & 2 deletions backend/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ def send_bloom():
return type_check_error

user = get_current_user()

blooms.add_bloom(sender=user, content=request.json["content"])
# //rebloom get new filed for repost
original_sender = request.json.get("original_sender")
# //add og sender to add bloom
blooms.add_bloom(sender=user, content=request.json["content"], original_sender=original_sender)

return jsonify(
{
Expand Down
11 changes: 10 additions & 1 deletion db/schema.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
DROP TABLE IF EXISTS hashtags CASCADE;
DROP TABLE IF EXISTS follows CASCADE;
DROP TABLE IF EXISTS blooms CASCADE;
DROP TABLE IF EXISTS users CASCADE;

-- //so I can add sender and count

CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR NOT NULL,
Expand All @@ -10,7 +17,9 @@ CREATE TABLE blooms (
id BIGSERIAL NOT NULL PRIMARY KEY,
sender_id INT NOT NULL REFERENCES users(id),
content TEXT NOT NULL,
send_timestamp TIMESTAMP NOT NULL
send_timestamp TIMESTAMP NOT NULL,
riginal_sender VARCHAR,
rebloom_count INT DEFAULT 0
);

CREATE TABLE follows (
Expand Down
163 changes: 96 additions & 67 deletions front-end/components/bloom.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,80 +8,109 @@
* "sender": username,
* "content": "string from textarea",
* "sent_timestamp": "datetime as ISO 8601 formatted string"}

// to rebloom I add 2 fields
"original_sender": "original_username",
"rebloom_count": 0
*/
const createBloom = (template, bloom) => {
if (!bloom) return;
const bloomFrag = document.getElementById(template).content.cloneNode(true);
const bloomParser = new DOMParser();

const bloomArticle = bloomFrag.querySelector("[data-bloom]");
const bloomUsername = bloomFrag.querySelector("[data-username]");
const bloomTime = bloomFrag.querySelector("[data-time]");
const bloomTimeLink = bloomFrag.querySelector("a:has(> [data-time])");
const bloomContent = bloomFrag.querySelector("[data-content]");

bloomArticle.setAttribute("data-bloom-id", bloom.id);
bloomUsername.setAttribute("href", `/profile/${bloom.sender}`);
bloomUsername.textContent = bloom.sender;
bloomTime.textContent = _formatTimestamp(bloom.sent_timestamp);
bloomTimeLink.setAttribute("href", `/bloom/${bloom.id}`);
bloomContent.replaceChildren(
...bloomParser.parseFromString(_formatHashtags(bloom.content), "text/html")
.body.childNodes
);

return bloomFrag;
if (!bloom) return;
const bloomFrag = document.getElementById(template).content.cloneNode(true);
const bloomParser = new DOMParser();

const bloomArticle = bloomFrag.querySelector("[data-bloom]");
const bloomUsername = bloomFrag.querySelector("[data-username]");
const bloomTime = bloomFrag.querySelector("[data-time]");
const bloomTimeLink = bloomFrag.querySelector("a:has(> [data-time])");
const bloomContent = bloomFrag.querySelector("[data-content]");

bloomArticle.setAttribute("data-bloom-id", bloom.id);
bloomUsername.setAttribute("href", `/profile/${bloom.sender}`);
bloomUsername.textContent = bloom.sender;
bloomTime.textContent = _formatTimestamp(bloom.sent_timestamp);
bloomTimeLink.setAttribute("href", `/bloom/${bloom.id}`);
bloomContent.replaceChildren(
...bloomParser.parseFromString(_formatHashtags(bloom.content), "text/html")
.body.childNodes
);

// rebloom function
//grab new fields
const rebloomLabel = bloomFrag.querySelector("[data-rebloom-label]");
const rebloomCounter = bloomFrag.querySelector("[data-rebloom-count]");

//see if the otiginal senser field is filled with repost, show el
if (bloom.original_sender) {
rebloomLabel.textContent = `(rebloomed ${bloom.original_sender})`;
rebloomLabel.classList.remove("hidden");
} else {
rebloomLabel.classList.add("hidden");
}

//see if count has been increased and show el
if (bloom.rebloom_count >= 1) {
rebloomCounter.textContent = bloom.rebloom_count;
rebloomCounter.classList.remove("hidden");
} else {
rebloomCounter.classList.add("hidden");
}

const rebloomBtn = bloomFrag.querySelector("[data-action='rebloom-btn']");
rebloomBtn.addEventListener("click", async () => {
await apiService.postBloom(bloom.content, bloom.sender);

window.location.reload();
});
return bloomFrag;
};

function _formatHashtags(text) {
if (!text) return text;
return text.replace(
/\B#[^#]+/g,
(match) => `<a href="/hashtag/${match.slice(1)}">${match}</a>`
);
if (!text) return text;
return text.replace(
/\B#[^#]+/g,
(match) => `<a href="/hashtag/${match.slice(1)}">${match}</a>`
);
}

function _formatTimestamp(timestamp) {
if (!timestamp) return "";

try {
const date = new Date(timestamp);
const now = new Date();
const diffSeconds = Math.floor((now - date) / 1000);

// Less than a minute
if (diffSeconds < 60) {
return `${diffSeconds}s`;
}

// Less than an hour
const diffMinutes = Math.floor(diffSeconds / 60);
if (diffMinutes < 60) {
return `${diffMinutes}m`;
}

// Less than a day
const diffHours = Math.floor(diffMinutes / 60);
if (diffHours < 24) {
return `${diffHours}h`;
}

// Less than a week
const diffDays = Math.floor(diffHours / 24);
if (diffDays < 7) {
return `${diffDays}d`;
}

// Format as month and day for older dates
return new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
}).format(date);
} catch (error) {
console.error("Failed to format timestamp:", error);
return "";
}
if (!timestamp) return "";

try {
const date = new Date(timestamp);
const now = new Date();
const diffSeconds = Math.floor((now - date) / 1000);

// Less than a minute
if (diffSeconds < 60) {
return `${diffSeconds}s`;
}

// Less than an hour
const diffMinutes = Math.floor(diffSeconds / 60);
if (diffMinutes < 60) {
return `${diffMinutes}m`;
}

// Less than a day
const diffHours = Math.floor(diffMinutes / 60);
if (diffHours < 24) {
return `${diffHours}h`;
}

// Less than a week
const diffDays = Math.floor(diffHours / 24);
if (diffDays < 7) {
return `${diffDays}d`;
}

// Format as month and day for older dates
return new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
}).format(date);
} catch (error) {
console.error("Failed to format timestamp:", error);
return "";
}
}

export {createBloom};
export { createBloom };
Loading